Home DevOps Jenkins Env Vars: The Complete Reference (and Pitfalls)
Intermediate ✅ Tested on Jenkins 2.440+ | EnvInject Plugin 2.0+ 8 min · 2026-07-09

Jenkins Env Vars: The Complete Reference (and Pitfalls)

Master Jenkins environment variables: BUILD_ID, JOB_NAME, NODE_NAME, WORKSPACE, JENKINS_HOME.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Production
production tested
July 09, 2026
last updated
230
articles · all by Naren
Before you start⏱ 25 min
  • Solid grasp of devops fundamentals
  • Comfortable reading and writing code examples independently
  • Basic understanding of production deployment concepts
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Use env.BUILD_ID in Declarative, env.BUILD_ID or withEnv in Scripted — avoid $BUILD_ID in shell steps in Declarative.
  • BUILD_ID is deprecated since Jenkins 2.0; use BUILD_NUMBER for the build number.
  • WORKSPACE on agents points to the agent's workspace, not the master — always use env.WORKSPACE to be safe.
  • Stage-level environment {} blocks override pipeline-level variables for that stage only.
  • withEnv(['VAR=value']) in Scripted Pipeline creates a scoped environment block.
  • Credentials in environment {} via credentials('id') map to _USR and _PSW variables.
  • Dot notation (env.SOME_VAR) is safer than string interpolation in sh steps.
  • JENKINS_HOME is the master's home directory, not available on agents by default.
✦ Definition~90s read
What is Jenkins Environment Variables Reference?

Jenkins environment variables are key-value pairs that provide context about the current build to pipeline steps and shell scripts. They are set by Jenkins core and plugins, and can be overridden or extended by users. These variables are accessible in Declarative Pipeline via the env object (e.g., env.BUILD_NUMBER) or directly as environment variables in shell steps (e.g., $BUILD_NUMBER).

Imagine Jenkins is a factory floor with many workstations (agents).

In Scripted Pipeline, they are available as env.BUILD_NUMBER or through withEnv blocks. The environment variable system solves the problem of passing runtime information — like build number, job name, workspace path, and node name — to scripts and tools without hardcoding.

It also enables secure credential injection via credentials() binding, which creates environment variables from stored credentials. Understanding the scoping and lifecycle of these variables is crucial: pipeline-level environment {} blocks set variables for the entire pipeline, stage-level blocks override them for a stage, and withEnv creates temporary scopes in Scripted Pipeline.

The variables are inherited by shell steps, but changes in shell steps do not propagate back to the pipeline.

Plain-English First

Imagine Jenkins is a factory floor with many workstations (agents). Each workstation has a whiteboard (environment) where the foreman writes important info like job name, build number, and workspace path. The foreman uses a special marker (environment variables) that workers (pipeline steps) can read. Some info is written in permanent marker (pipeline-level env vars), some in dry-erase (stage-level) that gets wiped between stages. If a worker writes on the whiteboard with a different marker (withEnv), it only affects that worker's task. The factory also has a secure locker (credentials) where passwords are stored; the foreman can copy them to the whiteboard as _USR and _PSW for workers to use. But beware: the foreman sometimes uses an old marker (BUILD_ID) that writes numbers that don't match the actual build number — that marker is being retired.

I still remember the Friday afternoon when a production deployment failed because a shell script used $BUILD_ID to tag a Docker image. The tag was '1' instead of '45', overwriting our production image. We spent hours debugging why the wrong image was deployed. That was my painful introduction to Jenkins environment variable nuances. Jenkins has been around since 2011, and its environment variable system carries legacy baggage. Variables like BUILD_ID were deprecated in Jenkins 2.0 but still exist for backward compatibility, causing silent data corruption. This article is the reference I wish I had back then: a complete guide to every built-in variable, their scoping rules, and the gotchas that bite you in production. We'll cover the full list: BUILD_ID, JOB_NAME, NODE_NAME, BUILD_URL, WORKSPACE, JENKINS_HOME, and more. You'll learn the difference between dot notation and env. notation in Declarative Pipelines, how to scope variables to stages, use withEnv in Scripted Pipelines, dynamically generate variables, map credentials, and avoid common pitfalls like BUILD_ID deprecation and WORKSPACE agent issues. By the end, you'll never be caught off guard by a misbehaving environment variable again.

