All posts
herokumigrationpreview-environmentsdevops

Heroku Review Apps Alternative in 2026: Migration Guide for Teams Left Behind

PreviewDrop Team·May 20, 2026·11 min read

It's 10:47 PM on a Thursday. Your team just pushed a feature branch with a critical bug fix, and everyone needs to review it before tomorrow's demo. You open the GitHub PR, scroll down to the "View deployment" link, and... nothing. The Heroku Review App never finished building.

You check the logs: R14 (Memory quota exceeded). You bump the dyno size to Standard-2X, which adds $50/month to the bill. The build restarts, the clock ticks past midnight, and your demo is in 8 hours.

This isn't hypothetical. Thousands of teams lived this exact scenario after Heroku sunset free Review Apps in 2024. Even paid teams on Heroku's Performance dynos face unpredictable build failures, environment variable drift, and the slow realization that Heroku's preview environment model was never designed for 2026's polyglot monorepos.

But the good news is that migrating off Heroku Review Apps is a one-afternoon task. And the replacement is better — faster builds, real database isolation, and support for any stack you throw at it.

Let's walk through exactly what broke, what a modern replacement looks like, and how to migrate in six steps.


What happened to Heroku Review Apps?

Heroku Review Apps launched in 2016 as part of GitHub integration. The pitch was perfect: every pull request gets its own temporary app, complete with a copy of your staging database, a unique URL, and automatic teardown when the PR merges. Teams at Stripe, Shopify, and GitHub itself used them for years.

But three things changed:

  1. November 2022: Heroku's security breach. An attacker exfiltrated OAuth tokens for GitHub integrations. Heroku disabled the integration entirely for months. When it came back, many teams never reconnected.

  2. May 2024: Free dynos, free PostgreSQL, and free Review Apps all ended. The "free" preview deployment channel evaporated overnight. For bootstrapped startups, the cost of running review apps on paid dynos added hundreds of dollars per month — per repository.

  3. 2025 onward: Salesforce, Heroku's parent company, refocused the platform on enterprise CRM workloads. The Heroku of 2015-2021 — the indie-developer-friendly, Rails-friendly platform — is not the Heroku of 2026. New features go to Salesforce integrations and Mulesoft connectors, not preview environments.

The result: Heroku Review Apps still work if you pay for them, but the experience is fragile, expensive, and tied to a platform that's diverged from the needs of modern frontend/backend teams.


The six things a replacement must do

Before we talk about solutions, let's define the checklist. A preview environment in 2026 needs to handle:

| Requirement | Why it matters | |---|---| | Automatic per-PR deployment | No manual setup. Open a PR, get a URL. | | Database isolation | Each preview gets its own sandbox — no cross-contamination between branches. | | Environment variable management | Per-branch config without the drift nightmare. | | Multi-framework support | You shouldn't need one solution for your Rails API and another for the Next.js frontend. | | Real URLs | pr-42.myapp.example.com, not an IP address with a port number that changes every deploy. | | Fast builds | CI is already 5 minutes. The preview shouldn't add another 10. |

If your current setup fails any of these, you're accumulating "review debt" — the invisible tax of manual staging environments that makes every PR take 30 minutes longer than it should.


Step 1: Inventory your current review apps

Before you touch any config, audit what you have.

Run this against your Heroku account (or wherever your review apps are running):

# List all review apps for a pipeline
heroku reviewapps:list --pipeline=my-pipeline

# Check which PRs they're attached to
heroku reviewapps --pipeline=my-pipeline --json | jq '.[] | {pr: .pr_number, status: .status, url: .web_url}'

Make a spreadsheet. For each app, record:

  • The PR number and branch name
  • The app.json or heroku.yml config
  • The env block (environment variables)
  • Any addons provisioned (Postgres, Redis, etc.)
  • The scripts.postdeploy hook (your database migration script)

You'll need every piece of this data to replicate the environment in your new setup.


Step 2: Extract and normalize environment variables

