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
.envfile - 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:
- Pull new image
- Start standby containers (green)
- Run health checks
- Switch Traefik routing from blue to green
- Stop old containers (blue)
- 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.
| Command | Description |
|---|---|
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 vps | VPS 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 cf | Cloudflare: setup, Pages create/sync/status/rm, ssl, dns, domain, cache purge, token-scope, whoami, reset, audit-www |
flo strapi | Strapi 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 control | Control plane: login, logout, whoami, dashboard start, snapshot, build, server, deploy, doctor, audit, alerts, backup-reader, agent-config, command, bootstrap |
flo failover | DR: setup, replication, status, monitor, sync-image/sync-secrets/sync-media, run, back, watch, drill, auto, incident, agent, teardown, storm, soak |
flo website | link, serve, stop a website repo against a local instance |
flo home | export/import a passphrase-encrypted ~/.flo bundle |
flo release (r) | check, create <version>, deploy [version] |
flo watchdog | install, enable <instance> --branch <branch>, disable, status, logs |
flo secrets (s) | status <id>, rotate <id> [keys...], rotate <id> --all |
flo add / flo rm | Attach/detach blog or real-estate CMS modules |
flo doctor | Local prereq checks (Docker, SSH, Node, disk, toolchains); doctor cache-headers <base-url> |
flo build | Build a Docker image from a git branch (--branch, --no-frontend, --platform) |
flo image | push <tag> to a VPS via SFTP |
flo watch | Poll health of all running instances (--interval <seconds>) |
flo ref | Command reference (--commands-only) |
flo version | sync [version] repository manifests |
flo completion | Shell completions: bash, zsh, fish |
flo init / flo setup / flo dev / flo sdd | First-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 list | flo 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 init | flo 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.