Skip to main content
floo’s primary config file is floo.app.toml. Services are declared inline under [services.<name>]. Managed-service credential attachments live under service env blocks. Managed Postgres, Redis, and Storage are provisioned either with floo services add or by declaring [managed.<name>] blocks — a deploy provisions anything declared-but-missing and never destroys. The legacy top-level [postgres], [redis], and [storage] sections are deprecated in favor of [managed.<name>]. floo.service.toml is an optional per-service config file used only when you want service configuration to live alongside each service’s code (the “delegated” layout). floo init creates floo.app.toml with your service declared inline. This page is the full reference.

Config shapes

ShapeFilesWhen to use
Single servicefloo.app.tomlOne service, with or without managed credential attachments
Inline multi-servicefloo.app.tomlAll services defined in one file
Delegated multi-servicefloo.app.toml + per-service floo.service.tomlService config lives alongside service code

Single service

[app]
name = "my-app"
access_mode = "public"

[services.web]
type = "web"
port = 3000
ingress = "public"
env_file = ".env"

[resources]
cpu = "1"
memory = "512Mi"
max_instances = 10

Single service with managed services

Provision the durable resources with the CLI:
floo services add postgres --app crm
floo services add redis --app crm
floo services add storage --app crm
Then keep the app shape in config:
[app]
name = "crm"

[services.web]
type = "web"
path = "."
port = 3000
ingress = "public"

[services.web.env]
managed = ["postgres", "redis", "storage"]

Inline multi-service

[app]
name = "full-stack"
access_mode = "password"

[resources]
cpu = "1"
memory = "512Mi"
max_instances = 10

[services.web]
type = "web"
path = "./web"
port = 3000
ingress = "public"

[services.web.env]
managed = []

[services.api]
type = "api"
path = "./api"
port = 8080
env_file = "./api/.env"

[services.api.env]
managed = ["postgres", "redis"]

[services.worker]
type = "worker"
path = "./worker"
port = 8081
ingress = "internal"
cpu = "2"
memory = "2Gi"

[services.worker.env]
managed = ["postgres", "redis"]
Inline and delegated are mutually exclusive per service. If a service has type and port in floo.app.toml, do not also place a floo.service.toml in that service’s directory. The CLI rejects this during preflight.

Delegated multi-service

Root floo.app.toml:
[app]
name = "full-stack"

[services.web]
path = "./web"

[services.api]
path = "./api"
web/floo.service.toml:
[app]
name = "full-stack"

[service]
name = "web"
type = "web"
port = 3000
ingress = "public"
api/floo.service.toml:
[app]
name = "full-stack"

[service]
name = "api"
type = "api"
port = 8080
ingress = "public"
env_file = ".env"

Field reference

[app]

FieldTypeDefaultDescription
namestringrequiredApp name (DNS-safe)
access_modestring"public"public, password, accounts

[services.<name>]

FieldTypeDefaultDescription
typestringrequiredweb, api, worker
portintegerrequiredPort the service listens on
ingressstring"public"public (internet-facing) or internal (only reachable by other services in the same app)
env_filestringnoneRelative path to env file synced on deploy
pathstring.Relative path to service directory (multi-service apps)
dev_commandstringnoneShell command run by floo dev to start the service locally. Example: "npm run dev"
migrate_commandstringnoneShell command run before floo dev starts the service (and after each deploy). Example: "alembic upgrade head"
domainstringnoneCustom domain for this service. Example: "api.example.com"

[services.<name>.env]

Per-service env contract for inline services. In delegated layouts and single-service apps, use the same fields in a top-level [env] block inside that service’s floo.service.toml.
FieldTypeDefaultDescription
requiredstring array[]Env var names that must be set before deploy
optionalstring array[]Env var names documented for the service but not required
managedstring arrayimplicit legacy modeManaged service credentials to inject into this service: postgres, redis, storage, or named handles such as postgres:analytics
[services.web.env]
managed = []

[services.api.env]
required = ["STRIPE_SECRET_KEY"]
managed = ["postgres", "redis"]
If no service declares managed, managed service credentials are injected into every service for backward compatibility. Once any service declares managed, services without it receive none.

[service] (floo.service.toml)

