aca9667ff8
The SFTP poller was off by default in the production container — operators had to POST /api/admin/scheduler/start manually after every restart, which they were not doing. As a result the inbound 999 / 277CA queue fell 7+ days behind in early July 2026 (scheduler.poll_count=0 in the live container since deploy). Mirrors the existing CYCLONE_BACKUP_AUTOSTART=1 entry. Note: there is a separate bug where scheduler._tick_impl's alphabetical list_inbound hangs after ~30s on mft.gainwelltechnologies.com (TCP + auth + first SFTP channel works, but the full alphabetic scan times out). The pull-inbound CLI's date-filtered path works fine — SP to follow.
116 lines
4.2 KiB
YAML
116 lines
4.2 KiB
YAML
name: cyclone
|
|
|
|
# Two-container production stack for Cyclone.
|
|
#
|
|
# backend FastAPI on python:3.11-slim-bookworm, internal port 8000.
|
|
# frontend nginx:1.27-alpine serving the built React SPA + reverse-
|
|
# proxying /api/* to the backend, host port 8080.
|
|
#
|
|
# Tag strategy:
|
|
# - Images are tagged semver from package.json / pyproject.toml.
|
|
# - Two aliases per image: `:latest` (set automatically by `docker
|
|
# compose build`) and `:stable` (manually promoted after a release
|
|
# has been running in prod for >=1 week).
|
|
# - Compose defaults to `:stable` so a fresh `docker compose build`
|
|
# never auto-rolls a new release.
|
|
# - Override with `TAG=0.0.9 docker compose up -d` to roll back.
|
|
|
|
services:
|
|
backend:
|
|
image: cyclone-backend:${TAG:-stable}
|
|
build:
|
|
context: ./backend
|
|
restart: unless-stopped
|
|
environment:
|
|
# SQLite path lives on the named `cyclone_db` volume.
|
|
CYCLONE_DB_URL: "sqlite:////var/lib/cyclone/db/cyclone.db"
|
|
# Wire the existing BackupService (SP17) to autostart on container boot.
|
|
CYCLONE_BACKUP_AUTOSTART: "1"
|
|
CYCLONE_BACKUP_INTERVAL_HOURS: "24"
|
|
CYCLONE_BACKUP_RETENTION_DAYS: "14"
|
|
# SP16 — SFTP polling scheduler. Without this the operator must
|
|
# POST /api/admin/scheduler/start manually after every container
|
|
# restart. Set on 2026-07-02 after the 7-day silent gap (see
|
|
# git log around the SP28 merge).
|
|
CYCLONE_SCHEDULER_AUTOSTART: "1"
|
|
# Logging — JSON to stdout (collected by `docker logs`) and to the
|
|
# bind-mounted file (collected by host-side logrotate).
|
|
CYCLONE_LOG_LEVEL: "INFO"
|
|
CYCLONE_LOG_FILE: "/var/log/cyclone/cyclone.log"
|
|
CYCLONE_LOG_JSON: "1"
|
|
# Cookie signing key defaults to the SQLCipher key. Override in
|
|
# production if you want to rotate them independently.
|
|
CYCLONE_SECRET_KEY_FILE: "/run/secrets/cyclone_db_key"
|
|
CYCLONE_COOKIE_SECURE: "1"
|
|
# First-admin bootstrap — populated by scripts/cyclone-init.sh.
|
|
# The `_FILE` variants are the standard Docker-secret pattern:
|
|
# the env var points at the mounted secret file rather than
|
|
# embedding the secret in the compose file.
|
|
CYCLONE_ADMIN_USERNAME_FILE: "/run/secrets/cyclone_admin_username"
|
|
CYCLONE_ADMIN_PASSWORD_FILE: "/run/secrets/cyclone_admin_password"
|
|
# SP26 — SFTP password via Docker secret (matches the admin-creds pattern).
|
|
CYCLONE_SFTP_PASSWORD_FILE: "/run/secrets/cyclone_sftp_password"
|
|
# Always bind to 0.0.0.0. The compose-managed bridge network
|
|
# isolates the backend from the host's LAN/WAN — only the
|
|
# `frontend` service joins it. Host firewall / port publishing
|
|
# is the layer that controls what reaches us from outside the
|
|
# compose network, not the bind address.
|
|
CYCLONE_HOST: "0.0.0.0"
|
|
secrets:
|
|
- cyclone_db_key
|
|
- cyclone_admin_username
|
|
- cyclone_admin_password
|
|
- cyclone_sftp_password
|
|
volumes:
|
|
- cyclone_db:/var/lib/cyclone/db
|
|
- cyclone_backups:/var/lib/cyclone/backups
|
|
- cyclone_prodfiles:/var/lib/cyclone/prodfiles
|
|
- cyclone_sftp_staging:/var/lib/cyclone/sftp_staging
|
|
- cyclone_logs:/var/log/cyclone
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-fs", "http://localhost:8000/api/health"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 30s
|
|
networks:
|
|
- cyclone_network
|
|
|
|
frontend:
|
|
image: cyclone-frontend:${TAG:-stable}
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.frontend
|
|
restart: unless-stopped
|
|
ports:
|
|
# LAN-bind: only this port is published to the host. The operator
|
|
# is expected to firewall this to the LAN subnet (or rely on VPN
|
|
# for outside access). NO public TLS in v1.
|
|
- "8080:8080"
|
|
depends_on:
|
|
backend:
|
|
condition: service_healthy
|
|
networks:
|
|
- cyclone_network
|
|
|
|
secrets:
|
|
cyclone_db_key:
|
|
file: /etc/cyclone/secrets/db.key
|
|
cyclone_admin_username:
|
|
file: /etc/cyclone/secrets/admin_username
|
|
cyclone_admin_password:
|
|
file: /etc/cyclone/secrets/admin_pw
|
|
cyclone_sftp_password:
|
|
file: /etc/cyclone/secrets/sftp_password
|
|
|
|
volumes:
|
|
cyclone_db:
|
|
cyclone_backups:
|
|
cyclone_prodfiles:
|
|
cyclone_sftp_staging:
|
|
cyclone_logs:
|
|
|
|
networks:
|
|
cyclone_network:
|
|
driver: bridge
|