floo.app.toml. Services are declared inline under [services.<name>]. Managed-service declarations use [managed.<name>], and credential attachments live under service env blocks. A deploy provisions declared Postgres, Redis, and Storage resources that are missing. Removing a deployed declaration is tier-3. The shared default retains the resource without review or deletion; an explicit approval policy requires review. The legacy top-level [postgres], [redis], and [storage] sections are deprecated.
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.
Monorepos
By default a service builds with its ownpath as the Docker build context, and
its Dockerfile must sit inside that directory. A service at ./apps/web cannot
read ./packages, so a workspace whose packages import each other fails at
install.
Set dockerfile to build from the repository root instead:
floo.app.toml partial
.. fails the deploy.
Each service still builds its own image. The context is the whole repository, so
COPY paths in those Dockerfiles are written from the root:
path still marks where the service’s source lives, which is what floo dev
and preflight’s source checks read, and it is what a service without
dockerfile uses as its context.
A service that sets dockerfile rebuilds whenever anything in the repository
changes, not just its own directory. That is deliberate: its build can read the
whole repository, so a change to a shared package has to invalidate it.
floo init does not generate a Dockerfile for a workspace repository. The
generated templates copy only the root manifest and lockfile, which cannot
install a workspace, so it declines and says so rather than writing one that
fails.
Config shapes
Single service
Single service with managed services
Declare the managed resources and app shape together:Inline multi-service
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
Rootfloo.app.toml:
floo.app.toml
web/floo.service.toml:
web/floo.service.toml
api/floo.service.toml:
api/floo.service.toml
type on every [services.<name>] entry, including delegated ones.
Field reference
[app]
[services.<name>]
There is no
build_args key. floo passes every env var whose name starts with
NEXT_PUBLIC_, VITE_, or REACT_APP_ to the build as a Docker build arg,
and a change to one of them rebuilds the service. Set them with
floo env set NEXT_PUBLIC_API_URL=https://... --app my-app --env dev; they are
also injected at runtime. Thread them through ARG and ENV in the Dockerfile.
See Build-time variables
and Thread build args into the build.
[services.<name>.env]
Per-service env contract in floo.app.toml, including single-service apps. In floo.service.toml, use the same fields in a top-level [env] block.
Both parsers ignore top-level [env] in floo.app.toml without error, so an existing [env] managed = [] no longer opts out; move it under [services.NAME.env].
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)
Each delegated service keeps its own floo.service.toml alongside its code. The CLI rejects unknown keys in this table.
cpu, memory, and max_instances do not belong in [service]. Put them in a [resources] table inside the same floo.service.toml; it takes the fields listed under [resources] and overrides the root file’s global values for that service.
[resources]
Resource values are app-wide: each configured value applies to dev, preview, and prod alike. See
[environments.<name>] for the fields that can vary.
Per-instance request concurrency is fixed platform-wide at 80 and is not a config key; a concurrency key fails config parse.
Per-service resource fields override delegated service values, which override
the global [resources] section. Workers use per-service instances instead
of min_instances. See Scaling and availability for the four
runtime postures and verification commands.
CPU and memory values
cpu accepts exactly four values, and each one accepts a fixed set of memory sizes:
A
cpu value outside this table, or a memory value that appears in no row, fails the deploy at config parse. A memory value that is valid in general but not for the chosen cpu is adjusted to a size that row allows, and the adjustment is written to the deploy log as a warning.
Setting min_instances = 1 bills continuously for one ready instance, even
while idle. Under the floo-credits-2026-09 rate policy the default 1 vCPU / 512 MiB shape is
about 63/month warm, excluding request activity and
credits. See Scaling and availability
for the arithmetic and migration behavior.
[managed.<name>]
Declare a managed service in config. <name> is a logical instance name and must be unique within the TOML [managed] table. Use [managed.default] for the resource that should expose conventional unsuffixed env vars. Additional named resources expose suffixed keys, such as DATABASE_URL_ANALYTICS or REDIS_URL_CACHE. A deploy provisions any declared service that doesn’t exist yet. Removing or disabling a previously deployed block produces a tier-3 to_deprovision proposal. By default, the resource keeps working without review or a teardown schedule. An explicitly configured approval policy requires human review; after approval, Redis and Storage receive a rescindable schedule while Postgres keeps working without one. Renaming a block proposes creation under the new name and removal of the old resource. Deploy does not delete data. Terminal deletion remains an explicit floo services remove action. floo services add <type> --name <name> is the operational provisioning lane for an existing app.
DATABASE_URL plus PG*; named Postgres services own only DATABASE_URL_<NAME>. Default and named Redis own REDIS_URL or REDIS_URL_<NAME>. Storage follows the same rule for STORAGE_BUCKET and STORAGE_URL. Credentials are attached per service via the [services.<name>.env] managed field above.
Managed Redis is cache-only: treat its contents as disposable. floo makes no backup, restore, or durability guarantee for it; keep authoritative data in Postgres.
[preview]
Deprecated for enablement. enabled and ttl_hours are read-time aliases for [github] preview_environments and preview_ttl_hours.
They still work for one migration window,
[github] wins whenever both are present, and each deploy that reads them emits a legacy_preview_toml warning. Move the values into [github] and delete them from [preview].
[preview.data] below is not deprecated and remains the hydration surface.
[preview.data]
Choose how a preview’s managed resources start. Applies to every attached managed Postgres, Redis, and Storage resource.
emptyprovisions fresh preview branches and copies no data.seedprovisions fresh preview branches, runs migrations, then runsseed_commandonce in a preview-scoped job.clone-devcopies dev data from every attached managed resource: Postgres schema data before migrations run, supported Redis key types with their TTLs, and Storage objects by server-side copy. Clone limits and per-resource behavior are in Preview environments.
[postgres], [redis], [storage] (deprecated)
If you’re still on the legacy authoring path, the sections are top-level in floo.app.toml (not nested under [services.*]).
[environments.<name>]
Per-environment overrides. <name> is dev, prod, or preview; any other name fails the deploy.
This table accepts only access_mode and edge; resource fields are not accepted per environment and must be set once in [resources] or on the service.
dev, prod, and preview, resolved per environment in this order:
[environments.<env>] access_mode[app] access_mode- that environment’s current applied mode
[app] access_modeas the seed, only when the environment does not exist yet
accounts or password to public; for dev, an org admin or their authorized agent can apply the committed change with floo redeploy --app <name> --rebuild. Setting password requires a shared password to exist already, which an org admin creates through the app’s access settings.
[domains."<hostname>"]
Declare a custom domain in config. A prod release creates the domain, and floo activates it after DNS is configured. To stop serving a host, delete its block and release to prod; floo retires the host and retains its certificate and DNS authorization for 7 days. See Custom domains.
[auth]
Sign-in policy and branding for accounts mode. Both apply only to the target non-preview environment when a deploy goes live.
The existing domain allowlist still governs
domain admission, but cannot be extended: its dashboard/API management controls were retired. [auth] allowed_domains is ignored. Push, release, and promote cannot loosen access_policy; for dev, an org admin or their authorized agent can apply the committed change with floo redeploy --app <name> --rebuild.
[auth.branding]
The push parser accepts branding. The CLI parser rejects [auth.branding], so a manifest declaring it cannot pass floo preflight or a local-config floo redeploy until schema parity lands.
[auth] block that declares neither access_policy nor [auth.branding] changes nothing. A [auth] block without [auth.branding] clears stored branding for the target environment.
[[routes]]
Per-path access modes. Multi-service apps declare the complete table in
floo.app.toml; each entry names a routable service and the table includes /.
Implicit single-service apps keep the route grammar below and omit service.
/ route.
In a multi-service app the table replaces conventional paths on every app
hostname, including service aliases and verified custom domains. Add /api
explicitly if it should still route to the API service. Longest-prefix selection
chooses the service and auth policy; the backend receives the unchanged path.
See App API keys and request history for the complete flow.
[edge]
IP and CIDR allow/deny rules evaluated at the gateway before a request reaches your app. Requires the Team plan or above. [environments.<env>.edge] overrides it for one environment; previews inherit the dev policy.
floo edge policy get --env prod and floo edge policy check <ip> --env prod.
[cron.<name>]
Scheduled jobs run by the platform. Declared as [cron.<name>] sections in floo.app.toml.
Example:
[github]
Controls GitHub integration behavior for the connected repo.
[preview] enablement keys, previews are off unless preview_environments = true; a missing key reconciles to off. preview_ttl_hours is read on every deploy whether or not previews are enabled, so a value declared next to preview_environments = false still applies to previews opted in per PR; when the key is absent those previews use the 72-hour default.
Any pull request can override the app default with an authorized comment on that PR: /floo preview on opts one PR in, /floo preview off opts one PR out. See Preview environments.
Precedence
When you run a command without--app, the CLI resolves the app in this order:
--app <name>flag- Nearest
floo.service.toml - Nearest
floo.app.toml
[environments.<env>].access_mode[app].access_modeinfloo.app.toml- the environment’s current applied mode
[app].access_modeas the seed for an environment that does not exist yet
[resources].
Validation
- service names are duplicated
- a service port is invalid or missing
- an inline service also has a
floo.service.tomlin its directory - a multi-service app has no public service
- managed service sections are placed in
floo.service.tomlinstead offloo.app.toml - a table contains a key the CLI does not know