# The single compose file for dev and prod. # # Dev: VS Code attaches to `api` (see .devcontainer/devcontainer.json) and # starts `runServices` (everything but watchtower). Dev specifics # (autoreload, editor, SSH keys, docker CLI) come from devcontainer.json. # Prod: `docker compose up -d --build` (own .env with secrets + # FRONTEND_MODE=preview). Code updates via `git pull` + restart; image # rebuilds only for dependency changes. services: api: build: context: ./backend dockerfile: Dockerfile env_file: # Optional: fresh clones and CI checkouts have no `.env` (gitignored); # containers must still create so `config`/`build` work everywhere. # Runtime secrets come from `.env` when present (see setup.sh). - path: .env required: false volumes: # No venv volume by design: the venv lives at /opt/venv (image-local, # see backend/Dockerfile). A named volume here used to shadow the image # content with an empty dir on first start (split-brain with the host # checkout, plus root-owned interpreters). - .:/srv/kirche-einfach ports: - "8000:8000" # FastAPI 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 env_file: # Optional: fresh clones and CI checkouts have no `.env` (gitignored); # containers must still create so `config`/`build` work everywhere. # Runtime secrets come from `.env` when present (see setup.sh). - path: .env required: false environment: # Dev default (HMR); prod .env sets `preview` (build + serve bundle). FRONTEND_MODE: ${FRONTEND_MODE:-dev} volumes: # No node_modules volume by design: deps live in the bind-mount and the # entrypoint runs `npm ci` on first start (a volume used to shadow the # image install with an empty dir and diverge from the host checkout). - .:/srv/kirche-einfach ports: - "3000:3000" # Vite depends_on: api: condition: service_healthy restart: unless-stopped langflow: # Pinned (never :latest): Langflow and LFX are versioned together, their # major.minor must match. Bump via LANGFLOW_VERSION in .env (see # .env.example) together with Dockerfile.lfx + backend `lfx~=` pin. image: langflowai/langflow:${LANGFLOW_VERSION:-1.12.0} env_file: # Optional: fresh clones and CI checkouts have no `.env` (gitignored); # containers must still create so `config`/`build` work everywhere. # Runtime secrets come from `.env` when present (see setup.sh). - path: .env required: false environment: # Static values only; secrets/URLs come from .env via env_file. LANGFLOW_CONFIG_DIR: /app/langflow LANGFLOW_HOST: 0.0.0.0 PORT: "7860" LANGFLOW_AUTO_LOGIN: "false" LANGFLOW_API_KEY_SOURCE: env # SQLite file inside the langflow-data volume (absolute path required # by Langflow); overrides any postgres URL that may linger in .env. LANGFLOW_DATABASE_URL: sqlite:////app/langflow/langflow.db # Custom form-input components (backend/components, category folders). LANGFLOW_COMPONENTS_PATH: /app/components # Seed the authoring UI with the canonical flows (read-only: the files # stay the source of truth, the API watcher is the only sync path). LANGFLOW_LOAD_FLOWS_PATH: /app/flows volumes: - langflow-data:/app/langflow - ./backend/components:/app/components:ro - ./backend/flows:/app/flows:ro ports: - "7860:7860" 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: . dockerfile: Dockerfile.lfx env_file: # Optional: fresh clones and CI checkouts have no `.env` (gitignored); # containers must still create so `config`/`build` work everywhere. # Runtime secrets come from `.env` when present (see setup.sh). - path: .env required: false 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 # Nightly auto-updates of :latest images (prod). Never started in dev # (not in devcontainer.json runServices; also needs a docker socket). # langflow/lfx carry no watchtower label on purpose: both are version-pinned # (major.minor must match) and update only via deliberate version bumps. 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: langflow-data: lfx-data: