Skip to main content

Multi-Tenant Deployment (Flo CLI)

The Flo CLI manages multiple isolated tenants with blue-green deployments, automatic rollback, and encrypted secrets.

Overview​

Each tenant gets:

  • Isolated PostgreSQL database
  • Separate Docker network
  • Dedicated volumes
  • Per-tenant .env file
  • OIDC certificates
  • AES-256-GCM encrypted secrets at rest

Traffic routing is handled by Traefik as a reverse proxy.

Commands are namespaced (flo instance …, flo deploy …, flo config …). The old flat syntax (flo create, flo list, flo rollback, flo traefik …) is no longer registered — see Deprecated syntax.

Installation​

cd cli
npm install
npm link

# Verify
flo --help

From the repository root, node scripts/setup-dev.mjs (or ./setup.sh / .\setup.ps1) wires the launcher to the current checkout; re-run it after cli/package-lock.json changes. Remote fleet operations authenticate through the control plane (flo control login) instead of holding SSH keys locally.

Infrastructure Setup​

# First-run wizard (creates ~/.flo and the operator config)
flo init

# Provision a VPS: Docker, Traefik, firewall, journal policy
flo vps setup production

# Link two VPS into the WireGuard DR mesh (optional, for failover)
flo vps link production production-uk

# Cloudflare credentials (Pages, DNS, Origin CA)
flo cf setup

# Traefik TLS certificates
flo config ssl install <cert-path> <key-path>
flo config ssl status

Tenant Lifecycle​

Create a Tenant​

flo instance create --domain api.customer.com --customer-name "Acme" --brand flo

Without flags the command runs an interactive wizard; with --domain it is non-interactive. It provisions the full tenant stack: database, Docker containers, network, volumes, Traefik routing, and .env file.

Useful flags: --branch, --tag, --customer-short, --no-start, --no-seed, --with-backup (schedules the standard GFS backup), --local-storage, -y.

Deploy​

# Blue-green deploy from an image tag or a CI branch
flo deploy customer-tenant --tag ghcr.io/team-ledges/flo:latest -y
flo deploy customer-tenant --branch master -y

Real deploy options: --tag, --branch (waits for the GitHub Actions image), --backup (create a DB backup before deploying), --force (development branch on a live tenant, explicit authorization), --no-wait-ci, --ci-timeout <minutes>, -y.

Blue-green deployment process:

  1. Pull new image
  2. Start standby containers (green)
  3. Run health checks
  4. Switch Traefik routing from blue to green
  5. Stop old containers (blue)
  6. If health check fails → automatic rollback

Deployment history and upgrades:

flo deploy history customer-tenant
flo deploy upgrade customer-tenant

Rollback​

# Swap routing back to the previous container
flo deploy rollback customer-tenant -y

Monitor​

flo instance ls # List all tenants with health status
flo instance health customer-tenant # Deep health check (DB, API, disk, containers)
flo instance stats customer-tenant # CPU/memory per container (--watch, --interval)
flo logs app customer-tenant -f # App logs (also: db, proxy, errors, days, day, raw)
flo instance monitor start customer-tenant --interval 5m # Long-term cron sampling
flo watchdog status # Auto-deploy watchdog state
flo doctor # Local/system health checks
flo vps stats production # Host-level CPU, memory, disk, containers

Backup & Restore​

flo backup create customer-tenant --full --upload
flo backup restore customer-tenant --file /path/to/flo-<ts>.sql.gz --no-owner
flo backup schedule customer-tenant --bucket flo-customer-backups --cron "0 3 * * *" -y
flo backup status customer-tenant

The GFS system keeps daily/weekly/monthly dumps on R2 with bucket-lifecycle retention — see Backups.

Delete​

flo instance rm customer-tenant -y

Deletes the instance permanently, including its volumes. To move a tenant to a new domain instead, use flo instance rename <id> <new-domain> -y.

Full Command Reference​

One line per namespace (registered by cli/src/index.ts). Run flo <namespace> --help or flo ref --commands-only for the complete subcommand trees.

