# second-brain web container — review/dashboard UI only. # # Slim by design: this image runs `uvicorn` against second_brain.web.app. # It does NOT need ffmpeg, the claude CLI, faster-whisper, or any GPU # tooling. Extraction runs on the dev host's CLI; transcription runs on # the tower. The web role is just "FastAPI + psycopg + Jinja templates". FROM python:3.12-slim AS base ENV PYTHONUNBUFFERED=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ UV_LINK_MODE=copy \ UV_PROJECT_ENVIRONMENT=/app/.venv # uv is fetched as a static binary from its official image so we don't # pull a full python stack to install it. COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/ # `git` is needed at *build* time because pyproject.toml pins # embedding-chunking to a Gitea VCS source. `ca-certificates` so the # https handshake validates. Slim image to clean up apt afterwards. RUN apt-get update \ && apt-get install -y --no-install-recommends git ca-certificates \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Project metadata first so the layer cache survives source edits. COPY pyproject.toml uv.lock ./ # `--frozen` enforces lockfile parity. `--no-dev` excludes pytest/ruff. # `--no-install-project` so we don't try to install the (not-yet-copied) # source tree in this layer. RUN uv sync --frozen --no-dev --no-install-project # Source + runtime config. config/settings.toml.example is committed; a # real settings.toml gets bind-mounted in by compose at runtime. COPY src/ ./src/ COPY config/ ./config/ COPY alembic/ ./alembic/ COPY alembic.ini ./alembic.ini # Now install the project itself into the venv (puts the `second-brain` # console script + the second_brain package on PATH). RUN uv sync --frozen --no-dev # Drop privileges. The image doesn't write to the host bind-mounts; the # settings.toml mount is read-only. RUN useradd --uid 1000 --create-home --shell /sbin/nologin app \ && chown -R app:app /app USER app EXPOSE 8000 # Bind to all interfaces inside the container — the host port publish # decides who can reach it from outside. `--proxy-headers` lets Traefik # (off-box on 10.0.11.20) forward X-Forwarded-* correctly. CMD ["/app/.venv/bin/uvicorn", \ "second_brain.web.app:app", \ "--host", "0.0.0.0", \ "--port", "8000", \ "--proxy-headers", \ "--forwarded-allow-ips=*"]