look@me

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.

  1. See https://docs.gitlab.com/ci/environments/#create-a-dynamic-environment (2026-02-24)

  2. If you call envsubst without specifying variable names (i.e., just envsubst), 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').

#cicd #devops #gitlab