Skip to main content

Variables

Un runner de GitLab recibe diversas variables de entorno que describen el contexto de ejecución del pipeline — como la rama actual, tipo de evento (push, merge request, etc.), usuario responsable, entre otros. Estas variables permiten crear flujos inteligentes y personalizados, siendo especialmente útiles en la definición de rules, condicionales de ejecución y comportamientos específicos para cada escenario del CI/CD.

Vamos ya de entrada a ver qué tenemos de variables de entorno en un runner con lo que tenemos hasta ahora.

En cualquier job coloca un - env. El comando env imprime todas las variables de entorno directamente en el shell. Vamos a seguir en la consola.

.check: # Plantilla para stage check
stage: check
before_script:
- env # Cualquier job que herede esta plantilla imprimirá esto.
- npm ci
artifacts:
when: always
expire_in: "3 months"

Abre un MR solo para ejecutar el pipeline vamos a revisar el log. Algunas variables están enmascaradas porque fueron creadas para ser secretas si seguiste el flujo del estudio.

# Log de uno de los jobs.
Skipping Git submodules setup
Executing "step_script" stage of the job script
Using docker image sha256:461edc13e56b039ebc3d898b858ac3acea00c47f31e93ec1258379cae8990522 for node:22-alpine with digest node@sha256:ad1aedbcc1b0575074a91ac146d6956476c1f9985994810e4ee02efd932a68fd ...
$ env
FF_USE_WINDOWS_LEGACY_PROCESS_STRATEGY=false
FF_SCRIPT_SECTIONS=false
FF_POSIXLY_CORRECT_ESCAPES=false
CI_SERVER_VERSION_PATCH=0
CI_SERVER_REVISION=eb738e3a3cd
CI_COMMIT_SHORT_SHA=0f053f0d
GITLAB_USER_LOGIN=davidpuziol
CI_DEPENDENCY_PROXY_PASSWORD=[MASKED]
CI_DEPENDENCY_PROXY_SERVER=gitlab.com:443
FF_USE_DUMB_INIT_WITH_KUBERNETES_EXECUTOR=false
CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED=true
FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false
FF_LOG_IMAGES_CONFIGURED_FOR_JOB=false
FF_USE_LEGACY_S3_CACHE_ADAPTER=false
CI=true
CI_SERVER_PROTOCOL=https
CI_PROJECT_NAME=learn-gitlab-app
CI_RUNNER_REVISION=0f67ff19
NODE_VERSION=22.15.0
HOSTNAME=runner-jyvyfkmfg-project-69186599-concurrent-1
CI_JOB_STAGE=check
CI_PROJECT_DESCRIPTION=
CI_COMMIT_DESCRIPTION=
YARN_VERSION=1.22.22
CI_DEPENDENCY_PROXY_USER=gitlab-ci-token
CI_SERVER_VERSION=18.0.0-pre
CI_MERGE_REQUEST_SOURCE_PROJECT_ID=69186599
SHLVL=3
FF_DISABLE_POWERSHELL_STDIN=false
FF_CLEAN_UP_FAILED_CACHE_EXTRACT=false
FF_DISABLE_AUTOMATIC_TOKEN_ROTATION=false
CI_PROJECT_ROOT_NAMESPACE=puziol
HOME=/root
OLDPWD=/
FF_NETWORK_PER_BUILD=false
CI_JOB_ID=9882688551
CI_SERVER_HOST=gitlab.com
CI_COMMIT_REF_NAME=pipe/variables
FF_RESOLVE_FULL_TLS_CHAIN=false
CI_PIPELINE_SOURCE=merge_request_event
CI_MERGE_REQUEST_TARGET_BRANCH_SHA=
CI_RUNNER_VERSION=17.11.0
FF_SKIP_NOOP_BUILD_STAGES=true
FF_USE_FASTZIP=false
FF_USE_WINDOWS_JOB_OBJECT=false
CI_BUILDS_DIR=/builds
CI_SERVER_VERSION_MAJOR=18
CI_DEFAULT_BRANCH=main
CI_MERGE_REQUEST_SOURCE_PROJECT_PATH=puziol/learn-gitlab-app
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME=pipe/variables
CI_REGISTRY_PASSWORD=[MASKED]
CI_SERVER_URL=https://gitlab.com
CI_TEMPLATE_REGISTRY_HOST=registry.gitlab.com
CI_COMMIT_REF_PROTECTED=false
CI_MERGE_REQUEST_DIFF_ID=1343611661
GITLAB_FEATURES=ldap_group_sync,multiple_ldap_servers,seat_link,seat_usage_quotas,pipelines_usage_quotas,transfer_usage_quotas,product_analytics_usage_quotas,zoekt_code_search,repository_size_limit,elastic_search,admin_audit_log,auditor_user,custom_file_templates,custom_project_templates,db_load_balancing,default_branch_protection_restriction_in_groups,extended_audit_events,external_authorization_service_api_management,geo,instance_level_scim,ldap_group_sync_filter,object_storage,pages_size_limit,project_aliases,disable_private_profiles,password_complexity,amazon_q,enterprise_templates,git_abuse_rate_limit,integrations_allow_list,required_ci_templates,runner_maintenance_note,runner_performance_insights,runner_upgrade_management,observability_alerts
CI_PROJECT_ID=69186599
CI_REGISTRY_IMAGE=registry.gitlab.com/puziol/learn-gitlab-app
FF_USE_LEGACY_GCS_CACHE_ADAPTER=false
GITLAB_CI=true
CI_COMMIT_SHA=0f053f0db8b19830a962bde2e22f68ea73239f03
CI_MERGE_REQUEST_SOURCE_BRANCH_PROTECTED=false
FF_RETRIEVE_POD_WARNING_EVENTS=true
CI_CONCURRENT_ID=1
FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR=false
FF_USE_DOCKER_AUTOSCALER_DIAL_STDIO=true
CI_REGISTRY_USER=gitlab-ci-token
CI_SERVER_PORT=443
CI_MERGE_REQUEST_SQUASH_ON_MERGE=true
CI_PROJECT_DIR=/builds/puziol/learn-gitlab-app
CI_PROJECT_PATH=puziol/learn-gitlab-app
CI_MERGE_REQUEST_PROJECT_URL=https://gitlab.com/puziol/learn-gitlab-app
FF_ENABLE_JOB_CLEANUP=false
FF_WAIT_FOR_POD_TO_BE_REACHABLE=false
CI_PROJECT_NAMESPACE=puziol
CI_COMMIT_TIMESTAMP=2025-04-30T11:27:02-03:00
FF_USE_DIRECT_DOWNLOAD=true
FF_USE_DYNAMIC_TRACE_FORCE_SEND_INTERVAL=false
CI_JOB_TOKEN=[MASKED]
CI_NODE_TOTAL=1
CI_SERVER_NAME=GitLab
CI_MERGE_REQUEST_DESCRIPTION=
CI_PROJECT_NAMESPACE_ID=106089966
CI_PIPELINE_CREATED_AT=2025-04-30T14:27:45Z
CI_MERGE_REQUEST_SOURCE_BRANCH_SHA=
CI_MERGE_REQUEST_DRAFT=false
CI_CONCURRENT_PROJECT_ID=1
CI_JOB_NAME_SLUG=lint-test
CI_MERGE_REQUEST_IID=6
RUNNER_TEMP_PROJECT_DIR=/builds/puziol/learn-gitlab-app.tmp
FF_KUBERNETES_HONOR_ENTRYPOINT=false
CI_PIPELINE_URL=https://gitlab.com/puziol/learn-gitlab-app/-/pipelines/1794394989
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
FF_EXPORT_HIGH_CARDINALITY_METRICS=false
CI_JOB_STARTED_AT=2025-04-30T14:27:50Z
CI_SERVER_VERSION_MINOR=0
CI_RUNNER_DESCRIPTION=general-debian
FF_USE_NEW_BASH_EVAL_STRATEGY=false
FF_DISABLE_UMASK_FOR_KUBERNETES_EXECUTOR=false
FF_MASK_ALL_DEFAULT_TOKENS=true
CI_PROJECT_TITLE=learn-gitlab-app
CI_PROJECT_VISIBILITY=private
CI_COMMIT_TITLE=prints env
GITLAB_USER_EMAIL=[email protected]
FF_USE_GIT_BUNDLE_URIS=true
FF_USE_NATIVE_STEPS=true
FF_USE_FLEETING_ACQUIRE_HEARTBEATS=false
CI_SERVER=yes
CI_JOB_GROUP_NAME=lint-test
CI_MERGE_REQUEST_EVENT_TYPE=detached
FF_TIMESTAMPS=false
CI_PROJECT_REPOSITORY_LANGUAGES=javascript,css,html
CI_MERGE_REQUEST_ID=380143752
CI_PAGES_URL=https://learn-gitlab-app-414996.gitlab.io
FF_SET_PERMISSIONS_BEFORE_CLEANUP=true
FF_PRINT_POD_EVENTS=false
CI_SERVER_FQDN=gitlab.com
CI_COMMIT_AUTHOR=David Puziol <[email protected]>
CI_JOB_IMAGE=node:22-alpine
CI_RUNNER_SHORT_TOKEN=jyvyfkmfg
CI_PAGES_DOMAIN=gitlab.io
CI_JOB_TIMEOUT=3600
CI_REPOSITORY_URL=https://gitlab-ci-token:[MASKED]@gitlab.com/puziol/learn-gitlab-app.git
CI_PROJECT_CLASSIFICATION_LABEL=
CI_PIPELINE_NAME=
GITLAB_ENV=/builds/puziol/learn-gitlab-app.tmp/gitlab_runner_env
FF_USE_POWERSHELL_PATH_RESOLVER=false
FF_GIT_URLS_WITHOUT_TOKENS=false
CI_API_GRAPHQL_URL=https://gitlab.com/api/graphql
CI_RUNNER_ID=47096042
CI_REGISTRY=registry.gitlab.com
CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX=gitlab.com:443/puziol/dependency_proxy/containers
CI_API_V4_URL=https://gitlab.com/api/v4
CI_MERGE_REQUEST_SOURCE_PROJECT_URL=https://gitlab.com/puziol/learn-gitlab-app
GITLAB_USER_NAME=David Puziol Prata
CI_PIPELINE_IID=33
CI_MERGE_REQUEST_PROJECT_ID=69186599
FF_USE_POD_ACTIVE_DEADLINE_SECONDS=true
FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=false
CI_JOB_URL=https://gitlab.com/puziol/learn-gitlab-app/-/jobs/9882688551
CI_SERVER_SHELL_SSH_HOST=gitlab.com
CI_MERGE_REQUEST_DESCRIPTION_IS_TRUNCATED=false
CI_RUNNER_EXECUTABLE_ARCH=linux/amd64
FF_TEST_FEATURE=false
FF_USE_INIT_WITH_DOCKER_EXECUTOR=false
CI_MERGE_REQUEST_REF_PATH=refs/merge-requests/6/head
CI_COMMIT_REF_SLUG=pipe-variables
CI_DISPOSABLE_ENVIRONMENT=true
PWD=/builds/puziol/learn-gitlab-app
FF_SECRET_RESOLVING_FAILS_IF_MISSING=true
CI_MERGE_REQUEST_PROJECT_PATH=puziol/learn-gitlab-app
CI_RUNNER_TAGS=["general", "debian"]
CI_SERVER_TLS_CA_FILE=/builds/puziol/learn-gitlab-app.tmp/CI_SERVER_TLS_CA_FILE
CI_PIPELINE_ID=1794394989
CI_PROJECT_PATH_SLUG=puziol-learn-gitlab-app
CI_PROJECT_URL=https://gitlab.com/puziol/learn-gitlab-app
CI_CONFIG_PATH=.gitlab-ci.yml
CI_COMMIT_BEFORE_SHA=0000000000000000000000000000000000000000
CI_MERGE_REQUEST_TITLE=Pipe/variables
CI_PROJECT_NAMESPACE_SLUG=puziol
CI_COMMIT_MESSAGE=prints env
FF_ENABLE_BASH_EXIT_CODE_CHECK=false
CI_PAGES_HOSTNAME=learn-gitlab-app-414996.gitlab.io
CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX=gitlab.com:443/puziol/dependency_proxy/containers
GITLAB_USER_ID=4300841
CI_JOB_STATUS=running
CI_JOB_NAME=lint-test
CI_SERVER_SHELL_SSH_PORT=22
CI_MERGE_REQUEST_DIFF_BASE_SHA=093afcb937eeffbc005998803ace71c2887f1c88
CI_MERGE_REQUEST_TARGET_BRANCH_NAME=main
CI_OPEN_MERGE_REQUESTS=puziol/learn-gitlab-app!6