Heroku stores review app config in two places: the pipeline-level config vars (which cascade to review apps) and the app.json env block (which overrides per-app). This split is the #1 cause of "works on staging, fails on review app" bugs.

Create a single .env.preview file that captures everything:

# .env.preview
DATABASE_URL=postgresql://preview:preview@localhost:5432/previewdrop
REDIS_URL=redis://localhost:6379/0
NEXT_PUBLIC_API_URL=${PREVIEW_URL}/api
SESSION_SECRET=preview-secret-do-not-use-in-production
AWS_ACCESS_KEY_ID={{AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY={{AWS_SECRET_ACCESS_KEY}}
AWS_REGION=us-east-1

The ${PREVIEW_URL} token is replaced at deploy time with the actual preview URL. No more hardcoding https://pr-42.herokuapp.com in your app.

Also extract your Heroku addon configs. For each addon, note what config vars it injects:

heroku addons --app=my-review-app-42
heroku config --app=my-review-app-42

Step 3: Write a database migration hook

Heroku runs scripts.postdeploy from app.json after every review app build. Your new platform needs the same concept.

Instead of app.json, create a standalone bin/setup-preview script:

#!/usr/bin/env bash
# bin/setup-preview — runs on every preview deploy
set -euo pipefail

echo "→ Running database migrations…"
npx prisma migrate deploy

echo "→ Seeding preview data…"
npx prisma db seed

echo "→ Running smoke test…"
curl -sf "$PREVIEW_URL/api/health" || (echo "Health check failed!" && exit 1)

echo "✓ Preview is ready: $PREVIEW_URL"

Test it locally against a throwaway database to make sure it's idempotent (running it twice on the same DB should not fail):

# Create a test DB
createdb preview_test
DATABASE_URL=postgresql://localhost:5432/preview_test bash bin/setup-preview

# Run it again — should be a no-op
DATABASE_URL=postgresql://localhost:5432/preview_test bash bin/setup-preview
echo "Exit code: $?"  # Should be 0

Step 4: Configure the build process

Heroku uses buildpacks — auto-detecting your language and running a standard build pipeline. Your new preview platform should do the same, but you want control over what happens.

If your app has a Dockerfile, you're already 90% done. If not, create one now:

# Dockerfile — works for PreviewDrop and any other Docker-based preview platform
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production=false

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
COPY bin/setup-preview /app/bin/setup-preview

EXPOSE 3000
CMD ["node", "server.js"]

This Dockerfile works with Next.js standalone output. For other frameworks, adjust the builder stage and CMD accordingly:

# For Django (Python)
FROM python:3.12-slim AS runner
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "myapp.wsgi:application", "--bind", "0.0.0.0:8000"]

Step 5: Set up the new preview platform

Now you replace Heroku's pipeline with a modern alternative. There are a few approaches:

Option A: DIY with GitHub Actions + Docker. Status: you write a 200-line YAML file that builds your Docker image, pushes it to a registry, provisions a cloud VM, runs your container, wires up DNS, and tears it down 24 hours after the PR closes. It works. It also requires maintaining that 200-line YAML forever, debugging Docker networking issues at 2 AM, and explaining to your team lead why preview builds took 18 minutes last Tuesday.

Option B: Use a dedicated preview environment platform. PreviewDrop connects to your GitHub repo, reads your Dockerfile (or auto-detects your framework), builds a container, gives every branch a live HTTPS URL, and tears it down when the PR merges — with zero YAML required. Setup takes about 90 seconds.

For the rest of this guide, we'll assume Option B. Here's the migration checklist:

# 1. Connect your GitHub repo
#    Done via PreviewDrop dashboard — no CLI required.

# 2. Add your environment variables
#    Paste the contents of .env.preview into the preview env vars section.

# 3. Set the build command (if needed)
#    PreviewDrop auto-detects Next.js, Rails, Django, FastAPI, Laravel, and others.
#    For custom setups, provide: npm run build && npm run start

# 4. Set the post-deploy hook
#    Point it at: bash bin/setup-preview

# 5. Open a test PR
#    Verify the preview URL is live, the database migration ran, and the health check passes.

That's it. Five steps. Your team's next PR gets a live URL automatically.


Step 6: Deprecate Heroku Review Apps

You can run both systems in parallel during migration. Update your GitHub branch protection rules to check both the Heroku review app and the new preview environment:

# .github/branch-protection.yml (conceptual)
required_status_checks:
  - "previewdrop/deploy"  # New
  # - "heroku/review-app" # Remove after 2 weeks

Once the new system has handled 5-10 PRs without issues, disconnect the Heroku pipeline:

heroku pipelines:remove my-pipeline
# Breathe deep. You're free.

What the migration actually looks like (real timeline)

Here's how this went for a Rails + React monorepo team I worked with:

| Time | Step | Result | |------|------|--------| | 09:15 | Audit existing review apps | 7 active, 3 orphaned, $340/month in dyno costs | | 09:45 | Extract env vars to .env.preview | Found 4 missing vars that were causing "works on staging" bugs | | 10:10 | Create bin/setup-preview and Dockerfile | 15 lines of bash, a compact Dockerfile | | 10:20 | Connect repo to PreviewDrop | 90 seconds — clicked "Connect GitHub" → selected repo → done | | 10:25 | Push test PR | Preview URL live in 4 minutes | | 10:35 | Migrate the other 3 PRs | Each got a new URL automatically | | 11:00 | Disconnect Heroku pipeline | $340/month saved |

Total: under 2 hours. The team spent more time debating lunch than they spent on the migration.


One thing to watch: the database reset pattern

On Heroku, review apps get a fresh clone of your staging database. This is convenient but dangerous — it means production credentials or PII in your staging DB ends up in review apps. Teams that handle regulated data (healthcare, finance) can't use this approach.

A better pattern: your bin/setup-preview script runs migrations from scratch, then seeds with anonymous test data:

#!/usr/bin/env bash
# bin/setup-preview — production-safe seeding
set -euo pipefail

npx prisma migrate deploy

# Seed with synthetic data only — never reference production records
cat <<SQL | psql "$DATABASE_URL"
INSERT INTO users (email, name) VALUES
  ('alice@example.com', 'Alice Tester'),
  ('bob@example.com', 'Bob Reviewer'),
  ('carol@example.com', 'Carol QA');
INSERT INTO projects (name, owner_email) VALUES
  ('Demo Project', 'alice@example.com'),
  ('Test Repo', 'bob@example.com');
SQL

echo "✓ Synthetic seed data loaded"

This approach is faster (no large database copy), cheaper (no duplicate storage), and safe for regulated environments.


What you gain post-migration

Beyond the obvious (working previews), teams report:

  • Review time drops by 40-60% because reviewers can see the change live instead of pulling the branch locally.
  • QA catches styling bugs earlier — your designer sees the actual rendered page, not a screenshot from someone's local machine.
  • Cross-branch testing becomes possible — open two PRs, get two URLs, test them side by side.
  • Onboarding speed improves — new engineers don't need to set up a 12-service local stack just to change a button label.

If you're still on Heroku and everything works

Keep it. Heroku Review Apps on paid dynos still function. If your team is happy and the cost is manageable, there's no urgency.

But if you've noticed any of these symptoms:

  • Review apps take longer to build than they did in 2023
  • Your team has stopped using review apps entirely and just deploys to a shared staging server
  • You're paying for dynos that sit idle 23 hours a day
  • Your app.json has become a 400-line beast that nobody fully understands

...then the migration outlined above will pay for itself within the first sprint.


Ready to set up branch previews that actually work? Start a free trial → No credit card, 2 concurrent previews, works with any Docker-based stack.

Ready to give every branch a live URL?

Free tier — 2 concurrent previews, no credit card required.

Start free