FlightDeck stays local-first: this directory is optional packaging for demos, staging, or a trusted private network. Read SECURITY.md before exposing HTTP beyond loopback.
Build (from this directory):
docker build -t flightdeck-serve:local .The image installs flightdeck-ai from PyPI and runs flightdeck serve on 0.0.0.0 using port 8765 by default. On platforms that set PORT (for example Railway), entrypoint.sh binds to $PORT instead.
entrypoint.sh creates a default flightdeck.yaml in /workspace on first start (flightdeck init) if the mounted volume is empty.
cd examples/deploy
docker compose up --build- UI + API:
http://127.0.0.1:8765/(static UI +/v1/*). - Health:
GET http://127.0.0.1:8765/health. - Compose healthcheck:
docker-compose.ymlprobes/healthso orchestrators can mark the service ready (seehealthcheck:in that file). - Data: named Docker volume
fd_workspace(SQLite under.flightdeck/inside the volume). Remove withdocker compose down -vwhen you want a clean ledger.
For team or production deployments, add a postgres service alongside flightdeck serve
and wire the connection URL via FLIGHTDECK_DATABASE_URL. Create a
docker-compose.postgres.yml override (or extend the existing docker-compose.yml):
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: flightdeck
POSTGRES_USER: flightdeck
POSTGRES_PASSWORD: changeme
volumes:
- pg_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U flightdeck"]
interval: 5s
retries: 5
flightdeck:
build: .
environment:
FLIGHTDECK_DATABASE_URL: postgresql://flightdeck:changeme@db:5432/flightdeck
FLIGHTDECK_LOCAL_API_TOKEN: "${FLIGHTDECK_LOCAL_API_TOKEN}"
depends_on:
db:
condition: service_healthy
ports:
- "8765:8765"
volumes:
pg_data:Install the PostgreSQL extra in the image by adding pip install "flightdeck-ai[postgres]"
to the Dockerfile, or set it as a build arg. Schema migrations run automatically on
startup. Back up with pg_dump flightdeck on your preferred schedule.
FlightDeck stores the ledger in .flightdeck/flightdeck.db under the workspace root. For a hot copy while the server is stopped or idle, run from the workspace directory:
flightdeck doctor --backup ./backups/flightdeck-$(date -u +%Y%m%dT%H%M%SZ).dbInside the Compose stack, exec into the running container with /workspace as cwd (same layout as local flightdeck init), or run a one-shot sidecar that mounts the same volume and invokes flightdeck doctor --backup /workspace/backups/snapshot.db. Schedule with cron or your platform scheduler; keep backups off the primary volume when possible.
Set FLIGHTDECK_LOCAL_API_TOKEN in your environment before docker compose up (or in an .env file beside docker-compose.yml). Clients must send Authorization: Bearer … for ledger writes: POST /v1/promote*, POST /v1/rollback, and POST /v1/events. With no token configured, those routes accept only loopback callers. POST /v1/diff stays unauthenticated (read-only); still treat network placement as a trust boundary.
For a public HTTPS demo or staging, see Railway or Fly.io below.
Railway often suits small demos; pricing and free allowances change — confirm Railway pricing before relying on $0/month long term.
- Create a new project → Deploy from GitHub (or
railway init/railway linkwith the CLI). - Set the service root directory to
examples/deployso Railway buildsDockerfileand picks uprailway.toml(config-as-code).
If the dashboard root cannot be a subdirectory, setRAILWAY_DOCKERFILE_PATH(service variable) toexamples/deploy/Dockerfileand point config as code atexamples/deploy/railway.tomlper config-as-code. - Networking: enable Public Networking and Generate Domain (HTTPS). Railway routes traffic to the
PORTyour process listens on;entrypoint.shusesPORTautomatically. - Variables (recommended for any public URL): add
FLIGHTDECK_LOCAL_API_TOKEN(random secret). The stock PyPI image does not embed that token in the browser bundle — use read-only UI (VITE_FLIGHTDECK_UI_READ_ONLY=truein a custom image build) or rebuild static assets withVITE_FLIGHTDECK_LOCAL_API_TOKENso the UI can authenticate whenread_authis bearer-gated — seedocs/web-ui.mdand SECURITY.md. - Persistent SQLite (optional): add a Railway volume mounted at
/workspaceso redeploys keep.flightdeck/. Without a volume, the ledger may reset when the container is recreated.
CLI sketch (from examples/deploy after railway link):
railway login
cd examples/deploy
railway variable set FLIGHTDECK_LOCAL_API_TOKEN="$(openssl rand -hex 24)"
railway up
railway domain # generate .railway.app URL if neededDeploy the same Docker image to Fly Machines. This gives you a URL you can open from any browser; treat it as trusted or lock it down with FLIGHTDECK_LOCAL_API_TOKEN (see SECURITY.md).
- Install
flyctland runfly auth login. - From
examples/deploy/:- Edit
fly.toml: setappto a unique name (or runfly apps create <name>and match). - Optional persistent ledger: create a volume in the same region as
primary_region:Uncomment thefly volumes create fd_workspace --region iad --size 1
[mounts]block at the bottom offly.toml(source = "fd_workspace",destination = "/workspace").
- Edit
- Secrets (recommended once you expose the app on the internet):
The server then expects
fly secrets set FLIGHTDECK_LOCAL_API_TOKEN="$(openssl rand -hex 24)"
Authorization: Bearer …for ledger writes from non-loopback clients. The stockexamples/deployimage does not embed a browser token; use either read-only UI (VITE_FLIGHTDECK_UI_READ_ONLY=truein a custom image build — seedocs/web-ui.md) or rebuild the image withVITE_FLIGHTDECK_LOCAL_API_TOKENmatching your secret so the bundled UI can call promote/diff whenread_authis bearer-gated.
cd examples/deploy
fly deploy --remote-onlyOpen https://<app>.fly.dev/ — static UI and /v1/* on the same origin.
- Cold starts:
fly.tomlallowsmin_machines_running = 0; first request may wake the Machine. - Demo-only UI: ship a build with
VITE_FLIGHTDECK_UI_READ_ONLY=trueif you only want read-only navigation (rebuildweb/and static bundle perdocs/web-ui.md). - Maintainers: this repo cannot run
fly deployfor you; use your own Fly org and the steps above.
A minimal chart lives under chart/flightdeck/. It runs one replica of flightdeck serve with an emptyDir workspace (ephemeral); for a persistent ledger, replace the volume in templates/deployment.yaml with a PVC or mount your own image init.
docker build -t flightdeck-serve:local .
helm install fd ./chart/flightdeck --namespace flightdeck --create-namespaceTune values.yaml (image, resources, service.type) for your cluster.
To reuse an existing directory that already contains flightdeck.yaml and .flightdeck/, replace the volumes entry with:
volumes:
- /path/on/host/my-workspace:/workspaceUse an absolute path on Linux/macOS; on Windows Docker Desktop, use a path Docker can mount.
Compose sets a healthcheck on /health plus restart: unless-stopped on the service; for systemd/Kubernetes, reuse the same image and run /entrypoint.sh (or invoke flightdeck serve directly with a prepared workspace directory).
- Logs:
docker compose logs -f flightdeck(or your platform log stream) when debugging ingest or policy failures. - State: one
flightdeck serveinstance per workspace SQLite file; do not run two writers against the same volume. - Upgrades: rebuild the image on semver bumps; keep
/workspacemounted so the ledger survives container recreation.
- examples/integration/README.md — emit
RunEventtraffic into a running server. - examples/ci/README.md — CI policy gates without
serve; approval-gated promote script promote_with_approval.sh and workflow samples. - SECURITY.md — trust boundaries before exposing
/v1/*beyond loopback.