Claro que ahí arriba hay variables que son de GitLab y otras que no lo son. Si observas tenemos variables del runner, de node, y del propio alpine.

Aquí está la documentación de las variables predefinidas de CI/CD.

Variables x Contexto

  • pre-pipeline: Antes de que inicie cualquier job. Solo GitLab "lo sabe". ej: CI_PIPELINE_SOURCE, CI_COMMIT_BRANCH.
    • Variables que GitLab evalúa antes de ejecutar el .gitlab-ci.yml.
    • Útiles en rules, only, except (Veremos más adelante)
  • pipeline: Durante toda la ejecución del pipeline. ej: CI_PIPELINE_ID, CI_PROJECT_NAME
    • Viven durante todo el pipeline. Están disponibles para todos los jobs y plantillas.
  • job-only: Solo dentro de un job. Fuera de él, no existe. ej: CI_JOB_NAME, CI_JOB_ID, CI_RUNNER_ID
    • Solo existen cuando el job ya está ejecutándose. Si intentas acceder en una rule o condicional de pipeline, fallará. Ejemplo: CI_RUNNER_DESCRIPTION.

El runner no recibe todas las variables de todos los contextos.

  • pre-pipeline: Estas variables se resuelven antes de que el pipeline inicie. Útiles en rules, workflow, etc, pero no van al runner.
  • pipeline y job-only: Disponibles en la ejecución de los jobs y visibles para el runner.

