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 that keeps a service’s configuration beside its 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
port in floo.app.toml is inline, and both parsers ignore any floo.service.toml in its directory.
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.
Top-level [env] in floo.app.toml does not opt out: the CLI rejects it as an unknown key, and the push parser ignores it with an unknown_manifest_key warning. Move an existing [env] managed = [] 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], and min_instances there has no effect on deploy. 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; the CLI rejects a concurrency key, and the push parser ignores it with an unknown_manifest_key warning.
Per-service resource fields override delegated service values, which override
the global [resources] section. A delegated service’s per-service override
goes in a nested [services.<name>.resources] table: flat cpu or memory on
a delegated entry is rejected by the CLI and ignored on deploy. 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, a memory value that appears in no row, or a memory value not allowed for the chosen cpu fails the deploy at config parse. When memory is omitted and the default 512Mi is not allowed for the chosen cpu, floo uses the largest size that row allows and writes the adjustment to the deploy log as a warning.
Setting min_instances = 1 bills continuously for one ready instance, even
while idle; rates are on getfloo.com/pricing.
See Scaling and availability for 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 the STORAGE_S3_* keys. 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 a fresh preview Postgres branch with a blank database; migrations run from scratch.seedprovisions a fresh preview Postgres branch with a blank database, runs migrations, then runsseed_commandonce in a preview-scoped job.clone-devcopies dev data from every attached managed resource: Postgres database 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.
clone-prod-sanitized, which requires sanitize_command, parses but fails at provisioning until the admin-gated runner exists. See Preview environments.
[postgres], [redis], [storage] (deprecated)
Legacy sections are top-level in floo.app.toml, not nested under [services.*].
[environments.<name>]
Per-environment overrides. <name> is dev, prod, or preview; a table with any other name that sets access_mode or edge fails the deploy.
This table accepts only access_mode and edge. Resource fields are not accepted per environment (the CLI rejects them; the push parser ignores them with a warning) 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
- the app’s stored access mode as the seed, only when the environment does not exist yet
accounts to public unless your org or app has an approval policy that gates access broadening; then, for dev, an org admin or their authorized agent applies the committed change with floo redeploy --app <name> --rebuild.
[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.
[auth] allowed_domains is ignored. Push, release, and promote apply a looser access_policy unless your org or app has an approval policy that gates access broadening; then, for dev, an org admin or their authorized agent applies the committed change with floo redeploy --app <name> --rebuild.
[auth.branding]
Both parsers accept branding. The server validates lengths, URLs, and colors.
[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 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
The CLI resolves the app in this order:--app <name>flag- The nearest directory, walking up from the current one and stopping at a
.gitboundary, that holdsfloo.service.tomlorfloo.app.toml. When it holds both,floo.service.tomlwins, and differing[app] namevalues in the two files are an error.
[environments.<env>].access_mode[app].access_modeinfloo.app.toml- the environment’s current applied mode
- the app’s stored access mode as the seed for an environment that does not exist yet
[resources].
Validation
- service names are duplicated
- a service port is invalid, or a non-worker service has none
- managed service sections are placed in
floo.service.tomlinstead offloo.app.toml - a table contains a key the CLI does not know