# ===================================================================== # WatchStack — Multi-stage Docker-Build # 1) builder : installiert Dependencies in ein separates venv # 2) runtime : minimal, non-root, nur Runtime-Deps + venv aus builder # ===================================================================== # ---------- 1. Builder ---------- FROM python:3.11-slim AS builder ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 WORKDIR /build RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ && rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN python -m venv /opt/venv \ && /opt/venv/bin/pip install --upgrade pip \ && /opt/venv/bin/pip install -r requirements.txt gunicorn==23.0.0 # ---------- 2. Runtime ---------- FROM python:3.11-slim AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PATH="/opt/venv/bin:$PATH" \ WATCHSTACK_DATA_DIR=/data \ WATCHSTACK_HOST=0.0.0.0 \ WATCHSTACK_PORT=8000 \ WATCHSTACK_WORKERS=2 RUN apt-get update && apt-get install -y --no-install-recommends \ tini \ && rm -rf /var/lib/apt/lists/* \ && groupadd -r watchstack \ && useradd -r -g watchstack -d /app -s /sbin/nologin watchstack \ && mkdir -p /data /app \ && chown -R watchstack:watchstack /data /app COPY --from=builder /opt/venv /opt/venv COPY --chown=watchstack:watchstack app /app/app WORKDIR /app USER watchstack EXPOSE 8000 # Volumes: persistiere die SQLite-DB ausserhalb des Containers VOLUME ["/data"] # tini fuer sauberes Signal-Handling (PID 1) ENTRYPOINT ["/usr/bin/tini", "--"] # gunicorn mit uvicorn-Worker-Klasse fuer ASGI CMD ["sh", "-c", "gunicorn app.main:app \ --bind ${WATCHSTACK_HOST}:${WATCHSTACK_PORT} \ --workers ${WATCHSTACK_WORKERS} \ --worker-class uvicorn.workers.UvicornWorker \ --access-logfile - \ --error-logfile -"] # Healthcheck (nutzt Python, weil wget in :slim fehlt) HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD python -c "import urllib.request, sys; r=urllib.request.urlopen('http://127.0.0.1:'+__import__('os').environ.get('WATCHSTACK_PORT','8000')+'/api/health',timeout=3); sys.exit(0 if r.status==200 else 1)" \ || exit 1 LABEL org.opencontainers.image.title="WatchStack" \ org.opencontainers.image.description="Watchlist für Bücher, Serien & mehr" \ org.opencontainers.image.source="https://git.pkop.de/Vibecode/watchlist" \ org.opencontainers.image.licenses="Apache-2.0"