Skip to main content

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:

RoleShared-zone formManaged by
apiapi-<slug>.<zone>Production VPS (Docker/Traefik)
appapp-<slug>.<zone>Cloudflare Pages
strapistrapi-<slug>.<zone>Strapi VPS (if a CMS is added)
cdncdn-<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):

FlagPurpose
--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-domainFrontend 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-seedSkip demo seed data (production default)
--with-backupSchedule the standard GFS backup with an immediate verifying run
--local-storageUse the local filesystem instead of R2
--no-startProvision 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_payments requires enable_bookings and enable_einvoicing.
  • enable_subscriptions cannot be turned off once _system_subscriptions_cutover is 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 check is 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_sender on, 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.