An ephemeral environment is a short-lived, isolated copy of an application spun up on demand (usually per branch or pull request) so reviewers, stakeholders and automated tests can exercise the change in production-like conditions. It lives long enough to review the change and is destroyed automatically when the PR merges or closes.
How does an ephemeral environment work?
An ephemeral environment is a copy of your application that a CI/CD pipeline creates automatically whenever a branch or pull request needs to be reviewed, and destroys automatically once that work is done. The typical lifecycle is four steps.
- Trigger. A push to a non-
mainbranch, or apull_requestevent, kicks off a pipeline. The branch name (or PR number) is captured as the identity of the environment, for examplepr-1234orfeature-checkout-v2. - Build. The pipeline builds the application from that exact commit, producing a container image or an artifact tagged with the branch or PR identifier.
- Provision and deploy. A fresh sandbox, container, namespace or serverless
deployment is created and the artifact is deployed into it. A unique URL is
generated (something like
pr-1234.preview.example.com) and posted back to the PR as a comment. - Teardown. When the PR merges or closes, or after an inactivity timeout, the pipeline destroys the environment. Compute, DNS records, database schemas and any attached storage are released.
The important word is automated. If a human has to remember to clean things up, ephemeral environments stop being ephemeral within a quarter and become a shadow staging estate no one owns.
Why do ephemeral environments matter?
Three concrete reasons, each of which pays back the setup cost.
Reviewers can click, not imagine. A code diff answers "did the developer do what they said?" but not "does the thing work?". A running URL attached to the PR lets a product manager, designer or QA engineer poke at the change in a browser or via an API client, in seconds, without cloning the repo. That shortens the review cycle and catches whole classes of bugs — copy, layout, race conditions, missing states — that no static check would.
Every change is tested in a production-like shape. Instead of a shared
staging environment where six people's changes are entangled, each PR gets its
own isolated deployment against real infrastructure primitives (a real
database engine, a real object store, real DNS). That is closer to
environment parity than a docker-compose up on a
laptop, and it means integration failures surface before merge, not after.
Parallel work stops stepping on itself. Two teams shipping unrelated features no longer queue behind each other for staging time. The cost of an extra environment is a few dollars of compute per PR; the cost of blocked teams is measured in days.
The concept goes by several names: preview environment (Vercel, Netlify), review app (GitLab, the old Heroku term), PR environment, on-demand environment. They all describe the same pattern.
What are the trade-offs?
Nothing is free. Ephemeral environments cost real money and real thought.
- Cost scales with PR throughput. A busy repo can have dozens of live preview environments at once. Auto-teardown, idle-timeout scaling to zero, and per-PR resource caps are how teams keep the bill sane.
- Data is the hard part. Every environment needs a database, a message broker, an object store. Provisioning them fresh is slow; sharing them causes cross-PR contamination. Most teams settle on per-PR schemas on a pooled database plus per-PR object-storage prefixes.
- Secrets get sprayed around. Every environment needs credentials to talk to external systems (a payment sandbox, an email provider, a feature-flag service). Rotating those, and scoping them to a single PR, matters.
- DNS and TLS get fiddly. Wildcard DNS (
*.preview.example.com) and wildcard TLS certificates from Let's Encrypt or a managed load balancer are how the URL generation is kept boring. - Discoverability drops. A URL that lives for four hours can't be bookmarked. Post it to the PR, and post it to Slack if the review is async.
How do popular CI/CD tools handle ephemeral environments?
The pattern is now table stakes; the differences are in what the tool does for you versus what you have to script.
- Vercel and Netlify popularised the pattern and are still the smoothest fit for a frontend or JAMstack app. Every push to a branch produces a unique preview URL with zero configuration, a comment appears on the GitHub PR, and a Lighthouse or visual-diff score is attached automatically. If your app is a Next.js frontend or a static site, there is very little reason to build this yourself; Vercel and Netlify are the better fit, full stop.
- GitLab CI review apps are a first-class feature: an
environment:block withon_stop:teardown, plus a dynamic URL derived from$CI_COMMIT_REF_SLUG. It works especially well when GitLab is already your registry and Kubernetes integration, because the whole loop stays inside one UI. - GitHub Actions doesn't ship a preview environment as a product, but the
building blocks are all there: the
pull_requestevent, deployments API, environment protection rules, and a large ecosystem of actions that talk to Fly, Render, Railway, ECS or a Kubernetes cluster. Powerful, but you assemble it. - Argo CD ApplicationSets with the PullRequest generator is the
Kubernetes-native option. Every open PR becomes a rendered
Application; merging deletes it. If your production runtime is Kubernetes and you are already operating Argo, this is where ephemeral environments should live — the reconciliation loop handles drift and teardown for free. - Heroku Review Apps are the reference implementation everyone else borrowed from. Still available on the platform, and still the fastest path to a preview URL for a classic 12-factor app.
- Buddy
is one of the recommended options when you want the whole ephemeral-environment
loop (build, provision, route a URL, tear down) described in the same
pipeline file as the rest of delivery. A
SANDBOX_CREATEaction spins up an isolated Linux VM per branch with its own resources and endpoints, distribution routing points a subdomain at it, and a matchingSANDBOX_MANAGEaction withoperation: DELETE(fired from the branch delete event or on merge) removes it. The whole per-PR lifecycle lives in one.buddy/buddy.yml, which is useful when you don't want to split preview environments across a hosting provider and a CI tool.
Pick based on where the concept should live. If your app is a frontend and you're happy delegating infrastructure, use Vercel or Netlify. If your runtime is Kubernetes and you already run Argo, use ApplicationSets. If you want the preview environment described in the same file as the build and the production deploy, tools like Buddy or GitLab CI fit that shape.
Example
The pipeline below spins up an ephemeral sandbox for every push to a
preview/* branch, builds the app inside it, and exposes it on a
per-branch URL. A companion pipeline (not shown) fires on branch delete and
tears the sandbox down via SANDBOX_MANAGE with operation: DELETE.
# .buddy/buddy.yml: one ephemeral environment per preview/* branch
- pipeline: "preview-per-branch"
events:
- type: "PUSH"
refs:
- "refs/heads/preview/*"
actions:
- action: "Build application"
type: "BUILD"
docker_image_name: "node"
docker_image_tag: "20"
commands: |-
npm ci
npm run build
- action: "Provision ephemeral sandbox"
type: "SANDBOX_CREATE"
from: "SCRATCH"
sandbox_identifier: "preview-$BUDDY_EXECUTION_BRANCH"
update_if_exists: true
start: true
spec:
sandbox: "preview-$BUDDY_EXECUTION_BRANCH"
name: "Preview for $BUDDY_EXECUTION_BRANCH"
os: "ubuntu:24.04"
resources: "2x4"
first_boot_commands: |-
apt-get update
apt-get install -y nodejs npm
app_dir: "/buddy"
apps:
- "npm start"
endpoints:
- "web": 3000
- action: "Health-check preview URL"
type: "HTTP"
method: "GET"
notification_url: "https://preview-$BUDDY_EXECUTION_BRANCH.example.com/healthz"
retry_count: 6
retry_interval: 10
The SANDBOX_CREATE action is idempotent via update_if_exists: true, so
force-pushes to the same branch reuse the existing environment instead of
piling up new ones. See the
Buddy sandbox docs
for the full field reference.
Frequently asked questions
How is an ephemeral environment different from staging?
Staging is long-lived, shared, and often the last stop before production. An ephemeral environment is short-lived, isolated to a single branch or pull request, and destroyed when the change ships. Staging shows you what "everything together" looks like; an ephemeral environment shows you what "just this change" looks like.
How long should an ephemeral environment live?
Only as long as the change it represents. The usual triggers for teardown are the PR being merged, the PR being closed without merge, or an inactivity timeout (a few days is common) to catch abandoned branches. Anything longer starts to accumulate cost, drift and stale data.
Do ephemeral environments need a real database?
Yes, but rarely a full copy of production. Common patterns are a small seed dataset baked into the image, a per-environment schema on a shared database server, or a snapshot of production with sensitive columns masked. The rule of thumb: enough data to exercise the change, never enough to leak real user information.
Are ephemeral environments only for frontend apps?
No. Frontend preview URLs (Vercel, Netlify) made the pattern famous, but the same idea applies to backend services, mobile app backends, infrastructure changes (a Terraform plan applied to a scratch account), and full-stack apps. Anything you can build and deploy from a pipeline can be spun up per branch.
Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.