Dynamically Name Environments in GitLab CI
While it is possible in GitLab CI to have dynamic environment URLs, the use of dynamic environment names is limited. It is restricted to environment variables provided by GitLab CI. Using a custom environment variable that has been declared in a previous job or parent pipeline is not supported.
While not explicitly mentioned in the official GitLab documentation1,
dynamic environment names cause pipelines to fail immediately. It seems that GitLab
CI evaluates environment names before any job in a pipeline starts. Creating
clear dependencies with depends or needs does not change this behavior.
job1: # works ✅
environment:
name: $CI_COMMIT_REF_SLUG
url: $DYNAMIC_ENVIRONMENT_URL
job2: # fails ⛔
environment:
name: $DYNAMIC_ENVIRONMENT_NAME
url: $DYNAMIC_ENVIRONMENT_URL
GitLab likely has good reasons for this behavior. Still, there are use cases, such as blue-green deployments, where dynamic environment names are required. We can work around the default behavior by creating a downstream pipeline during runtime. In other words, we create a GitLab CI pipeline file in our pipeline and then run this pipeline.
Instead of defining one or more deployment jobs inside another job, we can
create fully normal GitLab CI files like the following. The only special
line, which is totally valid but will not work natively, is when we set the
environment name to $DYNAMIC_ENVIRONMENT_NAME.
# template.gitlab-ci.yml
stages:
- deploy
deploy_dynamic_environment:
stage: deploy
script: |
# The actual deployment happens here ...
environment:
name: $DYNAMIC_ENVIRONMENT_NAME
url: $DYNAMIC_ENVIRONMENT_URL
Given such a template, we need just one job that loads this template and
replaces the variable $DYNAMIC_ENVIRONMENT_NAME with our actual value. We can
do this with the common Linux command envsubst. It substitutes all or some
given environment variables in a file.2
prepare_dynamic_environment:
stage: deploy
image: alpine:3.23
script: |
apk add --no-cache gettext
export DYNAMIC_ENVIRONMENT_NAME=SetTheEnvironmentNameHere
envsubst '$DYNAMIC_ENVIRONMENT_NAME' \
< template.gitlab-ci.yml \
> dynamic.gitlab-ci.yml
artifacts:
paths:
- dynamic.gitlab-ci.yml
Finally, we run the dynamically created pipeline file as a downstream pipeline.
run_dynamic_environment:
stage: deploy
needs:
- prepare_dynamic_environment
trigger:
include:
- artifact: dynamic.gitlab-ci.yml
job: prepare_dynamic_environment
strategy: depend
The result is a proper workaround that is easy to debug via artifacts. The template is valid YAML and a valid GitLab CI file. Thus, we can rely on all the support from our IDE that we are used to.
See https://docs.gitlab.com/ci/environments/#create-a-dynamic-environment (2026-02-24)↩
If you call
envsubstwithout specifying variable names (i.e., justenvsubst), it will replace all environment variables found in the template. This can unintentionally expose sensitive information, such as secrets set by GitLab CI, in the generated file and any resulting artifacts. To avoid this, always specify the variables you want to substitute (e.g.,envsubst '$DYNAMIC_ENVIRONMENT_NAME').↩