Skip to main content
Preview environments are short-lived deploys with their own URL and their own preview-owned managed resources. Open a pull request, and floo deploys that branch to a preview URL. Agents can also create the same kind of sandbox from the CLI for a pushed remote branch. Push again, and the same preview updates in place. Delete the preview, close the PR, or let the TTL expire, and floo tears it down. The important part: a preview does not reuse floo-managed dev or prod Postgres credentials. When a preview has managed Postgres attached, floo creates a preview database branch for that preview.

Enable Previews

Enable pull request previews in floo.app.toml:
[preview]
enabled = true
ttl_hours = 72
Commit and push the config. The next pull request creates or refreshes a preview environment when GitHub sends the PR webhook. ttl_hours controls how long an idle preview stays around. Closing or merging the PR tears it down earlier.

Create A Sandbox From The CLI

Use floo previews up when an agent needs an isolated sandbox for a feature branch before opening or relying on a pull request preview:
git push origin feat/foo
floo previews up --app my-app --branch feat/foo --wait
The command deploys GitHub source only. It never uploads local dirty files or an archive from your checkout, so push the branch or pass a remote ref before creating the sandbox. Useful lifecycle commands:
floo previews list --app my-app
floo previews status --app my-app feat/foo
floo previews logs --app my-app feat/foo --follow
floo previews delete --app my-app feat/foo --yes
The preview argument can be the exact slug, a preview URL, the source branch, or #123 when that PR number resolves to one preview. Agents should use --json for stable output:
floo previews up --app my-app --branch feat/foo --wait --json
JSON output includes the app, preview slug, source_branch, deploy_id, status, URL, expiry, database_branches, and dev_prod_untouched: true. --wait exits non-zero when the deploy fails. Managed-resource isolation failures surface as PREVIEW_MANAGED_SERVICE_ISOLATION_UNAVAILABLE; fix the attached managed service state and retry after pushing a new commit or rerunning the command. Deleting a preview sandbox tears down only preview-owned Cloud Run services, managed resources, gateway routes, and env vars. Dev and prod resources are untouched.

Preview Database Branches

A preview database branch is preview-owned managed Postgres state. It is isolated from dev, prod, and other previews. Migrations, reviewer changes, and agent experiments land in that preview branch only. V1 preview database branches are branch-like isolation on floo’s managed-resource substrate. They are not raw production clones, arbitrary point-in-time clones, or provider-native copy-on-write branches. True provider-backed branching is tracked separately in getfloo/floo#733. Use the CLI to inspect the branch behind a preview:
floo db branches list feat-db-abcde --app my-app
floo db branches show feat-db-abcde --app my-app --name default
The preview argument can be the preview slug, a preview URL, the source branch, or #123 when that PR number resolves to one preview. Use --json when an agent needs stable fields:
floo db branches list feat-db-abcde --app my-app --json
JSON includes the app, preview context, database_branches, source_environment, hydration_mode, resource_status, schema_name, reset eligibility, and expiry. It never includes plaintext database credentials.

Reset A Preview Branch

Reset drops and recreates only the selected preview database branch. Dev and prod are untouched.
floo db branches reset feat-db-abcde --app my-app --name default
In automation, confirm explicitly:
floo db branches reset feat-db-abcde --app my-app --name default --yes --json
Preview reset is tier 2. It destroys preview-only data, but it does not touch durable dev or prod data. Dry-run shows the same scope without calling the API:
floo --dry-run db branches reset feat-db-abcde --app my-app --name default
Reset works only for ready preview-owned managed Postgres branches. It is blocked when the preview has no managed Postgres attachment, when the branch is not ready, or when the data belongs to dev, prod, Redis, Storage, or an external database.

Choose Preview Data

Use [preview.data] to choose how a new preview branch starts.

Empty

empty is the default. floo creates a fresh preview database branch and copies no data.
[preview.data]
mode = "empty"
Use this for apps that can boot from migrations alone.

Seed

seed runs your seed command once after migrations and before the preview is marked live.
[preview.data]
mode = "seed"
seed_command = "bin/rails db:seed"
The command runs in a preview-scoped one-off job with preview env vars. Pushing another commit to the same PR reuses the preview data unless you change the preview data policy.

Clone Dev

clone-dev copies managed Postgres dev data into the preview branch before migrations run.
[preview.data]
mode = "clone-dev"
Use this when a reviewer needs realistic dev data but must be isolated from the actual dev database. This mode copies attached managed Postgres only.

Unsupported Modes

These are deliberately not V1 behavior:
  • Raw prod clone.
  • Sanitized prod clone until the admin-gated runner exists.
  • Redis copy or reset.
  • Storage copy or reset.
  • Merging preview database changes back into dev or prod.
  • Instant copy-on-write branches from arbitrary points in time.
If a config asks for unsupported data behavior, floo fails closed instead of silently using dev or prod credentials.

Multi-Service Apps

Preview branches follow the same managed credential attachment rules as dev and prod. Attach managed Postgres only to the services that need it:
[services.api.env]
managed = ["postgres"]

[services.web.env]
managed = []
If no service declares managed, legacy apps receive managed credentials in every service. Once any service declares managed, omitted services receive none.

Troubleshooting

SymptomMeaningWhat to do
floo db branches list shows no branchesThe preview has no floo-managed Postgres attachmentAdd managed Postgres with floo services add postgres --app my-app, then attach it with managed = ["postgres"]
Reset is blockedThe branch is not ready or is not preview-owned managed PostgresRe-run floo db branches show ... --json and inspect reset_blocked_reason
Preview creation fails before deployfloo could not provision isolated managed resourcesFix the managed-service state and push another commit
clone-dev works for Postgres but not Redis or StorageRedis and Storage copy modes are not implementedUse app-level seed logic or keep those resources empty in previews