feat: minimal prod compose with watchtower auto-updates

This commit is contained in:
Jonas Miederer 2026-09-07 09:05:51 +00:00
parent 338cdcf108
commit 94f353d773
4 changed files with 166 additions and 0 deletions

21
backend/Dockerfile Normal file
View file

@ -0,0 +1,21 @@
# Production image for the FastAPI backend.
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /srv/backend
COPY pyproject.toml alembic.ini ./
COPY app ./app
COPY migrations ./migrations
COPY scripts ./scripts
# No lockfile is committed; dependencies resolve at build time.
RUN uv sync --no-dev
ENV FLOWS_WATCH_ENABLED=true
EXPOSE 8000
# Migrate on start, then serve (watcher reconciles backend/flows automatically).
CMD ["sh", "-c", ".venv/bin/alembic upgrade head && exec .venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000"]

122
compose.prod.yml Normal file
View file

@ -0,0 +1,122 @@
# Minimal self-hosted deployment (internal use: no TLS / reverse proxy).
#
# Flows are the bind-mounted ./backend/flows directory (source of truth);
# the embedded API watcher reconciles them into the DB + LFX automatically.
# Registry images (:latest) auto-update nightly via watchtower; locally
# built images (app, lfx, frontend) update via `docker compose build --pull`.
#
# Usage: docker compose -f compose.prod.yml up -d --build
services:
app:
build:
context: ./backend
dockerfile: Dockerfile
env_file:
- .env
environment:
DATABASE_URL: sqlite:////srv/backend/data/kirche.db
FLOWS_DIR: /srv/backend/flows
LFX_URL: http://lfx:6004
LANGFLOW_URL: http://langflow:7860
CALLBACK_BASE_URL: http://app:8000
volumes:
- ./backend/flows:/srv/backend/flows
- appdata:/srv/backend/data
ports:
- "8000:8000"
depends_on:
lfx:
condition: service_healthy
langflow:
condition: service_healthy
restart: unless-stopped
healthcheck:
test:
[
"CMD-SHELL",
"python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=5)\"",
]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
depends_on:
app:
condition: service_healthy
restart: unless-stopped
langflow:
image: langflowai/langflow:latest
env_file:
- .env
environment:
LANGFLOW_CONFIG_DIR: /app/langflow
LANGFLOW_HOST: 0.0.0.0
PORT: "7860"
LANGFLOW_AUTO_LOGIN: "false"
LANGFLOW_API_KEY_SOURCE: env
LANGFLOW_DATABASE_URL: sqlite:////app/langflow/langflow.db
volumes:
- langflow-data:/app/langflow
ports:
- "7860:7860"
labels:
com.centurylinklabs.watchtower.enable: "true"
restart: unless-stopped
healthcheck:
test:
[
"CMD-SHELL",
"python -c \"import urllib.request; urllib.request.urlopen('http://localhost:7860/health', timeout=5)\"",
]
interval: 30s
timeout: 5s
retries: 20
start_period: 60s
lfx:
build:
context: .devcontainer
dockerfile: Dockerfile.lfx
env_file:
- .env
volumes:
- lfx-data:/flows
ports:
- "6004:6004"
restart: unless-stopped
healthcheck:
test:
[
"CMD-SHELL",
"python -c \"import urllib.request; urllib.request.urlopen('http://localhost:6004/health', timeout=5)\"",
]
interval: 30s
timeout: 5s
retries: 12
start_period: 30s
watchtower:
image: nickfedor/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_LABEL_ENABLE: "true"
WATCHTOWER_SCHEDULE: "0 0 4 * * *"
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_ROLLING_RESTART: "true"
labels:
com.centurylinklabs.watchtower.enable: "true"
restart: unless-stopped
volumes:
appdata:
langflow-data:
lfx-data:

13
frontend/Dockerfile Normal file
View file

@ -0,0 +1,13 @@
# Production image for the React UI (build + Vite preview; internal use).
FROM node:22-slim
WORKDIR /srv/frontend
COPY package.json package-lock.json ./
RUN npm ci
COPY . ./
RUN npm run build
ENV VITE_API_PROXY_TARGET=http://app:8000
EXPOSE 3000
CMD ["npm", "run", "preview", "--", "--host", "0.0.0.0", "--port", "3000"]

View file

@ -12,4 +12,14 @@ export default defineConfig({
},
},
},
preview: {
port: 3000,
proxy: {
"/api": {
// Overridable at container start (prod compose: http://app:8000).
target: process.env.VITE_API_PROXY_TARGET ?? "http://localhost:8000",
changeOrigin: true,
},
},
},
});