Aunque estas sean bien conocidas, voy a marcar aquellas que necesitan estar en la cabeza.

VariableContextoDescripción
CI_PIPELINE_IDJob-onlyID único del pipeline en la instancia de GitLab.
CI_PIPELINE_IIDPipelineÚnico solo dentro del proyecto
CI_COMMIT_SHAPre-pipelineSHA completo del commit que disparó el pipeline.
CI_COMMIT_REF_NAMEPre-pipelineNombre de la rama o tag del commit.
CI_COMMIT_BRANCHPre-pipelineNombre de la rama, si es un pipeline de rama.
CI_COMMIT_TAGPre-pipelineNombre del tag, si es un pipeline de tag.
CI_PROJECT_PATHPre-pipelineRuta completa del proyecto (namespace/proyecto).
CI_PROJECT_DIRJob-onlyDirectorio donde el código fue clonado en el runner. No siempre los runners clonan los proyectos en el mismo lugar.
CI_JOB_NAMEPipelineNombre del job actual. Generalmente usado para log y notificaciones
CI_JOB_STAGEPipelineEtapa del job en el pipeline (build, test, deploy, etc.). Generalmente usado para log y notificaciones
CI_JOB_STATUSJob-onlyEstado actual del job (éxito, fallo, cancelado). Generalmente usado para log y notificaciones
CI_JOB_TOKENJob-onlyToken usado para autenticación en APIs y artefactos. Este token tiene limitaciones.
CI_PIPELINE_SOURCEPre-pipelineOrigen del pipeline (push, merge_request_event, trigger, etc). Esta es una de las más importantes
CI_COMMIT_MESSAGEPre-pipelineMensaje completo del commit. Es bueno para validar si el mensaje del commit está en los estándares de commitizen por ejemplo
CI_REGISTRY_IMAGEPre-pipelineURL de la imagen Docker en el registry del proyecto. Útil si se va a subir una imagen al propio image registry de GitLab.
CI_ENVIRONMENT_NAMEPipelineNombre del entorno (staging, prod, etc.), si está configurado en el job.
CI_ENVIRONMENT_URLPipelineURL del entorno, si está definida.
CI_COMMIT_AUTHORPre-pipelineNombre y email del autor del commit. Bueno para auditoría y disparar notificación.
CI_PIPELINE_URLJob-onlyURL para detalles del pipeline en GitLab.
GITLAB_USER_LOGINPipelineNombre de usuario de quien disparó el pipeline (si aplica). No siempre es igual a quien hizo el commit. Generalmente quien dispara el pipeline suele hacer el commit si es automático, pero no siempre.
CI_COMMIT_TIMESTAMPPre-pipelinees una variable pre-pipeline que trae la fecha y hora exacta del commit, en formato ISO 8601 (ej: 2025-04-30T12:45:32+00:00. Bueno para incluir en nombres, changelogs, auditoría.
TRIGGER_PAYLOADPipelinecontiene el contenido bruto del webhook que disparó el pipeline — en JSON. Solo aparece cuando el pipeline se inicia vía disparador externo (como un webhook de GitHub, Jira, otro GitLab, etc). Muy utilizada, inclusive cuando utilizamos gitlab-ci como backend!

Cuando ocurre un merge request tenemos algunas variables específicas que también son importantes.

Algunas variables aparecerán como _SLUG. es prácticamente lo mismo, pero facilita bastante.

  • Sustituye todo lo que no sea a-z o 0-9 por guión (-)
  • Limita a 63 caracteres (límite común en DNS/K8s)
  • Elimina guiones al inicio/fin, si los hubiera
CI_COMMIT_REF_NAME=feature/Very_Cool-Feature_42
CI_COMMIT_REF_SLUG=feature-very-cool-feature-42

CI_COMMIT_REF_SLUG=pipe/variables
CI_COMMIT_REF_SLUG=pipe-variables

CI_JOB_NAME=lint-test
CI_JOB_NAME_SLUG=lint-test

CI_PROJECT_PATH=puziol/learn-gitlab-app
CI_PROJECT_PATH_SLUG=puziol/learn-gitlab-app

CI_PROJECT_NAMESPACE=puziol
CI_PROJECT_NAMESPACE_SLUG=puziol

Cuando necesitas el nombre original de la rama, con mayúsculas y símbolos intactos (ej: validación de patrón, git checkout, logs más legibles) no utilices slug. Es bueno definir un estándar en el equipo.

Usa slug cuando vayas a crear nombres de entornos, namespaces en Kubernetes, URLs (ej: review apps), nombre de ramas en tags Docker (app:${CI_COMMIT_REF_SLUG}), Labels, directorios, archivos generados automáticamente, etc.

VariableContextoDescripción / Uso Práctico
CI_MERGE_REQUEST_TITLEpipelineVerifica o muestra el título del MR. Útil para validar patrones de nomenclatura.
CI_MERGE_REQUEST_IDpipelineID global del MR. Puede usarse en logs o llamadas en la API.
CI_MERGE_REQUEST_IIDpipelineID del MR en el proyecto (ese número de la URL). Útil en scripts o dashboards.
CI_MERGE_REQUEST_ASSIGNEESpipelineLista de responsables. Se puede validar si el MR está asignado a alguien.
CI_MERGE_REQUEST_APPROVEDpipelineVerificar si ya fue aprobado antes de continuar (ej: traba release sin aprobación).
CI_MERGE_REQUEST_LABELSpipelineMuy útil para lógica condicional basada en labels (deploy, hotfix...).
CI_MERGE_REQUEST_DESCRIPTIONpipelinePuede usarse para análisis de contenido del MR (ej: changelog automatizado).
CI_MERGE_REQUEST_DRAFTpipelineEvita que MRs en borrador pasen al entorno o sean desplegados.
CI_MERGE_REQUEST_SOURCE_BRANCH_NAMEpre-pipelineRama de origen del MR (quien quiere hacer merge).
CI_MERGE_REQUEST_TARGET_BRANCH_NAMEpre-pipelineRama de destino del MR (ej: main, develop). Esta se puede fijar en la cabeza
CI_MERGE_REQUEST_EVENT_TYPEpre-pipelineÚtil para entender el tipo de pipeline del MR (merge_train, detached, etc).
CI_MERGE_REQUEST_SQUASH_ON_MERGEpipelinePara aplicar lógica si squash está activado (ej: validar commit único).

Existen algunas variables interesantes que es bueno conocer.

Las variables CHAT_CHANNEL, CHAT_INPUT, CHAT_USER_ID son específicas de ChatOps en GitLab, es decir, cuando un pipeline se dispara a partir de un comando enviado por chat (como Slack) usando integración con GitLab ChatOps.

CI_PIPELINE_SOURCE

Esta variable es muy importante. Lo que disparó el pipeline puede cambiar todo el flujo. Los posibles valores son:

ValorOrigen del pipeline
pushPush en una rama
webCreado manualmente vía UI
triggerDisparado por un trigger token
schedulePipeline agendado
apiPipeline creado vía API REST
externalDisparado por integración externa (como webhook de otro sistema)
pipelinePipeline creado por otro pipeline (multi-project o child pipeline)
chatPipeline iniciado por comando vía ChatOps
webideIniciado a partir del Web IDE
merge_request_eventPipeline de merge request (tipo antiguo)
external_pull_request_eventPipeline disparado por pull request externo (ej: GitHub)
parent_pipelinePipeline padre que generó este
ondemand_dast_scanPipeline iniciado por un escaneo DAST bajo demanda
security_orchestration_policyDisparado por una política de seguridad

Sé que son muchas variables, pero la idea es crear algo que podamos consultar más rápido. La documentación oficial siempre está disponible y conocer un poco de lo que podemos hacer nos da alas. ¡Ahora con los poderes correctos podemos comenzar a crear el flujo de las cosas!

Job Variables

Si la misma variable (misma key) se define en el repositorio y a nivel de job, esta sobrescribirá el valor localmente dentro del job.

Imaginemos que tenemos MY_VAR definida en el repositorio como MY_VAR=hello

build:
stage: build
variables:
MY_VAR: my-var # Variable a nivel de job
script:
- echo $MY_VAR # resultará my-var