A pull request gets its own environment
What a review environment is, why it is a dev tier rather than a copy of staging, what it costs, and what happens to it when the pull request closes.
Open a pull request against a repository this platform builds and it gets a whole environment: its own namespace, its own database, its own hostname. It exists while the pull request is open and it is collected when it closes.
Each one with its environment, or the reason it does not have one.
Open pull requests →
What gets created

- 1
An Environment named review-pr-N
Not in any DeploymentPipeline, deliberately. A pull request is not something you promote into — adding it to the pipeline once made the portal draw every open pull request as a promotion target of staging, which is the relationship backwards.
- 2
A project cell
A
ProjectReleaseBindingcopying the root environment's, which is what creates the namespace and its quota. - 3
A copy of every backing service
A real Postgres, a real object store — one per pull request, sized from the root environment's binding so a review database is small.
retainPolicyisDelete: a review environment's data should not outlive the pull request.This is what makes review environments expensive, and why there is a cap.
- 4
A forked component per changed repository
Named
<component>-pr-N, pinned to the pull request's branch. Components from other repositories in the same project are not forked — the original is bound in at whatever the root environment is running, so the environment is "this change against the rest of the project as it stands" rather than a snapshot of everything.
Where it sits in the promotion order
A review environment ranks below staging — it is the dev tier. Every list in this console puts pull-request environments first, before the standing environments, because a change moves from a pull request towards production and never the other way.
Standing environments first, then review environments under their own heading.
The estate by environment →
On a project page a review column is drawn as transient, with a link to the pull request it exists for. The estate is staging and production; everything else is somebody's work in progress and should not make the board churn every time a branch opens.
The one binding that looks like it is in staging
This is worth understanding, because it looks like a bug and is not.
A forked component must carry autoDeploy — without it no ComponentRelease
is ever cut and the review environment has nothing to bind. And autoDeploy
always writes a ReleaseBinding into the pipeline's root environment,
whichever that is.
So project-flow claims that binding first, itself, in the Undeploy state,
before the Component exists. That is what stops pull-request code deploying into
the root environment's cell against its real database. It reads the release name
off it and deletes it, usually within thirty seconds.
One of those once survived for fourteen hours: deleted on time, caught by a
finalizer, and then invisible to every cleanup path because it carried no labels
and lived outside any review-pr-* environment. It showed on the project page
as though a pull request were deployed into staging.
A sweep collects them by shape now — a *-pr-<n> subject whose Component does
not exist is garbage wherever it lives — and the console keeps them out of the
estate views. /deploys still shows them, under their own heading, because a
binding that exists has to be visible somewhere or the next stuck one is
invisible too.
Why there might not be one
OC_MAX_REVIEW_ENVS is 1 on this host. A second open pull request gets a
comment on the pull request saying so, and no environment.
That is a capacity limit, not an absence, and the pull-requests page says which it is. The three states it distinguishes:
| what you see | what it means |
|---|---|
| an environment, Ready | the ordinary case |
| an environment, not Ready | the reviewer's build is broken |
| no environment, with a reason | the cap, or a build that never cut a release |
A page that simply omitted the third would be reporting a capacity limit as nothing at all.
Teardown
Closing or merging the pull request tears down everything labelled for it, in order: project binding, resource bindings, release bindings, components, environment — each finalizer waits on the layer below.
RenderedRelease objects go first. Their cleanup resolves its plane client
through the Environment, so collecting the Environment first strands
renderings that can then never finalize. Thirteen of them once hot-looped for
twenty-one hours and took three controller restarts.