Customer Onboarding
Operator playbook for taking a new tenant from provisioning to handover. It
assumes the operator workstation is already set up (flo init,
flo vps setup <name>, flo cf setup, flo control login where the control
plane is used) — see Multi-Tenant Deployment (Flo CLI)
for the infrastructure prerequisites.
Domain convention
Every customer maps to one zone plus a customer slug. Hosts are role-first and single-label; two-level subdomains are rejected at create time:
| Role | Shared-zone form | Managed by |
|---|---|---|
api | api-<slug>.<zone> | Production VPS (Docker/Traefik) |
app | app-<slug>.<zone> | Cloudflare Pages |
strapi | strapi-<slug>.<zone> | Strapi VPS (if a CMS is added) |
cdn | cdn-<slug>.<zone> | Cloudflare R2 (custom domain) |
1. Provision
Generic customer provisioning is not exposed through the --vps
control-plane registry. Run flo instance create from an operator workstation
against the target VPS context (use --local only for a local tenant). The
command chains: backend containers and routing → Cloudflare Pages project with
the backend api DNS record and an initial frontend deploy → poll until the
frontend is live.
# Production tenant: backend + admin UI, frontend on a custom app domain
flo instance create \
--domain api-acme.useflo.net \
--frontend-domain app-acme.useflo.net \
--customer-name "Acme Studio" \
--customer-short ACME \
--brand flo \
--tag ghcr.io/team-ledges/flo:master \
--no-seed \
--with-backup -y
# Deploy from a CI branch instead of a pinned image (waits for the GHCR build)
flo deploy api-acme-useflo-net --branch master -y
Key flags (see flo instance create --help):
| Flag | Purpose |
|---|---|
--domain <api-host> | Backend host; becomes the tenant domain and the instance id |
--frontend-domain <app-host> | Provisions Cloudflare Pages with this custom domain |
--no-custom-domain | Frontend reachable only at <project>.pages.dev; CORS locked to that origin |
--tag <image> | Backend image; …:master makes the frontend build from master too |
--branch <branch> | Build the image from a branch (default develop; test targets only) |
--no-seed | Skip demo seed data (production default) |
--with-backup | Schedule the standard GFS backup with an immediate verifying run |
--local-storage | Use the local filesystem instead of R2 |
--no-start | Provision configuration without starting containers |
The wizard polls <project>.pages.dev and reports live / still-building. Verify:
flo instance health api-acme-useflo-net # deep health: container, API, DB, cert, backup, commerce
flo cf dns check api-acme.useflo.net # CF record · delegation · NS · resolver
flo logs app api-acme-useflo-net -f
flo instance health includes a Commerce readiness check: disabled is
healthy while enable_online_payments is off; once payments are enabled, a
missing provider or invoice configuration is reported as not ready.
To test a customer website locally, flo instance create-with-website creates a
local instance and connects a website repo (mints a public API token, registers
the dev origin in CORS, enables the public feature flags, writes
environment.local.ts and serves the boilerplate /tests playground):
flo instance create-with-website --domain api-demo.useflo.net --website ../Ledges.WEBSITE-ng -y
2. Instance configuration
flo config env set api-acme-useflo-net KEY VALUE [--restart]
flo config env set api-acme-useflo-net A=1 B=2 C=3 --restart
flo config env show api-acme-useflo-net [--reveal]
flo config env copy source-instance api-acme-useflo-net
env set writes the tenant .env (0600) and restarts only with --restart.
Secrets are masked by show unless --reveal. See
Environment Variables for the complete key
list.
Domain and TLS:
# Move a backend host (migrates volumes, routing and config) — interactive alternative: flo config domain <id>
flo instance rename api-acme-useflo-net api-acme2.useflo.net -y
# Frontend custom domain on the CF Pages project
flo cf domain add api-acme-useflo-net app-acme.useflo.net
flo cf domain swap api-acme-useflo-net app-acme2.useflo.net
# Cloudflare / DNS / TLS
flo cf setup
flo cf ssl app-acme.useflo.net
flo cf dns set api-acme.useflo.net <ip> --type A
flo config ssl origin useflo.net # Cloudflare Origin CA wildcard cert
flo config ssl install cert.pem key.pem --domain useflo.net
flo config ssl status
3. Feature flags
flo config flags show api-acme-useflo-net
flo config flags set api-acme-useflo-net enable_bookings on
flo config flags set api-acme-useflo-net enable_email_sender on
Use the declarative set form; flo config flags toggle refuses the
safety-critical enable_email_sender and enable_login. Flags are cached for
60 seconds. Dependencies to respect:
enable_online_paymentsrequiresenable_bookingsandenable_einvoicing.enable_subscriptionscannot be turned off once_system_subscriptions_cutoveris on.[enable_native_blog, enable_blogs]are mutually exclusive.
The admin UI (Settings > Feature Flags) batch-saves the same list. Full catalog: Feature Flags.
4. Email
Pick one transactional path per tenant:
# Amazon SES (password via hidden prompt or piped stdin, never on argv)
flo config email ses api-acme-useflo-net \
--region eu-south-1 --username AKIA… --from noreply@useflo.net --probe --restart
# Sweego SMTP (transactional)
flo config email sweego api-acme-useflo-net \
--host smtp.sweego.io --username flo --from noreply@useflo.net --probe
# Cloudflare Email Service (API-based)
flo config env set api-acme-useflo-net \
EMAIL_SENDER_EMAIL_PROVIDER=Cloudflare \
CF_EMAIL_ACCOUNT_ID=… CF_EMAIL_API_TOKEN=… \
EMAIL_SENDER_EMAIL_ALIAS=newsletter@useflo.net --restart
Then enable sending and verify:
flo config flags set api-acme-useflo-net enable_email_sender on
flo instance email api-acme-useflo-net status
flo instance email api-acme-useflo-net sink --recipient you@example.com # redirect all sends while testing
flo instance email api-acme-useflo-net sink --clear
Newsletter bulk uses a separate provider selection; for Sweego:
flo instance sweego api-acme-useflo-net set --api-key … --from …. Providers,
templates and the delivery log are documented in
Email Configuration.
5. Branding
Branding has two layers: the built-in brand themes (Flo / RespiraStudio, runtime brand detection) and the per-tenant whitelabel overlay. See the branding architecture in Multi-Brand Theming.
# Customer display name (registry + tenant .env)
flo instance set-customer api-acme-useflo-net --name "Acme Studio" --short ACME --restart
# Whitelabel assets (alias: flo config wl)
flo config theme upload api-acme-useflo-net ./logo.svg --name LOGO.svg
flo config theme upload api-acme-useflo-net ./background.webp
flo config theme ls api-acme-useflo-net
flo config theme clone source-instance api-acme-useflo-net
# Login branding / registration settings (DB-backed, shares state with the Whitelabel UI)
flo config branding set api-acme-useflo-net \
--enable-login --logo /uploads/whitelabeling/LOGO.svg \
--background /uploads/whitelabeling/background.webp --background-type image
The DB-backed branding UI is gated by enable_whitelabel.
6. Payments and e-invoicing
If the customer sells online, configure the payment provider and the SDI gateway before enabling the flags:
flo instance payments api-acme-useflo-net \
--stripe-secret-key sk_live_… --stripe-webhook-secret whsec_… \
--invoice-seller-vat … --invoice-seller-name … \
--e-invoicing-provider FatturaApi --e-invoicing-environment Test \
--fatturaapi-username … --fatturaapi-password … \
--admin-email admin@acme.example \
--enable-flags --test-connection
Validate on the Test environment first, then switch to Prod. Confirm the
readiness checks are green (flo instance payments <id> or
flo instance health <id>). Full procedure: Payments and E-invoicing Setup.
7. Analytics
Fleet-level prerequisite (once, on the operator workstation): provision the
Google service account and set GOOGLE_ANALYTICS_SA_JSON on the instance.
flo config web-analytics setup-gcp [api-acme-useflo-net]
flo config web-analytics set api-acme-useflo-net \
--ga4-property <numeric-id> --gsc-site sc-domain:acme.example \
--vertical website --enabled on
flo config web-analytics status api-acme-useflo-net
flo config web-analytics backfill api-acme-useflo-net
Enable enable_web_analytics (client-facing dashboard) and optionally
enable_external_web_analytics (home card with external links). Setup guides:
GA4 Setup and
Web Analytics Module.
8. Backup schedule
flo backup schedule api-acme-useflo-net --bucket flo-api-acme-useflo-net-backups --run-now -y
flo backup status api-acme-useflo-net
flo backup schedule api-acme-useflo-net --disable
The schedule is a daily GFS run (default cron 0 3 * * *, tiers
daily/weekly/monthly) uploaded to the tenant R2 bucket; on production tenants
the 7/30/90-day lifecycle is applied automatically. --run-now executes one
immediate verifying backup. On the remote control-plane form --run-now is
mandatory, and backup set-bucket / backup retention run operator-local. See
Backups for restore, retention and encryption details.
9. Handover checklist
-
flo instance health <id>— every check passes (container, API, DB, OIDC cert, backup freshness, commerce as applicable). - Frontend reachable on its custom domain (or pages.dev) and
flo cf dns checkis clean. - TLS certificate valid (
flo config ssl status) and Cloudflare SSL mode correct (flo cf ssl <domain>). - Customer display name and short label set (
flo instance set-customer). - Required feature flags on, dependencies satisfied (
flo config flags show <id>). - Email provider configured,
enable_email_senderon, a test message received, sink cleared. - Whitelabel assets uploaded and login branding set (if requested).
- Payments and e-invoicing configured, readiness green, one end-to-end test transaction verified (sandbox keys / Test environment).
- Web analytics IDs configured and ETL status healthy (if requested).
- GFS backup scheduled and the immediate run verified (
flo backup status <id>). - Demo/test users removed and the customer admin account confirmed.
- Credentials and admin URLs handed over through the agreed secure channel.