Deploy with Docker
Flaghoist ships a portable container: one image, configured entirely by environment variables,
that runs on a plain VPS, Fly.io, Railway, DigitalOcean, Google Cloud Run, AWS ECS, or Kubernetes.
This is the least locked-in way to run it, and every other container host below just consumes this
image. The runnable source is in
examples/docker.
The entry picks its storage adapter at startup from FLAGS_STORAGE, so the same image works against
Postgres, Redis, SQLite, or in-memory. (Workers KV is Cloudflare-only and is not a container option.)
Scaffold it
Section titled “Scaffold it”The CLI generates the same files (server.mjs, Dockerfile, .dockerignore, package.json) into a
directory of your own, so you do not have to copy examples/docker:
npm create flaghoist@latest team-flags -- --platform containercd team-flagsnpx flaghoist ejectnpx flaghoist deploy and choosing Another platform does the same scaffolding from an existing
flaghoist.toml. Either way you get the container project below to build and run.
Configuration
Section titled “Configuration”| Variable | Required | Default | Notes |
|---|---|---|---|
ADMIN_TOKEN |
yes | Bearer token for the admin API and dashboard. | |
READ_API_KEY |
yes | x-api-key for the OFREP read path. |
|
FLAGS_STORAGE |
no | memory |
postgres, redis, sqlite, or memory. |
DATABASE_URL |
when postgres |
Any Postgres connection string. | |
DATABASE_PATH |
when sqlite |
/data/flags.db |
Path to the SQLite file. Mount a volume here. |
REDIS_URL |
when redis |
Any Redis connection string. | |
FLAGS_TABLE |
no | flaghoist_flags |
Postgres or SQLite table, to scope per env. |
FLAGS_HASH_KEY |
no | flaghoist:flags |
Redis hash key, to scope per environment. |
FLAGS_CORS |
no | Comma-separated browser origins allowed to read. | |
PORT |
no | 8080 |
Most hosts set this for you. |
memory is for a quick look only: flags do not survive a restart. Use postgres, redis, or
sqlite for anything real. For sqlite, mount a volume at the directory containing your
DATABASE_PATH so flags survive container restarts.
Build and run
Section titled “Build and run”From the scaffolded project (or a copy of examples/docker):
docker build -t flaghoist .Against Postgres:
docker run -p 8080:8080 \ -e FLAGS_STORAGE=postgres \ -e DATABASE_URL="postgres://user:pass@host:5432/db" \ -e ADMIN_TOKEN="$(openssl rand -hex 32)" \ -e READ_API_KEY="$(openssl rand -hex 32)" \ flaghoistPush it once, run it anywhere
Section titled “Push it once, run it anywhere”docker tag flaghoist ghcr.io/you/flaghoist:latestdocker push ghcr.io/you/flaghoist:latestAny container host then pulls and runs the same image with the same environment: Fly.io (fly launch
against the image), Railway (deploy from the Dockerfile), DigitalOcean App Platform, Cloud Run, ECS,
or docker run on a VPS. Nothing about the image is host-specific.
Verify
Section titled “Verify”curl http://localhost:8080/healthCreate a flag from the dashboard at http://localhost:8080/admin (paste your ADMIN_TOKEN) or with
the CLI, then read it back exactly as your app would:
curl -X POST http://localhost:8080/ofrep/v1/evaluate/flags/new-checkout \ -H "x-api-key: $READ_API_KEY" -H "content-type: application/json" \ -d '{"context":{"targetingKey":"tester-1"}}'- Set
FLAGS_TABLE(Postgres or SQLite) orFLAGS_HASH_KEY(Redis) to keep environments apart. See Storage adapters. - Managed Postgres (Supabase, Neon, Render) requires TLS; the entry enables it unless your
DATABASE_URLalready setssslmode. - Set
FLAGS_CORSif a browser app reads flags cross-origin. Server-side reads need nothing.