Used only in the delegated multi-service layout, where each service keeps its own floo.service.toml alongside its code.
FieldTypeDefaultDescription
namestringrequiredService name (DNS-safe, 2-21 chars)
typestringrequiredweb, api, worker
portintegerrequiredPort the service listens on
ingressstring"public"public or internal
env_filestringnoneRelative path to env file synced on deploy

[resources]

FieldTypeDefaultDescription
cpustring"1"vCPU allocation
memorystring"512Mi"Memory allocation
min_instancesinteger0Min instance count (0 allows scale to zero)
max_instancesinteger10Max instance count
Per-service resource fields override the global [resources] section.

[managed.<name>]

Declare a managed service in config. <name> is a logical instance name — declare more than one block to run multiple services of the same type (e.g. [managed.primary] and [managed.analytics]). A deploy provisions any declared service that doesn’t exist yet; it never destroys one. Removing a block leaves the service running and surfaces an orphan warning in floo preflight — destroy it explicitly with floo services remove. The imperative equivalent is floo services add <type> --name <name>.
FieldTypeDefaultDescription
typestringrequiredpostgres, redis, or storage
tierstringnoneAccepted for backwards-compatibility but ignored — every managed service ships with the same defaults
enabledbooleantrueSet false to keep a declaration without provisioning it
[managed.primary]
type = "postgres"

[managed.cache]
type = "redis"
Credentials are injected per-service via the [services.<name>.env] managed field above.

[postgres], [redis], [storage] (deprecated)

Deprecated in favor of [managed.<name>]. Top-level [postgres] / [redis] / [storage] sections still auto-provision on first deploy during the deprecation window, but every deploy that processes them emits a deprecation warning. Run floo services migrate to move an existing app onto CLI-managed state in .floo/services.lock — it is idempotent and has zero data impact.
If you’re still on the legacy authoring path, the sections are top-level in floo.app.toml (not nested under [services.*]).
FieldTypeDefaultDescription
tierstringnoneAccepted for backwards-compatibility but ignored — every managed service ships with the same defaults. See Managed Services → Capacity.

[environments.<name>]

Per-environment overrides. <name> is the environment slug — dev and production are the two that exist out of the box.
FieldTypeDefaultDescription
access_modestringnoneParsed but not yet applied on push deploys. Schema-valid, but only [app] access_mode flows through to the running app today. See note below.
Per-environment access_mode is currently a no-op on push deploys: the schema validates the value (so a typo is caught at preflight), but the server does not apply it. The reason is safety — applying env overrides through the same column as the global default would silently downgrade sibling envs, which is the failure shape the post-2026-04-30 doctrine exists to prevent. The fix is a deferred-apply column on the deploy row, tracked as a follow-up. Until that lands, set one mode under [app] and use floo deploy --access-mode <mode> --app <name> for any env-specific override.

[cron.<name>]

Scheduled jobs run by the platform. Declared as [cron.<name>] sections in floo.app.toml.
FieldTypeDefaultDescription
schedulestringrequiredCron expression, e.g. "0 9 * * *" for 9am UTC daily
commandstringrequiredShell command executed in the target service container
servicestringrequiredName of the service whose image runs the command
timeoutinteger300Maximum execution time in seconds
Example:
[cron.daily-report]
schedule = "0 9 * * *"
command = "python -m reports.daily"
service = "api"
timeout = 600

[github]

Controls GitHub integration behavior for the connected repo.
FieldTypeDefaultDescription
deploy_on_pushbooleantrueWhen false, GitHub push webhooks do not trigger deploys — the agent deploys manually

Precedence

When you run a command without --app, the CLI resolves the app in this order:
  1. --app <name> flag
  2. Nearest floo.service.toml
  3. Nearest floo.app.toml
Access mode resolution:
  1. [environments.<env>].access_mode
  2. [app].access_mode in floo.app.toml
Resource precedence: per-service values override global [resources].

Validation

floo preflight --json
The CLI fails preflight if:
  • service names are duplicated
  • a service port is invalid
  • an inline service also has a floo.service.toml in its directory
  • a multi-service app has no public service
  • managed service sections are placed in floo.service.toml instead of floo.app.toml