> ## Documentation Index
> Fetch the complete documentation index at: https://getfloo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Config File Spec

> All floo config fields, file shapes, and precedence rules.

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

| Shape                   | Files                                             | When to use                                                 |
| ----------------------- | ------------------------------------------------- | ----------------------------------------------------------- |
| Single service          | `floo.app.toml`                                   | One service, with or without managed credential attachments |
| Inline multi-service    | `floo.app.toml`                                   | All services defined in one file                            |
| Delegated multi-service | `floo.app.toml` + per-service `floo.service.toml` | Service config lives alongside service code                 |

## Single service

```toml theme={null}
[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:

```bash theme={null}
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:

```toml theme={null}
[app]
name = "crm"

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

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

## Inline multi-service

```toml theme={null}
[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`:

```toml theme={null}
[app]
name = "full-stack"

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

[services.api]
path = "./api"
```

`web/floo.service.toml`:

```toml theme={null}
[app]
name = "full-stack"

[service]
name = "web"
type = "web"
port = 3000
ingress = "public"
```

`api/floo.service.toml`:

```toml theme={null}
[app]
name = "full-stack"

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

## Field reference

### `[app]`

| Field         | Type   | Default    | Description                      |
| ------------- | ------ | ---------- | -------------------------------- |
| `name`        | string | required   | App name (DNS-safe)              |
| `access_mode` | string | `"public"` | `public`, `password`, `accounts` |

### `[services.<name>]`

| Field             | Type    | Default    | Description                                                                                                       |
| ----------------- | ------- | ---------- | ----------------------------------------------------------------------------------------------------------------- |
| `type`            | string  | required   | `web`, `api`, `worker`                                                                                            |
| `port`            | integer | required   | Port the service listens on                                                                                       |
| `ingress`         | string  | `"public"` | `public` (internet-facing) or `internal` (only reachable by other services in the same app)                       |
| `env_file`        | string  | none       | Relative path to env file synced on deploy                                                                        |
| `path`            | string  | `.`        | Relative path to service directory (multi-service apps)                                                           |
| `dev_command`     | string  | none       | Shell command run by `floo dev` to start the service locally. Example: `"npm run dev"`                            |
| `migrate_command` | string  | none       | Shell command run before `floo dev` starts the service (and after each deploy). Example: `"alembic upgrade head"` |
| `domain`          | string  | none       | Custom 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`.

| Field      | Type         | Default              | Description                                                                                                                            |
| ---------- | ------------ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `required` | string array | `[]`                 | Env var names that must be set before deploy                                                                                           |
| `optional` | string array | `[]`                 | Env var names documented for the service but not required                                                                              |
| `managed`  | string array | implicit legacy mode | Managed service credentials to inject into this service: `postgres`, `redis`, `storage`, or named handles such as `postgres:analytics` |

```toml theme={null}
[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.

| Field      | Type    | Default    | Description                                |
| ---------- | ------- | ---------- | ------------------------------------------ |
| `name`     | string  | required   | Service name (DNS-safe, 2-21 chars)        |
| `type`     | string  | required   | `web`, `api`, `worker`                     |
| `port`     | integer | required   | Port the service listens on                |
| `ingress`  | string  | `"public"` | `public` or `internal`                     |
| `env_file` | string  | none       | Relative path to env file synced on deploy |

### `[resources]`

| Field           | Type    | Default   | Description                                 |
| --------------- | ------- | --------- | ------------------------------------------- |
| `cpu`           | string  | `"1"`     | vCPU allocation                             |
| `memory`        | string  | `"512Mi"` | Memory allocation                           |
| `min_instances` | integer | `0`       | Min instance count (0 allows scale to zero) |
| `max_instances` | integer | `10`      | Max 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`](/docs/cli/preflight) — destroy it explicitly with [`floo services remove`](/docs/cli/services#remove). The imperative equivalent is [`floo services add <type> --name <name>`](/docs/cli/services#add).

| Field     | Type    | Default  | Description                                                                                           |
| --------- | ------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `type`    | string  | required | `postgres`, `redis`, or `storage`                                                                     |
| `tier`    | string  | none     | Accepted for backwards-compatibility but ignored — every managed service ships with the same defaults |
| `enabled` | boolean | `true`   | Set `false` to keep a declaration without provisioning it                                             |

```toml theme={null}
[managed.primary]
type = "postgres"

[managed.cache]
type = "redis"
```

Credentials are injected per-service via the [`[services.<name>.env]`](#services-name-env) `managed` field above.

### `[preview]`

Deprecated for enablement. `enabled` and `ttl_hours` are read-time aliases for [`[github]`](#github) `preview_environments` and `preview_ttl_hours`.

| Field       | Type    | Default | Description                                               |
| ----------- | ------- | ------- | --------------------------------------------------------- |
| `enabled`   | boolean | `false` | **Deprecated.** Alias for `[github] preview_environments` |
| `ttl_hours` | integer | `72`    | **Deprecated.** Alias for `[github] 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.

| Field          | Type   | Default   | Description                     |
| -------------- | ------ | --------- | ------------------------------- |
| `mode`         | string | `"empty"` | `empty`, `seed`, or `clone-dev` |
| `seed_command` | string | none      | Required when `mode = "seed"`   |

```toml theme={null}
[preview.data]
mode = "seed"
seed_command = "bin/rails db:seed"
```

Supported modes:

* `empty` provisions fresh preview branches and copies no data.
* `seed` provisions fresh preview branches, runs migrations, then runs `seed_command` once in a preview-scoped job.
* `clone-dev` copies 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](/docs/guides/preview-environments#clone-dev).

Unsupported modes fail closed. Raw prod clone is not supported, and sanitized prod clone is unavailable until the admin-gated runner exists. See [Preview Environments](/docs/guides/preview-environments).

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

<Warning>
  **Deprecated** in favor of [`[managed.<name>]`](#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`](/docs/cli/services#migrate) to move an existing app onto CLI-managed state in `.floo/services.lock` — it is idempotent and has zero data impact.
</Warning>

If you're still on the legacy authoring path, the sections are top-level in `floo.app.toml` (not nested under `[services.*]`).

| Field  | Type   | Default | Description                                                                                                                                                                  |
| ------ | ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tier` | string | none    | Accepted for backwards-compatibility but ignored — every managed service ships with the same defaults. See [Managed Services → Capacity](/docs/guides/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.

| Field         | Type   | Default | Description                                                                                                                                        |
| ------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `access_mode` | string | none    | **Parsed but not yet applied on push deploys.** Schema-valid, but only `[app] access_mode` flows through to the running app today. See note below. |

<Warning>
  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.
</Warning>

### `[cron.<name>]`

Scheduled jobs run by the platform. Declared as `[cron.<name>]` sections in `floo.app.toml`.

| Field      | Type    | Default  | Description                                            |
| ---------- | ------- | -------- | ------------------------------------------------------ |
| `schedule` | string  | required | Cron expression, e.g. `"0 9 * * *"` for 9am UTC daily  |
| `command`  | string  | required | Shell command executed in the target service container |
| `service`  | string  | required | Name of the service whose image runs the command       |
| `timeout`  | integer | `300`    | Maximum execution time in seconds                      |

Example:

```toml theme={null}
[cron.daily-report]
schedule = "0 9 * * *"
command = "python -m reports.daily"
service = "api"
timeout = 600
```

### `[github]`

Controls GitHub integration behavior for the connected repo.

| Field                  | Type    | Default | Description                                                                                       |
| ---------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------- |
| `deploy_on_push`       | boolean | `true`  | When `false`, GitHub push webhooks do not trigger deploys — the agent deploys manually            |
| `preview_environments` | boolean | `false` | When `true`, opening a pull request deploys a [preview environment](/docs/guides/preview-environments) |
| `preview_ttl_hours`    | integer | `72`    | Idle preview lifetime before the reaper tears it down                                             |

```toml theme={null}
[github]
preview_environments = true
preview_ttl_hours = 72
```

Previews are off unless `preview_environments = true`; a missing key reconciles to off. `preview_ttl_hours` is only read while previews are enabled, so declaring it next to `preview_environments = false` has no effect and previews opted in per PR 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](/docs/guides/preview-environments#turn-previews-on).

## 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

```bash theme={null}
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`
