Heroku Review Apps Architecture: Requirements, Minimal Design, and Operational Guardrails
Heroku Review Apps give each pull request an isolated environment that mirrors production, using a minimal web dyno and add‑ons, with clear trust boundaries and automatic teardown.
16 Dec 2025, 02:58 UTC

When a pull request is opened, teams often need a disposable environment that runs the same code, configuration, and add‑ons as production so that changes can be tested in isolation.
Requirements
- Isolated, disposable environment per PR.
- Mirrors production config vars and add‑on attachments (e.g., Postgres, Redis).
- Spins up automatically on PR creation and tears down on merge or close.
- Uses the same slug (compiled slug) as the parent app.
Smallest Suitable Design
The minimal footprint that satisfies the requirements is a single web dyno (Free or Hobby tier) plus any required add‑on dynos, all launched from the identical slug as the parent application.
# Example: create a Review App via the Platform API (run with account that can create apps)
curl -n -X POST https://api.heroku.com/apps
-H \"Accept: application/vnd.heroku+json; version=3\"
-H \"Authorization: Bearer $HEROKU_API_TOKEN\"
-H \"Content-Type: application/json\"
-d '{'
\"name\": \"pr--\","
\"stack\": \"heroku-22\","
\"region\": \"us\"
}'
# Then release the slug:
curl -n -X POST https://api.heroku.com/apps/pr--/releases
-H \"Accept: application/vnd.heroku+json; version=3\"
-H \"Authorization: Bearer $HEROKU_API_TOKEN\"
-H \"Content-Type: application/json\"
-d '{'
\"slug\": \"\"
}'
Trust/Data Boundaries
- Each Review App runs in its own network namespace; it cannot reach other apps unless add‑on credentials are explicitly shared.
- Configuration variables are injected at runtime; only values defined for the review app (or inherited from the parent) are available, limiting secret exposure to PR‑specific scopes.
Operational Checks
- The platform validates that the slug is compatible with the target stack before releasing.
- Logplex aggregates dyno output; health checks are derived from web dyno status.
- Account‑wide dyno‑hour quotas are enforced; creation fails if the quota would be exceeded.
- A configurable TTL (time‑to‑live) or PR‑close event triggers automatic destruction via the Heroku Scheduler‑like cleanup process.
Failure Modes
- Excessive PR traffic can consume the account’s dyno‑hour pool, leading to unexpected billing or hitting the soft limit.
- Certain add‑ons (e.g., Heroku Private Spaces, SSL endpoints) cannot be provisioned in a review app, reducing fidelity to production.
- If the slug release fails because of stack incompatibility, the review app will not start and the platform logs an error in the app’s creation flow.
Conditions That Would Change the Design
- If a team requires multiple dyno types (worker, clock) for integration tests, the design expands to include those dyno formations.
- When an add‑on that is not review‑app‑compatible is mandatory, a workaround such as a shared external service or a feature flag must be introduced.
- Should the organization enforce stricter network isolation (e.g., VPC‑level), the design would shift to using Heroku Private Spaces for review apps, accepting the associated cost and provisioning limits.
Practical verification: after opening a PR linked to a Heroku‑connected repository, watch the Dashboard for an app named pr--. Run heroku apps:info -a pr-- to confirm the app exists, then heroku config -a pr-- and heroku addons -a pr-- to verify that config vars and add‑ons match the parent. Finally, merge or close the PR and check that heroku apps no longer lists the review app within the configured TTL.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.