CommandDescription
flo instance (i)Tenant lifecycle and operations: create, clone, duplicate, ls, start, stop, restart, rm, health, info, stats, monitor, env, rename, seed, db, exec, maintenance, regen-config, sync, resources, clean-users, set-password, pg-upgrade, media tools
flo deploy (d)Blue-green deploy, rollback, upgrade, history
flo config (cfg)env, domain, ssl, flags, branding, theme (whitelabeling), R2 setup/policies/CORS, web-analytics, set/get, export, notify, email
flo backup (b)create, restore, ls, download, upload, status, retention, schedule, set-bucket
flo vpsVPS profiles and hosts: add, edit, ls, rm, use, test, setup, provision, ghcr-login, stats, journal, link, exec, jump, set-key, cleanup, push-config, enroll-key, rotate-password
flo cfCloudflare: setup, Pages create/sync/status/rm, ssl, dns, domain, cache purge, token-scope, whoami, reset, audit-www
flo strapiStrapi CMS: instance, backup, db, config, media, security, maintenance, logs, token, plugin, export-blog, vps-setup
flo logs (l)app, db, proxy, errors, days, day, raw
flo test (t)run, seed, api test pipelines
flo controlControl plane: login, logout, whoami, dashboard start, snapshot, build, server, deploy, doctor, audit, alerts, backup-reader, agent-config, command, bootstrap
flo failoverDR: setup, replication, status, monitor, sync-image/sync-secrets/sync-media, run, back, watch, drill, auto, incident, agent, teardown, storm, soak
flo websitelink, serve, stop a website repo against a local instance
flo homeexport/import a passphrase-encrypted ~/.flo bundle
flo release (r)check, create <version>, deploy [version]
flo watchdoginstall, enable <instance> --branch <branch>, disable, status, logs
flo secrets (s)status <id>, rotate <id> [keys...], rotate <id> --all
flo add / flo rmAttach/detach blog or real-estate CMS modules
flo doctorLocal prereq checks (Docker, SSH, Node, disk, toolchains); doctor cache-headers <base-url>
flo buildBuild a Docker image from a git branch (--branch, --no-frontend, --platform)
flo imagepush <tag> to a VPS via SFTP
flo watchPoll health of all running instances (--interval <seconds>)
flo refCommand reference (--commands-only)
flo versionsync [version] repository manifests
flo completionShell completions: bash, zsh, fish
flo init / flo setup / flo dev / flo sddFirst-run wizard, guided onboarding/project setup, local hot-reload dev loop, spec-driven-development tooling

Deprecated syntax​

Old (removed)Current
flo create <domain>flo instance create --domain <domain>
flo listflo instance ls
flo status <id>flo instance health <id> or flo instance info <id>
flo rollback <id>flo deploy rollback <id>
flo backup <id>flo backup create <id>
flo restore <id>flo backup restore <id>
flo delete <id>flo instance rm <id>
flo traefik initflo vps setup <name>
flo ssl install <cert> <key>flo config ssl install <cert> <key>
flo cloudflare …flo cf … (the namespace is cf)

Secrets Management​

Tenant secrets (database passwords, API keys, OIDC certs) are encrypted at rest using AES-256-GCM. The CLI handles encryption/decryption transparently.

flo secrets status customer-tenant
flo secrets rotate customer-tenant --all --save-to 1password

See Secrets Rotation for what rotates and how.

Architecture​

Traefik (Reverse Proxy)
├── HTTPS → Tenant 1 (api.customer1.com)
│ ├── Flo App Container (blue)
│ ├── Flo App Container (green) ← standby
│ └── PostgreSQL Database
├── HTTPS → Tenant 2 (api.customer2.com)
│ ├── Flo App Container (blue)
│ └── PostgreSQL Database
└── HTTPS → Tenant N
└── ...

Each tenant is fully isolated: separate Docker network, database, and volumes. Traefik handles SSL termination and routing based on hostname.

Data Directory Structure​

The CLI data dir defaults to ~/.flo (FLO_BASE_PATH overrides the instance root):

~/.flo/
├── config.json # Operator config (encrypted fields)
├── master.key # AES-256-GCM master key (0600)
├── instances/
│ ├── customer1/
│ │ ├── .env # Tenant config (secrets encrypted at rest)
│ │ ├── docker-compose.yml
│ │ ├── certs/ # OIDC certificates
│ │ ├── .flo-private/ # CLI-only credentials (never mounted)
│ │ └── backups/ # pg_dump files + .sha256 sidecars
│ └── customer2/
├── scripts/ # GFS backup scripts (on the target host)
└── logs/ # Backup logs (on the target host)

Remote backup objects live in the tenant's R2 bucket under daily/, weekly/, and monthly/ prefixes, each artifact accompanied by a .sha256 sidecar.