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

# Managed Services

> Provision managed Postgres, Redis, and Storage via the CLI. Understand the lock file, dev/prod isolation, and the legacy TOML deprecation.

Managed services are floo's stateful primitives — Postgres, Redis, and Storage. They hold persistent data, they outlive any single deploy, and destroying them is irreversible. For that reason **destroying a managed service is always an explicit [`floo services remove`](#remove)** — never a side effect of editing or deleting config. You can *provision* either imperatively with `floo services add` (below) or declaratively with [`[managed.<name>]`](/reference/config-spec) blocks in `floo.app.toml`; a deploy provisions anything declared-but-missing, but never destroys.

## Provision a service

Use `floo services add` for any managed service. The command is idempotent on `(app, type, name)`, so re-running it is safe.

```bash theme={null}
floo services add postgres --app my-app
floo services add redis --app my-app
floo services add storage --app my-app
```

Each command:

1. Provisions the underlying resource (a Postgres schema + role on the shared instance, an Upstash Redis database, or a GCS bucket).
2. Synthesizes the matching env vars and injects them into your app's environment scope.
3. Writes `.floo/services.lock` — commit it alongside other changes.

| Service  | Type flag  | Env vars injected                                                        |
| -------- | ---------- | ------------------------------------------------------------------------ |
| Postgres | `postgres` | `DATABASE_URL`, `PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD` |
| Redis    | `redis`    | `REDIS_URL`                                                              |
| Storage  | `storage`  | `STORAGE_BUCKET`, `STORAGE_URL`                                          |

Connection credentials are never stored in `floo.app.toml` or `.floo/services.lock`. They're synthesized at deploy time and injected directly into the runtime environment. Postgres `DATABASE_URL` is a standard PostgreSQL URI that mainstream frameworks can parse directly; the `PG*` vars expose the same default connection as separate fields for tools that prefer them.

## Attach Credentials To Services

For single-service apps, managed credentials go to the only runtime service unless top-level `[env] managed = []` opts out. For multi-service apps, put the attachment next to the service that needs it:

```toml theme={null}
[services.web]
type = "web"
path = "./web"
port = 3000

[services.web.env]
managed = []

[services.api]
type = "api"
path = "./api"
port = 8000

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

Handles map to generated env vars: `postgres` gives `DATABASE_URL` plus `PG*`, `redis` gives `REDIS_URL`, and `storage` gives `STORAGE_BUCKET` plus `STORAGE_URL`. Named services use `type:name`, such as `postgres:analytics` for `DATABASE_URL_ANALYTICS`.

If no service declares `managed`, floo keeps legacy all-service injection. Once any service declares it, services without `managed` receive no managed-service credentials. floo only strips keys owned by managed-service records, so an external user-managed `DATABASE_URL` is not swept up by this filter. `floo preflight --json` shows the exact per-service plan in `env_injection_plan`.

## Inspect

```bash theme={null}
floo services list --app my-app                # everything: managed + application services
floo services show default --app my-app        # one row, with status and connection info
floo env get DATABASE_URL --app my-app          # plaintext credential when you actually need it
```

## Remove

`floo services remove` is the only path that destroys managed-service data. Deploy never destroys, regardless of what's in `floo.app.toml`.

```bash theme={null}
# Interactive: type the service name to confirm
floo services remove postgres --app my-app

