Guides
Preview environments per pull request, explained
What gets created, what is shared with production and what is not, how sleeping keeps twenty of them nearly free, and the two mistakes that make previews expensive.
A preview environment is a full copy of your application, on its own URL, for one branch. Open a pull request and it appears; merge or close it and it goes away.
The reason to care is not the demo. It is that reviewing a diff tells you whether the code is plausible, and clicking the thing tells you whether it works.
What gets created
For every pull request:
- Your app, built from that branch, in its own micro-VM
- A URL:
pr-241-linktree.cmdz.app, with a certificate - Its own environment variables, inherited from a preview set you control
- Its own database, if the app has one
That last item is the important default. A preview does not get a connection to your production database. It gets its own, seeded from your seed script.
That is deliberate and occasionally inconvenient. The alternative — previews pointed at live data — means a migration in an unmerged branch can alter production, and a debug endpoint someone added at two in the morning can read real customer records from a URL that is not behind your auth. We would rather you opt into that explicitly than inherit it.
What is shared, and what is not
| Production | Preview | |
|---|---|---|
| Application code | main | the branch |
| Database | production | its own, seeded |
| Object storage | production bucket | its own bucket |
| Environment variables | production set | preview set |
| Secrets | production values | preview values you set |
| Domain | yours | generated |
You can override any of it. If a preview genuinely needs to talk to a shared staging database, point it there — just do so on purpose.
Sleeping is what makes this affordable
A preview with no traffic scales to zero after a few minutes of inactivity and wakes on the first request. The wake takes a second or two for most apps.
The cost consequence is large. Twenty open pull requests, each awake maybe an hour a day between the author, a reviewer and a designer, bill about 4% of a month each. Twenty previews cost roughly what one always-on instance costs.
Which means the honest advice is: do not ration them. A preview per pull request is the point.
The two ways previews get expensive
A preview with a background worker that never idles. A queue worker polling every second is never inactive, so it never sleeps, so twenty previews are twenty always-on workers. Either exclude workers from previews or give them a longer poll interval there.
[apps.worker]
preview = false
A large database per preview. A small Postgres is € 1.46 a month; twenty of them is € 29. If your seed data is large, share one preview database across previews with a schema per branch, or seed less.
Both are visible in the usage screen broken down per environment, so you will see it before the invoice does. And either way, the ceiling still holds.
Migrations in previews
The release command runs in a preview exactly as it does in production, which is the whole value: you find out that your migration fails on a fresh database before it runs on the real one.
If it fails, the preview deploy fails and the pull request gets the error. That is a much better place to learn it than production.
The invoice preview
A comment on the pull request with what this change does to your monthly run rate:
cmdz · preview pr-241
✓ https://pr-241-linktree.cmdz.app
cost impact of this branch, if merged:
compute +€ 1.10/mo (worker replicas 1 → 2)
storage unchanged
──────────────────────────────────────────
run rate € 7.04 → € 8.14/mo
limit € 15.00 · 54% used
Knowing that a change costs money at review time is worth considerably more than knowing it at the end of the month. This is also available to your agent through cmdz_explain_cost, which means "what will this cost" is a question you can ask the thing that wrote the branch.
Cleanup
Merging or closing the pull request removes the preview, its database and its bucket within minutes. A branch that has had no commits for thirty days is cleaned up too, after a warning, because everybody has a fix-the-thing-2 branch from March.
Sharing them
Preview URLs are public by default — reviewers, designers and clients should not need an account. If your app is pre-launch and you would rather not have it indexed or found, turn on basic authentication for previews with one setting; the credentials appear in the pull request comment.
Search engines are excluded from preview hostnames regardless.
Set your limit and start.
One click with a passkey, then you verify a payment method once to start your 14-day free trial (€ 10 of credit). After that it is prepaid pay-as-you-go — you only ever spend credit you have already bought, and no invoice ever arrives above the amount you set.