Environments & Deployments

Learn how GitLab tracks deployments across environments, how to configure review apps for merge requests, and how to roll back to a previous deployment when things go wrong.

Environments Deployments GitLab CI/CD
Why Environments Matter

A pipeline that deploys code is useful. A pipeline that tracks what it deployed, where, and when is transformative. GitLab environments turn deployments from fire-and-forget scripts into first-class objects with history, status, and rollback capabilities. Every time a job deploys to an environment, GitLab records the deployment: which commit, which job, which user, and when. This record becomes the foundation for auditing, debugging, and incident response.

Environments also unlock review apps — temporary environments created for each merge request, allowing reviewers to see changes running live before merging. And they enable environment-specific variables and protected environments, so production secrets are only available to authorized jobs on protected branches. When a deployment fails, the environment page shows exactly what's running and lets you redeploy or roll back to a previous version with a single click .

The keyword that ties this together is environment. Adding it to a deploy job tells GitLab that this job deploys to a named environment. From there, GitLab builds the deployment history, the environment dashboard, and the rollback interface automatically. Without it, you have deployments; with it, you have deployment management.

Core Principle: Every deploy job should have an environment block. It costs three lines of YAML and unlocks tracking, rollbacks, and environment-scoped variables.

Environment

A named deployment target (staging, production, review/app-123). Tracks deployment history and current state.

Deployment

A single record of a job deploying a commit to an environment. Includes commit, job, user, and timestamp.

Review App

A temporary environment created per merge request. Deleted automatically when the MR is merged or closed.

Rollback

Redeploy a previous commit to an environment. One click from the environment page, no pipeline needed.
Configuring Environments

The environment keyword is added to a job to mark it as a deployment. At its simplest, it takes a name: environment: staging. That single line tells GitLab to record the deployment, show it in the environment dashboard, and enable rollback. But the keyword supports much more: a URL that appears as a button in merge requests, a Kubernetes namespace for cluster integration, and an action that can be start, stop, or prepare .

The action: stop is particularly useful for review apps. When a merge request is merged or closed, GitLab runs the job marked with action: stop, which tears down the review environment. This keeps your infrastructure clean without manual cleanup. The on_stop keyword links the stop job to the deploy job, so GitLab knows which job to run when the environment needs to be removed .

Environment names are dynamic: you can use variables like $CI_COMMIT_REF_SLUG to create per-branch environments. This is the foundation of review apps — every branch gets its own environment name, its own URL, and its own deployment history, all from a single job definition.

# Basic environment deploy_staging: stage: deploy script: - ./deploy.sh staging environment: staging # Environment with URL (shown in MR) deploy_staging: stage: deploy script: - ./deploy.sh staging environment: name: staging url: https://staging.example.com # Dynamic environment (per branch) deploy_review: stage: deploy script: - ./deploy.sh review-$CI_COMMIT_REF_SLUG environment: name: review/$CI_COMMIT_REF_SLUG url: https://$CI_COMMIT_REF_SLUG.review.example.com # Review app with automatic stop deploy_review: stage: deploy script: - ./deploy.sh review-$CI_COMMIT_REF_SLUG environment: name: review/$CI_COMMIT_REF_SLUG url: https://$CI_COMMIT_REF_SLUG.review.example.com on_stop: stop_review stop_review: stage: deploy script: - ./teardown.sh review-$CI_COMMIT_REF_SLUG environment: name: review/$CI_COMMIT_REF_SLUG action: stop rules: - if: $CI_MERGE_REQUEST_ID when: manual allow_failure: true # Kubernetes environment (auto-linked to cluster) deploy_k8s: stage: deploy script: - kubectl apply -f k8s/ environment: name: production kubernetes: namespace: production agent: my-k8s-agent # Environment with auto-stop timer deploy_demo: stage: deploy script: - ./deploy.sh demo environment: name: demo url: https://demo.example.com auto_stop_in: 1 week # Environment actions: # start (default): Deploy to environment # stop: Stop/teardown environment # prepare: Prepare environment (no deployment) # verify: Verify environment # Environment tiers (for dashboard grouping): # production, staging, testing, development, other environment: name: production deployment_tier: production
Environment Best Practices:
  • Always add an environment block to deploy jobs — even simple ones
  • Include a url so reviewers can click through from merge requests
  • Use action: stop with on_stop for review apps
  • Use deployment_tier to group environments in the dashboard
  • Set auto_stop_in for temporary environments to avoid orphaned infrastructure
Review Apps

Review apps are one of the most valuable features of GitLab environments. A review app is a temporary deployment created for a specific merge request, running the code from that MR's branch. Reviewers can click a link in the merge request and see the changes live — no need to check out the branch, install dependencies, and run it locally. Product managers can review UI changes without a developer's help. QA can test in a production-like environment before merge .

The mechanism is simple: define a deploy job with a dynamic environment name based on the branch or MR ID, and GitLab creates a new environment for each MR. When the MR is merged or closed, a corresponding stop job tears down the environment. The on_stop keyword links the two, so GitLab automatically runs the stop job when the MR is closed — no manual intervention needed.

Review apps typically require dynamic URLs, which often means wildcard DNS (e.g., *.review.example.com) and dynamic resource naming (Kubernetes namespaces, Docker container names, etc.). The specific implementation depends on your infrastructure, but the GitLab configuration is the same regardless: a deploy job with a dynamic environment name, and a stop job with action: stop.

# Review app deployment deploy_review: stage: deploy image: alpine/helm:latest script: - helm upgrade --install review-$CI_COMMIT_REF_SLUG ./chart --namespace review-$CI_COMMIT_REF_SLUG --create-namespace --set image.tag=$CI_COMMIT_SHORT_SHA --set ingress.host=$CI_COMMIT_REF_SLUG.review.example.com environment: name: review/$CI_COMMIT_REF_SLUG url: https://$CI_COMMIT_REF_SLUG.review.example.com on_stop: stop_review rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" # Stop review app stop_review: stage: deploy image: alpine/helm:latest script: - helm uninstall review-$CI_COMMIT_REF_SLUG --namespace review-$CI_COMMIT_REF_SLUG - kubectl delete namespace review-$CI_COMMIT_REF_SLUG environment: name: review/$CI_COMMIT_REF_SLUG action: stop rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual allow_failure: true # Review app with Docker Compose deploy_review_docker: stage: deploy script: - docker-compose -p review-$CI_COMMIT_REF_SLUG up -d environment: name: review/$CI_COMMIT_REF_SLUG url: http://$CI_COMMIT_REF_SLUG.review.example.com:8080 on_stop: stop_review_docker rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" stop_review_docker: stage: deploy script: - docker-compose -p review-$CI_COMMIT_REF_SLUG down -v environment: name: review/$CI_COMMIT_REF_SLUG action: stop rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual allow_failure: true # Review app with auto-stop (even if MR stays open) deploy_review: stage: deploy script: - ./deploy.sh review-$CI_COMMIT_REF_SLUG environment: name: review/$CI_COMMIT_REF_SLUG url: https://$CI_COMMIT_REF_SLUG.review.example.com on_stop: stop_review auto_stop_in: 3 days rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" # Variables available for review apps: # $CI_COMMIT_REF_SLUG - Branch name, slugified # $CI_MERGE_REQUEST_IID - MR number # $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME - Source branch # $CI_MERGE_REQUEST_TARGET_BRANCH_NAME - Target branch
Review App Requirements: