Skip to main content
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 — never a side effect of editing or deleting config. You can provision either imperatively with floo services add (below) or declaratively with [managed.<name>] 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.
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.
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:
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

Remove

floo services remove is the only path that destroys managed-service data. Deploy never destroys, regardless of what’s in floo.app.toml.
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.
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. If your app was created before Redis or Storage dev/prod isolation landed and data separation matters, email team@getfloo.com. 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:
Reset destroys and recreates only preview-owned Postgres state:
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 clone-dev for every attached managed Postgres, Redis, and Storage resource. It does not support raw prod clone, sanitized prod clone, or provider-native copy-on-write branching; Storage copy is explicit object copy. See 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:
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.

Legacy TOML sections (deprecated)

Earlier versions of floo accepted [postgres], [redis], and [storage] top-level sections in floo.app.toml as a declarative shortcut:
These sections still auto-provision on first deploy during the deprecation window, but every deploy that processes them emits a warning:
To migrate an existing 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: 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.

Redis health and capacity

Upstash meters Redis commands and can reject data-plane traffic when a database reaches a request-rate limit, daily allowance, or account budget cap. A TCP connection or Redis PING does not prove application commands are available. floo runs one billed EVAL canary command per ready Redis database every minute. Its Lua script writes a random token under a reserved key, reads it back, and sets a two-minute expiry atomically. It never reads, lists, overwrites, or deletes a customer key. This adds 43,200 billed commands per database in a 30-day month. Upstash currently includes 500,000 monthly commands on its free database allowance, so the canary uses 8.64% of that allowance before application traffic. Provider limits and prices can change. See Upstash pricing for the current provider contract. Run the doctor when Redis work or cron coordination fails:
throttle_request_rate means requests are arriving faster than the database allows. Reduce request concurrency or batch commands, then contact team@getfloo.com to raise managed throughput. throttle_daily_limit means the database exhausted its daily or monthly command allowance. Reduce command volume, then contact team@getfloo.com for paid capacity. throttle_temporary is provider-imposed throttling without a more specific published reason. Retry later and contact the same address if it persists. Authentication, permission, configuration, missing, or stale-canary failures need floo intervention. The doctor gives the matching action. Its output never contains credentials or raw provider responses.
Need more than 25 connections? Email team@getfloo.com — for sustained higher concurrency we provision dedicated Postgres instances rather than a self-serve performance tier.

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%.
  • CLIfloo 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

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.

Databases

Code examples and debugging for Postgres and Redis.

Cloud Storage

Upload, download, and signed URL patterns.

Cron Jobs

Schedule recurring tasks inside your app’s containers.