Dockerfile, compose.yml, env template, and runbook for the second-brain web container. Targets herbys-dev (10.0.21.207); Traefik (file-provider on 10.0.11.20) reaches the published host port at 10.0.21.207:8080. Image: - python:3.12-slim + the official uv binary copied from the upstream image, plus apt-installed git + ca-certificates so the Gitea VCS pin for embedding-chunking resolves at build time. - uv sync --frozen --no-dev --no-install-project, source copy, then a second uv sync to install the project itself. Two-step so the lock install layer caches independently of source edits. - No ffmpeg / claude CLI / faster-whisper — web role doesn't need any of them. Extraction runs on the dev host's CLI; transcription on the tower. - Drops to uid 1000 (`app`) before CMD. Uvicorn binds 0.0.0.0:8000 inside the container, with --proxy-headers + --forwarded-allow-ips=* so Traefik's X-Forwarded-* survive. compose.yml: - Joins the existing external `homelab` bridge network so the container reaches homelab-postgres:5432 and ollama:11434 by service DNS. - Publishes the uvicorn port at 10.0.21.207:8080 (LAN IP bound, not 0.0.0.0) for Traefik on the separate VM to reach. NO traefik.* labels — file-provider Traefik can't read them. - env_file: .env (0600, gitignored) — SECOND_BRAIN_DATABASE_URL points at homelab-postgres:5432 (containerised), NOT the host's 127.0.0.1:5433 port-map. - restart: unless-stopped. README.md: - Build + bring-up commands. - SECURITY note: no SSO / no CSRF / mutating endpoints — Traefik route must be LAN-only for now (Travis's call). - The two infra steps Travis applies himself, with ready-to-paste snippets: - Knot DNS: brain.herbylab.dev → 10.0.11.20. - Traefik dynamic config: file-provider router + service block. - Verification checklist for both before-and-after-DNS states. Live-verified on herbys-dev: container Up, dashboard returns 200 with real status counts from petalbrain, settings save round-trip works, no tracebacks in logs.
64 lines
2.3 KiB
Docker
64 lines
2.3 KiB
Docker
# 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=*"]
|