This article covers the environment
keyword in GitLab CI/CD, explaining its role in defining and managing deployment environments within your pipelines. We will explore how it links jobs to specific environments, tracks deployments, and provides access to environment-specific variables, enhancing visibility and control over your continuous delivery process.
What is an Environment in GitLab CI/CD?
An environment in GitLab CI/CD represents a specific deployment target for your application. It is a logical name you assign to a deployment, helping you organize and see where your code is running.
Common Environment Examples:
development
staging
production
review/feature-branch-xyz
(for dynamic review apps)qa-testing
By defining environments, GitLab gives you:
- Visibility: A dedicated “Environments” page in your project (
Deployments > Environments
) showing all active deployments. - Tracking: A history of deployments to each environment, including who deployed what and when.
- Control: The ability to stop environments, browse deployed applications, and perform easy rollbacks.
- Integration: Direct links to the deployed application’s URL.
Defining Environments in .gitlab-ci.yml
You define an environment for a specific job using the environment
keyword within your .gitlab-ci.yml
file.
Basic Syntax:
deploy_to_staging:
stage: deploy
script:
- echo "Deploying to staging..."
- deploy_script --env=staging
environment: staging # Defines this job as deploying to the 'staging' environment
This simple definition is enough for GitLab to recognize “staging” as an environment and start tracking deployments to it.
Key Attributes of the environment
Keyword
The environment
keyword can take several attributes, providing richer functionality:
environment:name
Purpose: This is the mandatory name of the environment. GitLab uses this to identify and group deployments. It is the primary way to refer to your deployment target.
Syntax: environment: <environment_name>
Example:
deploy_to_prod:
stage: deploy
script: deploy_to_production.sh
environment: production # The name 'production'
environment:url
Purpose: Provides a direct link from the GitLab UI to the running application in that environment. This is invaluable for quick access and testing.
Syntax: environment: url: <url_string>
Example:
deploy_review_app:
stage: deploy
script:
- deploy_review_app.sh "$CI_COMMIT_REF_SLUG" # Script deploys and gets URL
environment:
name: review/$CI_COMMIT_REF_SLUG # Dynamic environment name based on branch
url: https://$CI_COMMIT_REF_SLUG.my-review-app.com # Dynamic URL for the review app
Explanation: $CI_COMMIT_REF_SLUG
is a predefined variable that provides a “slug” version of the branch name (e.g., feature/my-new-feature
becomes feature-my-new-feature
), perfect for creating dynamic URLs.
environment:on_stop
Purpose: Specifies a job that GitLab should run when this environment is manually stopped from the UI or automatically via auto_stop_in
. This is essential for cleaning up dynamic environments (like review apps) to save resources.
Syntax:
environment:
name: <name>
on_stop: <stop_job_name> # Links to a separate job definition
Example:
deploy_review_app:
stage: deploy
script: deploy_review_app.sh "$CI_COMMIT_REF_SLUG"
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.my-review-app.com
on_stop: stop_review_app # Link to the stop job
stop_review_app:
stage: cleanup # A dedicated cleanup stage (could be hidden until needed)
script:
- delete_review_app.sh "$CI_COMMIT_REF_SLUG"
when: manual # This job is manually triggered by the 'Stop' button
rules:
# This rule ensures the job only appears in the pipeline when its 'on_stop' action is triggered.
# It also makes it so the job doesn't run automatically in regular pipelines.
- if: '$CI_ENVIRONMENT_ACTION == "stop"'
when: on_success
allow_failure: true # Cleanup should ideally not fail the main pipeline
Explanation: The stop_review_app
job will be associated with the review/$CI_COMMIT_REF_SLUG
environment. When you go to Deployments > Environments
and click “Stop” on that specific review app, GitLab will execute the stop_review_app
job. The rules
with $CI_ENVIRONMENT_ACTION == "stop"
are critical here to ensure the job runs only when explicitly triggered by the stop action.
environment:action
Purpose: The action
keyword is defined within the environment
block of a job to specify how that job interacts with the environment. It tells GitLab the role of the job regarding the environment’s lifecycle. It is important for granular control over environment states and for integrating with GitLab’s deployment tracking features.
Keyword Type: Job keyword, meaning it is used only as part of a job definition.
Supported Values:
start (Default):
Meaning: This is the default action. It indicates that the job starts or creates the environment. A new deployment record is typically created after this job starts.
Use Case: Your primary deployment jobs (e.g., deploy_to_staging
, deploy_production
).
Example:
deploy_app_to_dev:
stage: deploy
script: deploy_dev_script.sh
environment:
name: development
url: https://dev.my-app.com
action: start # This is the default, often omitted
prepare:
Meaning: Indicates that the job is only preparing the environment. It does not trigger a deployment record in GitLab.
Use Case: Jobs that set up infrastructure prerequisites before the actual deployment job, but don’t deploy the application itself (e.g., provisioning a database, setting up a VPC).
Example:
provision_db:
stage: infra
script: provision_db_script.sh
environment:
name: staging
action: prepare # This job prepares 'staging' but doesn't show as a deployment
stop:
Meaning: Indicates that the job stops or tears down an environment. This action is critical when paired with on_stop
or auto_stop_in
to clean up resources. When an on_stop
job runs, the $CI_ENVIRONMENT_ACTION
predefined variable is set to "stop"
.
Use Case: Jobs that delete review apps, de-provision temporary testing environments.
Example:
stop_review_app:
stage: cleanup
variables:
GIT_STRATEGY: none # Often no need to clone repo for cleanup
script: make delete-app "$CI_COMMIT_REF_SLUG"
when: manual
environment:
name: review/$CI_COMMIT_REF_SLUG # Refers to the environment name being stopped
action: stop # Explicitly marks this job as stopping the environment
rules:
# This rule ensures the job only appears in the pipeline when its 'on_stop' action is triggered.
- if: '$CI_ENVIRONMENT_ACTION == "stop"'
when: on_success
verify:
Meaning: Indicates that the job is only verifying the environment. It does not trigger a deployment record.
Use Case: Post-deployment smoke tests, health checks, or integration tests that run against a deployed environment without being part of the deployment itself.
Example:
smoke_test_staging:
stage: verify
script: run_smoke_tests.sh --env=staging
environment:
name: staging
action: verify # This job verifies 'staging' but isn't a deployment
access:
Meaning: Indicates that the job is only accessing the environment. It does not trigger a deployment record.
Use Case: Jobs that gather information about an environment (e.g., getting current status, listing resources) or interact with it for administrative purposes, but don’t modify its state.
Example:
get_prod_status:
stage: ops
script: get_production_status.sh
environment:
name: production
action: access # This job accesses 'production' without deploying
environment:auto_stop_in
Purpose: Automatically stops a dynamic environment after a specified duration. This requires an on_stop
job to be defined. This is great for managing temporary testing environments to prevent resource waste.
Syntax:
environment:
name: <name>
auto_stop_in: <duration> # e.g., '1 week', '2 days', '3 hours', '90 minutes'
on_stop: <stop_job_name>
Example:
deploy_temp_test_env:
stage: deploy
script: deploy_temp_env.sh
environment:
name: temporary-test-$CI_COMMIT_SHORT_SHA
url: https://temp-$CI_COMMIT_SHORT_SHA.my-app.com
on_stop: cleanup_temp_env
auto_stop_in: 4 hours # This environment will automatically be stopped after 4 hours
cleanup_temp_env:
stage: cleanup
script: delete_temp_env.sh
when: manual # Will be automatically triggered by auto_stop_in
rules:
- if: '$CI_ENVIRONMENT_ACTION == "stop"'
when: on_success
Explanation: The environment temporary-test-...
will be automatically marked for stopping after 4 hours. Once that time passes, the cleanup_temp_env
job will be automatically triggered.
environment:kubernetes
Purpose: Integrates with a Kubernetes cluster configured in GitLab. When this is set, GitLab automatically provides a Kubeconfig file to the job, allowing it to interact with the specified Kubernetes cluster context. This is particularly useful for deployments to Kubernetes.
Syntax:
environment:
name: <name>
kubernetes:
namespace: <kubernetes_namespace> # Optional: default is project's
# Optional: You can specify an actual Kubernetes service account name if needed
# service_account_name: my-app-service-account
Example:
deploy_to_k8s_staging:
stage: deploy
image: alpine/helm:3.8.1 # Or any image with kubectl/helm
script:
- helm upgrade --install my-app ./charts --namespace "$CI_ENVIRONMENT_SLUG"
environment:
name: staging
kubernetes:
namespace: myapp-staging # Deploy to this specific namespace
url: https://staging.my-k8s-app.com
Explanation: The deploy_to_k8s_staging
job will be configured to use the Kubernetes cluster integrated with your GitLab project. The namespace
attribute directs the deployment to a specific Kubernetes namespace. The job automatically receives credentials to interact with that cluster.
environment:deployment_tier
Purpose: Categorizes your environments into predefined tiers (e.g., production
, staging
, development
, testing
, other
). This helps GitLab understand the criticality and role of an environment, which can influence how it’s displayed, protected, or integrated with other features.
Syntax:
environment:
name: <name>
deployment_tier: <tier_name> # e.g., production, staging, development, testing, other
Example:
deploy_to_prod:
stage: deploy
script: deploy_production.sh
environment:
name: production
url: https://my-app.com
deployment_tier: production # Mark this environment as production tier
deploy_to_dev:
stage: deploy
script: deploy_dev.sh
environment:
name: development
url: https://dev.my-app.com
deployment_tier: development # Mark this as a development tier
Explanation: GitLab’s UI might visually distinguish environments based on their tier. More critically, features like Protected Environments (which prevent unauthorized deployments) specifically leverage the production
tier by default.
Dynamic Environments
Dynamic environments are environments whose names (and often URLs) are generated at runtime, usually based on branch names or merge request IDs. They are typically short-lived and are crucial for creating “review apps” or temporary testing instances for every feature branch or merge request.
- How they work: You use predefined CI/CD variables like
$CI_COMMIT_REF_SLUG
in thename
andurl
attributes of your environment definition. - Cleanup: To prevent sprawl and cost, dynamic environments are almost always paired with an
on_stop
job and oftenauto_stop_in
to ensure they’re automatically cleaned up when no longer needed. - Example: (See
deploy_review_app
andauto_stop_in
examples above for dynamic environment usage).
The Environments Page (Deployments > Environments)
This dedicated section in GitLab is where the power of environments truly comes to life:
- Current Deployments: See all active environments, their last deployed commit, and the associated job.
- History: View a full history of deployments to each environment.
- Browse Live: Click the URL link to directly access your deployed application.
- Rollback: Easily re-deploy a previous successful deployment to an environment. This triggers a new pipeline for the historical commit.
- Stop Environment: Manually trigger the
on_stop
job for cleanup.
Environment-Scoped Variables
Beyond the environment
keyword, GitLab also lets you define CI/CD variables (in Project/Group settings) that are scoped to specific environments. This is extremely useful for managing secrets or configuration that differs between environments (e.g., database URLs, API keys).
- You could set a variable
DB_HOST
todb.staging.example.com
for thestaging
environment anddb.prod.example.com
for theproduction
environment. - These variables are only injected into jobs that declare they are deploying to that specific environment (using
environment: <name>
).
Best Practices for Using Environments
- Name Consistently: Use clear and consistent naming conventions for your environments (e.g.,
dev
,staging
,production
,review/MR-ID
). - Leverage Dynamic Environments: For feature branches or merge requests, use dynamic environment names (e.g.,
review/$CI_COMMIT_REF_SLUG
) to provide isolated testing environments for each change. - Define
on_stop
for Dynamic Environments: Always pair dynamic environments with anon_stop
job. This ensures resources are cleaned up efficiently, preventing “environment sprawl” and unnecessary cloud costs. - Automate
url
s: Whenever possible, programmatically generate and set theurl
for your environments so they are directly clickable from GitLab. - Secure Production Deployments:
- Use
when: manual
for production deployment jobs. - Protect the production environment in GitLab settings (
Project > Settings > CI/CD > Environments > Protected Environments
) to restrict who can trigger deployments. - Use protected variables for production credentials.
- Consider using dedicated runners with specific tags for production deployments.
- Use
- Utilize Environment-Scoped Variables: For any configuration that varies per environment, store it as an environment-scoped CI/CD variable.
- Monitor the Environments Dashboard: Regularly check the
Deployments > Environments
page to get an overview of your active deployments and their status.
FAQs – GitLab CI/CD Environment
What is the environment
keyword in GitLab CI/CD?
The environment
keyword in GitLab CI/CD defines where your code is being deployed (e.g., staging, production). It allows GitLab to track deployments, show them in the Environments dashboard, and optionally enable features like manual rollbacks, deployment history, and auto-stop of review apps.
How do I define an environment in a GitLab CI job?
Use the environment
keyword inside a job to specify the environment’s name:
deploy_staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging
This tells GitLab to track this job as a deployment to staging.
What are the common use cases for the environment
keyword?
- Track which job deployed to which environment.
- Enable manual deployments and rollbacks.
- View deployment logs and environment states in GitLab UI.
- Automatically stop dynamic environments like review apps.
Can I create dynamic environment names with variables?
Yes. You can use GitLab predefined or custom variables to create dynamic names, often used in review apps:
review:
script: ./deploy.sh $CI_COMMIT_REF_NAME
environment:
name: review/$CI_COMMIT_REF_NAME
This creates a separate environment per branch.
How do I define a URL for the environment?
You can include a url:
key to make the environment accessible via the GitLab UI:
environment:
name: production
url: https://prod.example.com
GitLab shows a “View Environment” button in the UI that links to this URL.
How can I stop a dynamic environment when it is no longer needed?
Use the on_stop:
keyword to define a job that stops the environment (e.g., when a branch is deleted or closed):
review:
script: ./deploy-review.sh
environment:
name: review/$CI_COMMIT_REF_NAME
on_stop: stop_review
stop_review:
script: ./remove-review.sh
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
when: manual
This configuration links the stop job to the environment and allows it to be triggered manually or automatically.
What is the action: stop
keyword used for in environments?
The action: stop
is used in conjunction with on_stop:
to mark a job as the environment shutdown job:
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
This job will be responsible for cleaning up or shutting down the environment.
Can I restrict a job to run only in a specific environment?
Indirectly, yes. You can use rules
or only
/except
keywords with environment-related variables like $CI_ENVIRONMENT_NAME
:
deploy:
environment: production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
This ensures that the job only runs when deploying to production.
What happens when I deploy multiple times to the same environment?
GitLab shows only the most recent deployment in the Environments dashboard. Previous deployments are still accessible via the deployment history tab.
How do I view environments and deployments in GitLab?
Go to your project and navigate to:
Operations → Environments
Here, you can view:
- Active environments
- Deployment history
- Associated pipeline jobs
- Environment URLs
Can environments be used with manual jobs?
Yes. You can mark deployment jobs as manual
so they require a user trigger from the UI:
deploy_prod:
stage: deploy
script: ./deploy-prod.sh
when: manual
environment:
name: production
This adds a “Play” button to the job in the GitLab UI.
Can I define multiple environments in the same pipeline?
Yes. You can create multiple deployment jobs targeting different environments:
deploy_staging:
stage: deploy
script: ./deploy.sh staging
environment:
name: staging
deploy_production:
stage: deploy
script: ./deploy.sh production
environment:
name: production
You can control which one runs using rules
, only
, or manual
.
What happens if I delete an environment from the GitLab UI?
Deleting an environment removes:
- The environment from the Environments dashboard.
- Access to deployment history via UI.
Note: This does not remove any infrastructure unless your stop job or cleanup script explicitly handles that.
Can I track deployment time and status using environments?
Yes. GitLab automatically tracks:
- When the deployment started and ended
- Which user or pipeline deployed it
- Whether the deployment succeeded or failed
This information is visible in the Environments and Deployments tabs.
Author

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com