Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for GitLab CI/CD By James Joyner IV · · 8 min read

GitLab Review Apps: Ship a Live Preview for Every Merge Request

Reviewing code in a diff is hard; reviewing a running app is easy. Here's how I set up GitLab Review Apps so every MR gets an ephemeral environment that cleans itself up.

  • #gitlab
  • #cicd
  • #review-apps
  • #kubernetes
  • #environments
  • #preview

The best code review I ever gave wasn’t a comment on a diff. It was clicking a link, using the feature, and immediately seeing it was broken on mobile — something no amount of reading the patch would have caught.

That’s the whole pitch for Review Apps: every merge request gets its own live, deployed copy of the application. Reviewers, designers, and product folks click a link and use the change instead of imagining it. After years of doing reviews both ways, I won’t go back. Here’s how I set them up so they’re genuinely useful and don’t leak infrastructure everywhere.

What a Review App actually is

A Review App is an ephemeral environment, spun up per merge request, torn down when the MR merges or closes. In GitLab it’s just an environment with a dynamic name and an on_stop job. The magic is in two keywords working together.

The minimal working setup

Here’s the core of a Review App job deploying to Kubernetes:

deploy-review:
  stage: deploy
  script:
    - helm upgrade --install review-$CI_MERGE_REQUEST_IID ./chart
      --namespace review
      --set image.tag=$CI_COMMIT_SHA
      --set ingress.host=mr-$CI_MERGE_REQUEST_IID.review.example.com
  environment:
    name: review/$CI_MERGE_REQUEST_IID
    url: https://mr-$CI_MERGE_REQUEST_IID.review.example.com
    on_stop: stop-review
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

The environment.url is what shows up as a clickable “View app” button right in the merge request. The name: review/$CI_MERGE_REQUEST_IID groups all review environments together in the UI.

Always pair it with a stop job

This is the part people forget, and it’s why so many teams end up with hundreds of zombie environments eating cluster capacity. The on_stop job tears everything down:

stop-review:
  stage: deploy
  script:
    - helm uninstall review-$CI_MERGE_REQUEST_IID --namespace review
  environment:
    name: review/$CI_MERGE_REQUEST_IID
    action: stop
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      when: manual
  variables:
    GIT_STRATEGY: none

When the MR merges or closes, GitLab runs this automatically. GIT_STRATEGY: none lets it run even after the branch is gone.

Add auto-stop so nothing lingers

Even with on_stop, a stale open MR can hold an environment for weeks. Add a TTL so abandoned previews clean themselves up:

environment:
  name: review/$CI_MERGE_REQUEST_IID
  auto_stop_in: 3 days

Three days of inactivity and GitLab stops the environment for you. This single line has saved me more cluster headroom than any other optimization.

Make them cheap or they won’t get used

A Review App that takes ten minutes to deploy and costs real money per MR will quietly get disabled. Keep them lightweight:

  • Share a namespace, isolate by release name and ingress host.
  • Use small resource requests — these are previews, not production.
  • Stub or share heavy dependencies. Don’t spin up a dedicated database cluster per MR; point at a shared sandbox or seed a lightweight one.
  • Cache aggressively so the deploy is fast.

The goal is a preview that’s ready before the reviewer has finished reading the description.

Seed realistic data

An empty app is a bad preview. Bake a small data-seeding step into the deploy so reviewers land on something that looks real:

- kubectl exec deploy/review-$CI_MERGE_REQUEST_IID -- ./seed-demo-data.sh

The difference between “here’s a blank login page” and “here’s the feature working with sample data” is the difference between a Review App people use and one they ignore.

Post the link where people will see it

The “View app” button is good, but a comment on the MR with the URL and what changed is better. You can use GitLab’s API from a job to drop a comment so the link is impossible to miss. Reviewers shouldn’t have to hunt for the preview.

Where AI helps

Setting up the dynamic environment plumbing — naming, ingress, cleanup — has a lot of fiddly moving parts that fail silently. I paste the deploy-review and stop-review jobs into a model and ask: “Will the stop job actually fire on every MR close, and is anything here going to leak a namespace or DNS record?” It’s caught missing on_stop wiring and ingress collisions for me. I keep GitLab CI prompts for this, and run review-app changes through our Code Review tool before merge.

Why this changes reviews

When reviewers can click and use the change, review quality jumps. Bugs that hide in a diff — broken layouts, bad empty states, slow pages, confusing flows — become obvious in seconds. Product and design can weigh in before merge instead of after. And because the environments clean themselves up, you get all of that without drowning in zombie infrastructure.

Set them up once, make them cheap and self-cleaning, and every merge request becomes a live demo. It’s one of the highest-leverage things you can add to a GitLab pipeline.

AI suggestions for ephemeral environment setup are assistive, not authoritative. Always confirm stop jobs and auto-stop behavior actually tear down resources in a real run.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.