Complete Built-in Environment Variable Reference

Jenkins provides a set of built-in environment variables that are automatically set for every build. Here is the complete list with descriptions and typical values:

  • BUILD_ID: Deprecated, use BUILD_NUMBER. Example: 1 (but may be inconsistent).
  • BUILD_NUMBER: The current build number, e.g., 45.
  • BUILD_TAG: String of jenkins-${JOB_NAME}-${BUILD_NUMBER}, e.g., jenkins-my-job-45.
  • BUILD_URL: Full URL of the build, e.g., http://jenkins.example.com/job/my-job/45/.
  • EXECUTOR_NUMBER: Number of the executor on the node, e.g., 0.
  • JAVA_HOME: Java home path, if set.
  • JENKINS_HOME: Jenkins master's home directory, e.g., /var/jenkins_home. Not set on agents by default.
  • JENKINS_URL: Jenkins master URL, e.g., http://jenkins.example.com/.
  • JOB_BASE_NAME: Short job name without folder path, e.g., my-job.
  • JOB_DISPLAY_NAME: Display name of the job.
  • JOB_NAME: Full job name with folders, e.g., folder/my-job.
  • JOB_URL: Full URL of the job.
  • NODE_NAME: Name of the node (agent) running the build, e.g., agent-1.
  • NODE_LABELS: Space-separated list of labels on the node.
  • WORKSPACE: Path to the workspace on the node, e.g., /home/jenkins/workspace/my-job.

Production Insight: In a multi-branch pipeline, JOB_NAME includes the branch name, e.g., my-job/feature%2Fbranch. Always use env.JOB_NAME in pipeline steps to get the correct value. We once had a script that used $JOB_NAME and failed on branches with slashes because the slash was URL-encoded.

BUILD_ID Deprecation
BUILD_ID was deprecated in Jenkins 2.0. It may return the build number as a string, but can be inconsistent across plugins. Always use BUILD_NUMBER for numeric build ID.
Production Insight
We had a pipeline that used $BUILD_ID to name artifacts. After a Jenkins upgrade, BUILD_ID started returning '1' for all builds, causing artifact overwrites. Switched to $BUILD_NUMBER and added a validation step.
Key Takeaway
Memorize the built-in variables, but never rely on BUILD_ID.

Dot Notation vs env. Notation in Declarative Pipelines

In Declarative Pipeline, you can access environment variables using dot notation (env.BUILD_NUMBER) or directly as environment variables ($BUILD_NUMBER). However, there are critical differences:

  • Dot notation (env.BUILD_NUMBER) is Groovy code that evaluates at the pipeline execution context. It is safe in sh steps when used with string interpolation: sh 'echo ${env.BUILD_NUMBER}'. But avoid sh 'echo $BUILD_NUMBER' because it relies on shell environment, which may not be set in all contexts.
  • env. notation is not valid; you cannot write env.BUILD_NUMBER in a shell step without $ prefix.
  • In environment {} blocks, you must use the env prefix: environment { MY_VAR = env.BUILD_NUMBER } is wrong; use environment { MY_VAR = "${env.BUILD_NUMBER}" } or environment { MY_VAR = BUILD_NUMBER }.

Best Practice: Always use env.VARIABLE in Groovy expressions, and $VARIABLE in shell steps within sh scripts. For safety, use triple-double-quoted strings with interpolation: sh """echo ${env.BUILD_NUMBER}""".

Example: ``groovy pipeline { agent any environment { MY_TAG = "${env.BUILD_NUMBER}-${env.BRANCH_NAME}" } stages { stage('Build') { steps { sh """ echo "Tag: ${env.MY_TAG}" docker build -t myapp:${env.MY_TAG} . """ } } } } ``

Use env. Prefix in environment Blocks
When setting a variable from another env var, use environment { MY_VAR = "${env.OTHER_VAR}" } to ensure correct evaluation.
Production Insight
We saw a pipeline where environment { MY_VAR = "$BUILD_NUMBER" } set MY_VAR to literal '$BUILD_NUMBER' because the shell expanded it before Groovy. Fixed by using "${env.BUILD_NUMBER}".
Key Takeaway
Use env.VAR in Groovy, $VAR in shell. Never mix without understanding expansion order.

Stage-Level Environment Variable Scoping

In Declarative Pipeline, you can define environment variables at the pipeline level (global) or at the stage level. Stage-level variables override pipeline-level variables for that stage only. They are not available in subsequent stages.

Example: ``groovy pipeline { agent any environment { DEPLOY_ENV = 'production' } stages { stage('Build') { environment { DEPLOY_ENV = 'staging' // overrides for this stage } steps { sh 'echo $DEPLOY_ENV' // prints 'staging' } } stage('Deploy') { steps { sh 'echo $DEPLOY_ENV' // prints 'production' (pipeline-level) } } } } ``

Scoping Rules
  • Pipeline-level environment {} block sets variables for all stages.
  • Stage-level environment {} block overrides for that stage.
  • Variables set with env.VAR = 'value' in a script block are global and persist across stages.
  • withEnv in Scripted Pipeline creates a temporary scope; variables are not available after the block.

Production Insight: We had a stage that set DATABASE_URL to a test database, but the next stage still used the production one because we assumed stage-level variables were global. We moved the variable to pipeline-level and used a condition to set it differently per stage.

Stage Variables Are Not Inherited by Parallel Branches
If you have parallel stages, each branch gets its own environment. Stage-level variables in one branch do not affect others.
Production Insight
A developer set NODE_ENV = 'test' in a stage-level environment block and expected it to persist across all stages. After the stage, NODE_ENV reverted to pipeline-level value, causing test failures. Moved to script { env.NODE_ENV = 'test' } for global effect.
Key Takeaway
Use stage-level environment for temporary overrides only; use script { env.VAR = 'value' } for global changes.

withEnv Step Usage for Scripted Pipelines

In Scripted Pipeline, withEnv is the primary way to set environment variables temporarily. It takes a list of 'KEY=value' strings and executes a closure with those variables set. Variables are restored after the block.

Syntax: ``groovy withEnv(['MY_VAR=hello', 'ANOTHER_VAR=world']) { sh 'echo $MY_VAR' // prints 'hello' } sh 'echo $MY_VAR' // prints nothing or previous value ``

Important: withEnv sets the environment for the closure but does not modify env object. To set a variable permanently, use env.MY_VAR = 'value'.

Common Pattern: Use withEnv to override PATH or other system variables: ``groovy withEnv(['PATH+MY_TOOL=/opt/my-tool/bin']) { sh 'my-tool --version' } ``

Production Insight: We used withEnv to set AWS_DEFAULT_REGION for a specific step, but the next step also needed it. We had to repeat withEnv or use env.AWS_DEFAULT_REGION = 'us-east-1' outside. For a one-off, withEnv is perfect.

withEnv Does Not Affect env Object
If you need the variable to be accessible via env.VAR, set it directly with env.VAR = 'value'.
Production Insight
We had a scripted pipeline that used withEnv to set JAVA_HOME for a Maven build. The Maven process inherited the correct JAVA_HOME, but later steps that checked env.JAVA_HOME still saw the old value. We switched to env.JAVA_HOME = '/usr/lib/jvm/java-11' for consistency.
Key Takeaway
Use withEnv for temporary overrides, env.VAR = for permanent changes.

Dynamic Environment Variable Patterns

Sometimes you need to set environment variables dynamically based on conditions or external data. Here are production patterns:

1. Setting from file content: ``groovy pipeline { agent any stages { stage('Load Config') { steps { script { def config = readFile('config.json') def json = new groovy.json.JsonSlurperClassic().parseText(config) env.DB_URL = json.db.url } } } } } ``

2. Conditional variable: ``groovy environment { DEPLOY_ENV = BRANCH_NAME == 'main' ? 'production' : 'staging' } ``

3. Using withEnv with dynamic keys: ``groovy def vars = [:] vars['MY_VAR'] = 'value' withEnv(vars.collect { k, v -> "$k=$v" }) { sh 'echo $MY_VAR' } ``

4. Reading from external source (e.g., Vault): ``groovy pipeline { agent any environment { // Using a plugin like HashiCorp Vault SECRET = vault path: 'secret/data/myapp', key: 'password' } stages { stage('Use Secret') { steps { sh 'echo $SECRET' } } } } ``

Production Insight: We dynamically set BUILD_TAG based on branch and timestamp to ensure unique artifact names. We used env.BUILD_TAG = "${BRANCH_NAME}-${BUILD_NUMBER}-${env.BUILD_ID}" but replaced BUILD_ID with currentBuild.startTimeInMillis.

Use currentBuild for Timestamps
currentBuild.startTimeInMillis gives a precise timestamp. Combine with BRANCH_NAME for unique tags.
Production Insight
We had a pipeline that read variables from a YAML file using readYaml and set them with env. This allowed configuration without modifying the pipeline code.
Key Takeaway
Dynamic variables are powerful; always use env. to set them globally.

environment {} Block Directives

The environment {} block in Declarative Pipeline has several directives:

  • Direct assignment: MY_VAR = 'value'
  • From another variable: MY_VAR = "${env.OTHER_VAR}"
  • From credential: MY_CRED = credentials('credential-id')
  • From function: MY_VAR = someFunction()

Credentials directive: When you use credentials('id'), Jenkins creates two environment variables: MY_CRED_USR and MY_CRED_PSW (for username-password credentials) or just MY_CRED (for secret text). For secret files, it sets the path.

Example: ``groovy pipeline { agent any environment { DOCKER_CREDS = credentials('docker-hub-creds') } stages { stage('Login') { steps { sh 'docker login -u $DOCKER_CREDS_USR -p $DOCKER_CREDS_PSW' } } } } ``

Important: The variable name you choose (e.g., DOCKER_CREDS) becomes the prefix. You cannot change the suffix _USR and _PSW.

Production Insight: We accidentally used credentials('id') with a secret text credential that didn't have username/password, so _USR and _PSW were not set. We switched to a username-password credential type.

Credential Type Matters
Only username-password credentials generate _USR and _PSW. Secret text generates only the variable itself.
Production Insight
We used environment { AWS_ACCESS_KEY = credentials('aws-creds') } but the credential was secret text; we got the key but no secret key. We changed to separate credentials for access key and secret key.
Key Takeaway
Match credential type to variable usage: username-password for pairs, secret text for single values.

Credentials-to-Environment Mapping

Jenkins provides a secure way to inject credentials as environment variables. The credentials() binding in environment {} block maps a credential ID to one or two environment variables.

Mapping Rules
  • Username with password: MY_VAR = credentials('id') creates MY_VAR_USR (username) and MY_VAR_PSW (password).
  • Secret text: MY_VAR = credentials('id') creates MY_VAR containing the secret.
  • Secret file: MY_VAR = credentials('id') creates MY_VAR containing the path to the file.
  • Certificate: Similar to username-password, but the password is the certificate password.

Example with multiple credentials: ``groovy pipeline { agent any environment { GIT_CREDS = credentials('github-ssh') DOCKER_CREDS = credentials('docker-hub') } stages { stage('Checkout') { steps { // GIT_CREDS_USR and GIT_CREDS_PSW available sh 'git clone https://$GIT_CREDS_USR:$GIT_CREDS_PSW@github.com/org/repo.git' } } } } ``

Production Insight: We had a credential that was a secret file containing a JSON. The MY_VAR pointed to the file path, but our script expected the content. We used readFile to read the file content and set another variable.

Secret File Path
For secret files, the variable contains the path to a temporary file. Read it with readFile in a script block.
Production Insight
We used credentials('id') for a secret text but forgot to mask the variable in logs. We added sh 'set +x; echo $MY_VAR; set -x' to avoid exposure.
Key Takeaway
Use credentials() for secure injection; never echo credentials in plain text.

Common Pitfall: BUILD_ID Deprecation

BUILD_ID was deprecated in Jenkins 2.0 (released in 2016) and replaced by BUILD_NUMBER. However, BUILD_ID still exists for backward compatibility. The problem is that its value may not match the actual build number. In some plugins, BUILD_ID returns the build number as a string; in others, it returns a different identifier. This inconsistency can cause silent data corruption.

Production Incident: We used $BUILD_ID in a shell script to tag Docker images. After a Jenkins upgrade, BUILD_ID started returning '1' for all builds, overwriting the same tag. We lost the ability to rollback because all images had the same tag.

Fix: Replace all occurrences of $BUILD_ID with $BUILD_NUMBER. Also check plugins that might set BUILD_ID differently.

Detection: Run echo BUILD_ID=$BUILD_ID BUILD_NUMBER=$BUILD_NUMBER in a build and compare.

Best Practice: In new pipelines, never use BUILD_ID. If you see it in legacy code, refactor immediately.

BUILD_ID Is Unreliable
Do not use BUILD_ID in any production pipeline. Always use BUILD_NUMBER.
Production Insight
We added a pipeline validation step that checks BUILD_ID equals BUILD_NUMBER and fails if not. This caught the issue before deployment.
Key Takeaway
Treat BUILD_ID as toxic; replace with BUILD_NUMBER everywhere.

Common Pitfall: WORKSPACE on Agents

The WORKSPACE environment variable points to the workspace directory on the node executing the build. On agents, this is the agent's workspace path, not the master's. However, there are nuances:

  • Master vs Agent: If the build runs on master, WORKSPACE is on master. If on an agent, it's on the agent. Never assume the path is the same.
  • Symbolic links: Some configurations use symbolic links for workspaces, which can cause WORKSPACE to resolve differently.
  • Custom workspace: If you set a custom workspace via ws('path'), WORKSPACE changes to that path within the ws block.

Production Incident: We had a pipeline that used WORKSPACE in a script that copied files to a network share. The script assumed the workspace was on the master, but the build ran on an agent. The copy failed because the path didn't exist on the master.

Fix: Use env.WORKSPACE in pipeline steps to get the correct path. In shell scripts, $WORKSPACE is correct but ensure the agent has the same filesystem.

Best Practice: Always use env.WORKSPACE in Groovy code. In shell, use $WORKSPACE but be aware of agent context.

WORKSPACE Is Node-Specific
Do not hardcode workspace paths. Use env.WORKSPACE or $WORKSPACE to get the dynamic path.
Production Insight
We had a pipeline that used ws('custom-workspace') to override workspace. Later steps that used $WORKSPACE got the custom path, but we forgot to revert, causing confusion. We used ws blocks carefully.
Key Takeaway
Always reference WORKSPACE dynamically; never assume a fixed path.

Common Pitfall: JENKINS_HOME Not Available on Agents

JENKINS_HOME is the home directory of the Jenkins master, typically /var/jenkins_home. This variable is set only on the master node. On agents, JENKINS_HOME is not set by default. If your pipeline tries to access $JENKINS_HOME on an agent, it will be empty or undefined.

Production Incident: A pipeline step that read a configuration file from $JENKINS_HOME/config.xml worked on master but failed on agents because the variable was empty. The file didn't exist on agents anyway.

Fix: Do not rely on JENKINS_HOME on agents. If you need to access master files, use the http:// API or copy files via plugins like Copy Artifact or External Workspace Manager.

Alternative: Use Jenkins.instance.getRootDir() in a script block on master, but this won't work on agents.

Best Practice: Avoid using JENKINS_HOME in pipeline steps that may run on agents. If you must, ensure the step runs on master via agent { label 'master' }.

JENKINS_HOME Is Master-Only
Do not use JENKINS_HOME in agent builds. It will be empty or cause errors.
Production Insight
We had a shared library that read a global config from $JENKINS_HOME. It worked in development (master) but failed in production (agents). We moved the config to a shared library resource or used the Config File Provider plugin.
Key Takeaway
Never assume JENKINS_HOME is available; use agent-safe alternatives.

Common Pitfall: Environment Variable Leakage Between Stages

In Declarative Pipeline, variables set in a stage-level environment {} block are scoped to that stage. However, variables set via script { env.VAR = 'value' } are global and persist across stages. This can cause unintended side effects.

Symptom: A variable set in one stage is unexpectedly available in another stage, causing conflicts.

Example: ``groovy pipeline { agent any stages { stage('Set Var') { steps { script { env.MY_VAR = 'old_value' } } } stage('Override') { environment { MY_VAR = 'new_value' } steps { sh 'echo $MY_VAR' // prints 'new_value' } } stage('Check') { steps { sh 'echo $MY_VAR' // prints 'old_value' (global) } } } } ``

Fix: Use stage-level environment for temporary overrides, and avoid setting global variables unless necessary. If you need a global variable, use a unique name to avoid collisions.

Best Practice: Prefix global variables with the stage name or pipeline name, e.g., BUILD_MY_VAR.

Use Unique Names for Global Variables
Prefix global env vars with a namespace to avoid accidental overrides.
Production Insight
We had a pipeline where stage 'Test' set env.DB_URL to a test database, and stage 'Deploy' also used env.DB_URL expecting production. The test value leaked into deploy. We renamed the test variable to TEST_DB_URL.
Key Takeaway
Be deliberate about variable scope; use stage-level for temporary, script-level for global with unique names.

Common Pitfall: Environment Variables Not Available in Parallel Stages

In Declarative Pipeline, parallel stages each run in their own context. Environment variables set in one parallel branch are not visible in another. Also, stage-level environment in a parallel block is isolated.

Symptom: A variable set in one parallel branch is empty in another branch.

Example: ``groovy pipeline { agent any stages { stage('Parallel') { parallel { stage('Branch A') { environment { MY_VAR = 'A' } steps { sh 'echo $MY_VAR' // prints 'A' } } stage('Branch B') { steps { sh 'echo $MY_VAR' // prints nothing or pipeline-level value } } } } } } ``

Fix: If you need to share variables between parallel branches, set them at the pipeline level or use a shared workspace file.

Best Practice: Avoid relying on cross-branch variable sharing. Use artifacts or stash/unstash to pass data.

Parallel Branches Are Isolated
Each parallel branch has its own environment. Use pipeline-level variables for shared state.
Production Insight
We had a parallel stage that built two Docker images; each branch needed a unique tag. We set env.TAG at pipeline level using currentBuild.startTimeInMillis to ensure uniqueness across branches.
Key Takeaway
Parallel stages cannot share environment variables; use pipeline-level variables or external storage.
● Production incidentPOST-MORTEMseverity: high

BUILD_ID Overwrites Docker Tag in Production

Symptom
Production deployment rolled out an older Docker image; build number in Jenkins UI showed 45, but image tag was '1'.
Assumption
Assumed $BUILD_ID was the same as $BUILD_NUMBER.
Root cause
BUILD_ID is deprecated and returns the build number as a string but can be inconsistent; in this case, it returned '1' due to a plugin interaction.
Fix
Replaced $BUILD_ID with $BUILD_NUMBER in the shell script and all pipeline references.
Key lesson
  • Never use BUILD_ID in production; always use BUILD_NUMBER for the numeric build ID.
Production debug guideSymptom → Root cause → Fix4 entries
Symptom · 01
Shell script prints wrong build number; echo $BUILD_ID shows '1' instead of '45'.
Fix
Replace $BUILD_ID with $BUILD_NUMBER. Run echo $BUILD_NUMBER to verify.
Symptom · 02
WORKSPACE variable points to master's workspace on an agent build, causing file not found errors.
Fix
Always use env.WORKSPACE in pipeline steps. In shell, $WORKSPACE is correct. Ensure agent has workspace directory.
Symptom · 03
Stage-level environment variable not available in later stage; echo $MY_VAR returns empty.
Fix
Declare the variable in pipeline-level environment {} block or use env.MY_VAR = 'value' in script step. Stage variables are scoped to that stage only.
Symptom · 04
withEnv block in Scripted Pipeline does not persist variable after block ends.
Fix
This is expected. Use env.MY_VAR = 'value' to set a variable outside the block if needed.
★ Jenkins Environment Variables Reference Quick Referenceprint this for your desk
Wrong build number in logs
Immediate action
Check if using `BUILD_ID`
Commands
echo $BUILD_NUMBER
echo $BUILD_ID
Fix now
Replace BUILD_ID with BUILD_NUMBER
Workspace path wrong on agent+
Immediate action
Print WORKSPACE in agent step
Commands
echo $WORKSPACE
pwd
Fix now
Use env.WORKSPACE in pipeline
Stage variable leaking to next stage+
Immediate action
Check environment block scope
Commands
env > /tmp/env_dump.txt
cat /tmp/env_dump.txt | grep MY_VAR
Fix now
Move variable to pipeline-level if needed
withEnv variable not available after block+
Immediate action
Check if variable set with env. prefix
Commands
echo $MY_VAR
echo $env.MY_VAR
Fix now
Use env.MY_VAR = 'value' outside withEnv
Credential env vars not set+
Immediate action
Verify credential ID in environment block
Commands
echo $MY_CRED_USR
echo $MY_CRED_PSW
Fix now
Check credentials() binding syntax
Environment Variable Access Methods Comparison
MethodScopePersistent?Use Case
env.VAR (Groovy)Pipeline globalYesSetting variable in script block
$VAR (shell)Shell step scopeNoUsing variable in sh step
withEnv(['VAR=val'])Closure scopeNoTemporary override in scripted pipeline
environment { VAR = 'val' }Stage or pipelineYesDeclarative static assignment
credentials('id')Stage or pipelineYesInjecting credentials as env vars
📦 Downloadable Quick Reference

Print-friendly master reference covering all topics in this track.

⇩ Download PDF

Key takeaways

1
Never use BUILD_ID; always use BUILD_NUMBER for build number.
2
Use env.VAR in Groovy, $VAR in shell steps.
3
Stage-level environment variables are scoped to that stage only.
4
Use withEnv for temporary overrides in Scripted Pipeline.
5
Credentials mapping creates _USR and _PSW suffixes for username-password.
6
WORKSPACE is dynamic and node-specific; always reference it dynamically.
7
JENKINS_HOME is not available on agents.
8
Parallel stages have isolated environments; use pipeline-level variables for shared state.

Common mistakes to avoid

6 patterns
×

Using `BUILD_ID` instead of `BUILD_NUMBER`

Symptom
Wrong build number in logs and artifacts
Fix
Replace all $BUILD_ID with $BUILD_NUMBER
×

Assuming `WORKSPACE` is on master when build runs on agent

Symptom
File not found errors
Fix
Always use env.WORKSPACE dynamically
×

Using `env.VAR` in shell step without interpolation

Symptom
Literal string env.VAR printed
Fix
Use sh "echo ${env.VAR}"
×

Setting stage-level environment and expecting it to persist

Symptom
Variable empty in next stage
Fix
Use script { env.VAR = 'val' } for global scope
×

Using `JENKINS_HOME` in agent builds

Symptom
Empty variable or error
Fix
Avoid; use API or plugins for master data
×

Not masking credentials in logs

Symptom
Secrets exposed in console output
Fix
Use sh 'set +x; ...; set -x' or credential masking plugin
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the difference between `BUILD_ID` and `BUILD_NUMBER` in Jenkins?
Q02SENIOR
How do you set a global environment variable in a Declarative Pipeline t...
Q03SENIOR
Explain the scoping of `withEnv` in Scripted Pipeline.
Q04JUNIOR
How do you map a Jenkins credential to environment variables in a Declar...
Q05JUNIOR
What happens if you use `$BUILD_ID` in a shell step on a Jenkins 2.x sys...
Q06SENIOR
Why is `WORKSPACE` not always the same across builds?
Q07SENIOR
How can you share environment variables between parallel stages in Decla...
Q08SENIOR
What is the best practice for setting dynamic environment variables from...
Q01 of 08JUNIOR

What is the difference between `BUILD_ID` and `BUILD_NUMBER` in Jenkins?

ANSWER
BUILD_ID is deprecated and may return an inconsistent value; BUILD_NUMBER is the reliable numeric build number. Always use BUILD_NUMBER.
FAQ · 8 QUESTIONS

Frequently Asked Questions

01
Is `BUILD_ID` completely removed in newer Jenkins versions?
02
Can I use `env.VAR` in a `sh` step directly?
03
How do I list all environment variables in a pipeline?
04
What is the difference between `environment {}` and `withEnv`?
05
Can I set a variable in one stage and use it in another without using `env`?
06
How do I pass a credential to a Docker build?
07
Why does `$WORKSPACE` sometimes include a trailing slash?
08
What happens if I define an environment variable with the same name as a built-in?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 09, 2026
last updated
230
articles · all by Naren
🔥

That's Jenkins. Mark it forged?

8 min read · try the examples if you haven't

Previous
Jenkins Email Notification Setup
38 / 39 · Jenkins
Next
Jenkins Build Triggers