# Non-interactive: explicit verbose flag — no --yes shortcut
floo services remove postgres --app my-app --yes-i-know-this-destroys-data
```

The flag name is deliberately verbose. Destroying user data must be an explicit, acknowledged decision — there is no `--force`.

## The lock file

Every `add`, `remove`, and `migrate` updates `.floo/services.lock` atomically. The file is auto-generated, committed to the repo, and never hand-edited. It's a **record** of state, not a source — the platform DB is authoritative. Same model as `package-lock.json` or `Cargo.lock`.

```json theme={null}
{
  "version": 1,
  "managed_services": [
    {
      "type": "postgres",
      "name": "default",
      "tier": "basic",
      "created_at": "2026-04-24T15:00:00Z"
    }
  ]
}
```

Hand-editing does nothing. Running any `floo services` command re-syncs the file from platform truth.

## Dev and prod isolation

Each service type isolates dev from prod differently. **Read this before promoting to prod.**

| Service      | Dev/prod model                                                                                                                                                                                                                                                     |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Postgres** | **Fully isolated.** Provisioning creates both `{schema}_dev` and `{schema}_prod` schemas, each with its own role and password. Dev gets `DATABASE_URL` + `PG*` for `_dev`; prod gets a different role/password for `_prod`. Promote does not need to re-provision. |
| **Redis**    | **Isolated for new services.** Provisioning creates separate dev and prod Upstash databases. Legacy rows created before dev/prod Redis isolation may still share one database until floo backfills them.                                                           |
| **Storage**  | **Isolated for new services.** Provisioning creates separate dev and prod GCS buckets. Legacy rows created before dev/prod Storage isolation may still share one bucket until floo backfills them.                                                                 |

If your app was created before Redis or Storage dev/prod isolation landed and data separation matters, email [team@getfloo.com](mailto:team@getfloo.com?subject=Managed%20service%20isolation%20check). We can inspect the row and backfill dedicated prod resources when needed.

## Preview Database Branches

Preview environments get their own managed resources too. For managed Postgres, the preview-owned resource is called a **preview database branch**. It is isolated from dev, prod, and other previews, and it is the branch the preview app uses for migrations and runtime writes.

Inspect branches from the CLI:

```bash theme={null}
floo db branches list feat-db-abcde --app my-app
floo db branches show feat-db-abcde --app my-app --name default
```

Reset destroys and recreates only preview-owned Postgres state:

```bash theme={null}
floo db branches reset feat-db-abcde --app my-app --name default
```

Dev and prod are untouched. JSON output includes reset scope and `dev_prod_untouched`, but never plaintext credentials.

V1 supports empty preview branches, seeded preview branches, and managed-Postgres `clone-dev`. It does not support raw prod clone, sanitized prod clone, Redis copy/reset, Storage copy/reset, or provider-native copy-on-write branching. See [Preview Environments](/guides/preview-environments) for the full contract.

## Preflight reconciliation

`floo preflight` shows the planner's view of managed-service state across five bins:

* **`to_provision`** — declared (via CLI or legacy TOML) but no row exists. Will be created on the next deploy or `floo services add`.
* **`to_retain`** — declared and exists. No-op.
* **`to_retry`** — declared and a `failed` row exists. The next deploy retries provisioning into the same row.
* **`to_orphan`** — exists, not declared anywhere. **Will NOT be deprovisioned.** Surfaced as a non-blocking warning telling you to run `floo services remove` if you actually want destruction.
* **`in_flight_deprovisioning`** — a `deprovisioning` row is mid-flight. Excluded from `to_retain` / `to_orphan` until it completes.

The orphan bin exists because deploy never destroys data. Clearing an orphan is always an explicit `remove`.

## Declare services in config (`[managed.<name>]`)

If you'd rather keep managed-service intent in `floo.app.toml` alongside the rest of your app shape, declare each one as a `[managed.<name>]` block. `<name>` is a logical instance name, so you can declare more than one of a type:

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

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

A deploy provisions any declared service that doesn't exist yet. Removing a block never destroys the service — it surfaces an orphan warning in `floo preflight`, and you destroy it explicitly with `floo services remove`. This is the modern replacement for the legacy top-level `[postgres]` / `[redis]` / `[storage]` sections below. Full field reference: [config spec](/reference/config-spec).

## Legacy TOML sections (deprecated)

Earlier versions of floo accepted `[postgres]`, `[redis]`, and `[storage]` top-level sections in `floo.app.toml` as a declarative shortcut:

```toml theme={null}
# legacy — still works, but deprecated
[postgres]
tier = "basic"

[redis]

[storage]
```

These sections still auto-provision on first deploy during the deprecation window, but every deploy that processes them emits a warning:

```
DEPRECATED: [postgres] in floo.app.toml. Run 'floo services migrate' to move to CLI-managed state.
```

To migrate an existing app:

```bash theme={null}
floo services migrate --app my-app
```

`migrate` is idempotent and has zero data impact — it records the existing managed-service rows in `.floo/services.lock` and prints instructions to remove the legacy TOML sections. After migration, your authoring surface is the CLI alone; the underlying managed services are untouched.

After the sunset date, the parser will ignore the legacy sections entirely. Apps that haven't migrated keep their managed services (the CLI sees them via `floo services list`); only the authoring surface changes.

## Capacity

Every managed Postgres service ships with the same defaults — there are no self-serve tiers to choose between:

| Setting                     | Default |
| --------------------------- | ------- |
| Max connections             | 25      |
| Statement timeout           | 60s     |
| Idle-in-transaction timeout | 120s    |
| Lock timeout                | 10s     |
| `work_mem`                  | 128 MB  |

The `--tier` flag on `floo services add postgres` is accepted for backwards-compatibility but ignored — every value maps to the same defaults.

Redis uses Upstash. Storage uses GCS buckets. Neither has tier choices.

<Note>
  **Need more than 25 connections?** Email [team@getfloo.com](mailto:team@getfloo.com?subject=Capacity%20request) — for sustained higher concurrency we provision dedicated Postgres instances rather than a self-serve performance tier.
</Note>

## Connection usage

floo tracks live Postgres connection usage and warns you before you hit the limit:

* **Dashboard** — the managed-Postgres panel shows `N / 25 connections in use` and surfaces a warning when you cross 75%.
* **CLI** — `floo db connections --app my-app [--env dev|prod]` prints the same data, with a `--json` mode for agents.
* **Email** — the org owners and admins get an email at 75% sustained, with a one-click `mailto:team@getfloo.com` for capacity requests. One email per app per 24h max.

Most apps that hit the cap either need connection pooling at the application layer (PgBouncer, SQLAlchemy pool tuning) or more raw capacity. If pooling isn't enough, email us.

## Usage metering

| Resource            | Metered by                                |
| ------------------- | ----------------------------------------- |
| Compute (Cloud Run) | per vCPU-second + per GiB-second          |
| Postgres            | shared instance, metered by compute       |
| Redis               | serverless (Upstash), metered by commands |
| Storage             | per GB stored + per operation             |
| Bandwidth           | per GB egress                             |

Your plan includes compute credits — 5 lifetime (Free), 5 / month (Hobby), 20 / month (Pro), 200 / month (Team) — and one credit equals \$1 of metered app usage. Usage beyond your credits is billed as overage up to your spend cap.

<CardGroup cols={2}>
  <Card title="Databases" href="/guides/databases">
    Code examples and debugging for Postgres and Redis.
  </Card>

  <Card title="Cloud Storage" href="/guides/cloud-storage">
    Upload, download, and signed URL patterns.
  </Card>

  <Card title="Cron Jobs" href="/guides/cron-jobs">
    Schedule recurring tasks inside your app's containers.
  </Card>
</CardGroup>
