feat: minimal prod compose with watchtower auto-updates
This commit is contained in:
parent
338cdcf108
commit
94f353d773
4 changed files with 166 additions and 0 deletions
21
backend/Dockerfile
Normal file
21
backend/Dockerfile
Normal 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
122
compose.prod.yml
Normal 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
13
frontend/Dockerfile
Normal 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"]
|
||||||
|
|
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue