Moving Data between Agents

Scenario: A test stage on a second node expects the build workspace from the first node to exist.

Two different machines don't automatically share a hard drive

A very natural but incorrect assumption: "I built the application in one stage, so the next stage can obviously still see those files." If the next stage happens to land on a different agent machine, that assumption breaks completely, because two separate machines simply don't share a filesystem by default.

Analogy: Think of two separate office branches of the same company, in different cities. If someone in the New York office prints an important document, someone in the London office can't just walk over and grab it off the printer - the physical paper doesn't teleport between buildings. If the document needs to reach London, someone has to deliberately package it up and ship it there (that's exactly what stash and unstash do between Jenkins stages on different agents). Assuming "the file is right there" just because it existed in one office is the mistake this section exists to prevent.

Understanding that each agent allocation is its own separate filesystem - even two allocations with the exact same label can land on genuinely different machines - is the foundational fact behind everything in this section.

Pipeline example

stage('Build') { steps { sh './package.sh'; stash name: 'pkg', includes: 'dist/**' } }
stage('Test') { steps { unstash 'pkg'; sh './verify.sh' } }

Workspaces are local scratch space, not a shared drive

Different agent allocations have entirely separate filesystems, even when they're requested with identical labels - Jenkins might assign the exact same label expression to two different physical or virtual machines on different runs, or even within the same run if stages are configured to pick their own agents. A later stage may genuinely run on a freshly-provisioned machine that has never seen any file from an earlier stage. stash packages a selected set of workspace files for later use within the same build, and unstash restores those files onto whatever workspace calls for them elsewhere in that same run. Use narrow, purpose-specific include patterns and clearly descriptive names - don't stash an entire checkout or a whole dependency cache just because it happens to be convenient; that wastes storage and slows down every unstash operation that has to retrieve it.

[Pipeline] { (Build)
[Pipeline] sh
+ ./package.sh
Building dist/app-1.4.2.tgz
[Pipeline] stash
Stashed 1 file(s)
[Pipeline] }
[Pipeline] { (Test)
[Pipeline] node
Running on agent-worker-07
[Pipeline] unstash
Unstashed 1 file(s)
[Pipeline] sh
+ ./verify.sh
Verified dist/app-1.4.2.tgz
[Pipeline] }

Notice the Test stage explicitly logs which agent it landed on (agent-worker-07, likely different from wherever Build ran), and that it has to unstash before the file is actually present - it does not just automatically appear because "Build already created it."

Stash is for this-build data, not for durable release storage

stash suits modest amounts of intra-run data, not long-term release storage. Large stashes consume real controller or artifact-manager storage, and they expire along with the build's own retention window - a stash from build #200 will not still be there when someone goes looking for it after build #350's retention has kicked in. preserveStashes supports Jenkins' "restart from stage" feature for a bounded number of recent builds, but that is not the same thing as making a package durable for the long term. Genuine release outputs belong in a real artifact repository, addressed by immutable coordinates (a version number or content digest), not in a stash.

Verify bytes actually survived the handoff

Build the artifact exactly once, create a manifest recording the source revision and exact tool versions used, calculate a checksum digest for the built output, and carry those same bytes through testing and promotion rather than rebuilding at each step. Verify the checksum again after every unstash and after every download from a repository - a corrupted transfer is rare, but "rare" is exactly the kind of failure that's worth cheaply catching automatically rather than debugging manually months later. Exclude credentials, Git metadata, uncontrolled symbolic links, and temporary files from anything you stash or archive.

A pitfall: quietly rebuilding instead of actually transferring

When expected data is simply absent on a later stage, check in order: did the producing step actually run and succeed, did the include glob pattern actually match any real files, did retention policy remove the state before this build got to it, or did the consuming stage request the data under a different, mismatched name than the one it was stashed under. A genuinely tempting-looking "fix" for a missing-file error is to just rebuild the artifact fresh on the destination node instead of tracking down why the transfer failed - but doing that can make a broken pipeline turn green while quietly destroying the very thing you were trying to prove: that the test stage and the release stage examined the exact same artifact, not two separately-built, potentially-different copies that merely look the same on the surface.

Goal: Practice the concrete behavior in jenkins-artifact-promotion. Learn sections have no Jenkins terminal; the Jenkins lab opens the real shared service in a new full-screen tab inside your private folder.