Skip to content
All posts
DevOps#devops#jenkins#ci-cd#automation#security#cloud#learning

Stop Losing Builds: Version Jenkins Artifacts

DNS that points nowhere, sockets that won't connect, latency that shouldn't exist. The small set of Linux commands that turned networking from guesswork into a checklist.

Priyanshu Maurya

DevOps & Cloud Engineer

23 Aug ’26 8 min
ShareLink copied

Software teams spend enormous effort making builds succeed, then sometimes treat the resulting artifact like disposable output.

That is backwards.

A successful build produces something valuable: a WAR, JAR, package, binary, container image, or another deployable unit. If that artifact cannot be identified, preserved, verified, and retrieved later, the pipeline has lost much of its value.

The key principle is simple:

Build once, identify it permanently, store it safely, and promote the same artifact through environments.

That principle turns artifact management from a housekeeping task into part of deployment reliability.

The artifact is the output of your build

Consider a Maven application:

mvn clean package

After compilation and packaging, Maven might produce:

target/vprofile-v2.war

A naive pipeline may deploy that file immediately and forget about it. That works until somebody asks: hu

  • What exact build is running in production?
  • Which Git commit produced it?
  • Can we redeploy the same binary?
  • Can we roll back without rebuilding?
  • Did staging test the same artifact that reached production?

If the answer is "we can probably rebuild it," the process is weaker than it looks.

Rebuilding from the same source does not automatically guarantee the same output. Dependencies, build tools, base images, repositories, environment variables, and external build inputs may have changed.

A reproducible deployment therefore starts with preserving the artifact that was actually built.


Why copying files is not artifact management

A tempting Jenkins stage looks something like this:

post {
    success {
        sh 'mkdir -p versions'
        sh 'cp target/*.war versions/vpro.war'
    }
}

It appears reasonable. The artifact has been copied somewhere, therefore civilization has apparently achieved reliable CI/CD.

Not quite.

The directory is usually inside the Jenkins workspace. Workspaces are working directories, not durable artifact repositories. They can be cleaned, replaced, deleted with agents, or reused by later builds.

There is another problem:

versions/vpro.war

does not tell you which build created the file.

If every build writes the same filename, previous artifacts may be overwritten.

Keep copying the latest WAR into a folder Give every successful build an immutable identity and store it in durable artifact storage.

That distinction matters enormously.

Give every artifact an identity

A useful artifact version should allow engineers to connect a binary back to the pipeline execution and source code that produced it.

Jenkins already provides useful identifiers such as:

BUILD_NUMBER
BUILD_ID
BUILD_TAG
JOB_NAME

Git provides another critical identifier:

git rev-parse --short HEAD

You can combine them:

BUILD_VERSION="${BUILD_NUMBER}-$(git rev-parse --short HEAD)"

A resulting artifact might look like:

vprofile-184-a91f62c.war

Now the filename communicates two useful facts:

  • Jenkins build 184 created it.
  • Git commit a91f62c contributed the source identity.

A timestamp can also be useful:

TIMESTAMP=$(date +%Y%m%d-%H%M%S)

producing something like:

vprofile-20260905-221530.war

But timestamps alone are weaker because they do not naturally connect the artifact to source control.

Compare common versioning approaches

IdentifierTraceabilityHuman readabilityBest use
TimestampMediumHighSupplemental metadata
Jenkins build numberHighHighCI build identity
Git commit SHAVery highMediumSource traceability
Semantic versionHighHighReleases
Build number + Git SHAVery highHighCI/CD artifacts

For many Jenkins pipelines, build number plus Git SHA is an excellent default.

Archive artifacts through Jenkins

Jenkins provides artifact archival specifically for preserving build outputs.

A Declarative Pipeline can use:

post {
    success {
        archiveArtifacts(
            artifacts: '**/target/*.war',
            fingerprint: true
        )
    }
}

This is fundamentally better than treating the workspace as permanent storage.

The artifact becomes associated with a specific Jenkins build, making it easier to answer:

Which pipeline execution produced this file?

Fingerprinting adds another layer of traceability by allowing Jenkins to identify artifacts using checksums and track their usage across builds.

The official Jenkins Pipeline documentation is worth learning rather than memorizing isolated snippets copied from mysterious corners of the internet.

But Jenkins archival is only part of the architecture.

Jenkins should not become your artifact repository

There is an important tradeoff.

archiveArtifacts is useful for CI history, troubleshooting, and modest artifact retention. It does not mean the Jenkins controller should become the permanent warehouse for every binary your organization produces forever.

At scale, use dedicated artifact storage.

Depending on the artifact type, that might include:

  • Amazon S3
  • JFrog Artifactory
  • Sonatype Nexus Repository
  • AWS CodeArtifact
  • Amazon ECR for container images

For AWS-based environments, an S3 object structure might look like:

s3://company-artifacts/vprofile/
├── 181-a83c19d/
│   └── vprofile.war
├── 182-741bd42/
│   └── vprofile.war
└── 183-f29ca71/
    └── vprofile.war

A Jenkins stage could upload the artifact:

stage('Publish Artifact') {
    steps {
        sh '''
            VERSION="${BUILD_NUMBER}-$(git rev-parse --short HEAD)"
            aws s3 cp target/vprofile-v2.war \
              "s3://company-artifacts/vprofile/${VERSION}/vprofile.war"
        '''
    }
}

The same concept works with repository managers and container registries.

The architecture becomes:

Source Code
    |
    v
Jenkins Build
    |
    v
Tests
    |
    v
Versioned Artifact
    |
    v
Artifact Repository
    |
    +--------> Development
    |
    +--------> Staging
    |
    +--------> Production

Notice what is not happening.

Production is not rebuilding the application.


Build once, promote many times

This is the deeper lesson.

Imagine Jenkins builds:

vprofile-184-a91f62c.war

The artifact passes unit tests and is deployed to staging.

A weak release process later runs:

git checkout <commit>
mvn clean package

and deploys the newly generated WAR to production.

That means staging and production did not necessarily receive the same binary.

The better process is:

Build 184
   |
   v
vprofile-184-a91f62c.war
   |
   +--> Test
   |
   +--> Staging
   |
   +--> Production

Promotion changes where an artifact runs, not what the artifact is.

That makes debugging dramatically cleaner. If production behaves differently from staging, you can investigate configuration, infrastructure, traffic, data, or environment differences without immediately wondering whether the application binary changed too.

Store metadata with the artifact

A filename helps, but mature pipelines preserve additional build metadata.

For example:

{
  "application": "vprofile",
  "build_number": "184",
  "git_commit": "a91f62c",
  "branch": "main",
  "artifact": "vprofile.war"
}

The pipeline can generate metadata automatically:

cat > build-info.json <<EOF
{
  "application": "vprofile",
  "build_number": "${BUILD_NUMBER}",
  "git_commit": "$(git rev-parse HEAD)",
  "branch": "$(git rev-parse --abbrev-ref HEAD)",
  "artifact": "vprofile.war"
}
EOF

Archive both files:

archiveArtifacts(
    artifacts: 'target/*.war,build-info.json',
    fingerprint: true
)

Now an engineer investigating an incident has evidence instead of archaeology.

Treat artifact integrity as a security concern

Versioning solves identity. It does not prove integrity.

A deployment system should also be able to detect whether an artifact changed after it was built.

Generate a checksum:

sha256sum target/vprofile-v2.war > vprofile.war.sha256

Later, verify it:

sha256sum -c vprofile.war.sha256

For more mature software supply chains, organizations can add artifact signing, provenance information, dependency inventories, and Software Bill of Materials generation.

The principle remains the same:

The artifact entering production should be demonstrably connected to the artifact produced and validated by CI.

Security controls around the repository matter too. CI may need write permission, while deployment systems often need only read permission. We should not casually replace release artifacts.

Design retention instead of keeping everything forever

Immutable artifacts do not mean infinite storage.

Define a retention policy deliberately.

For example:

  • Keep recent development builds for 30 days.
  • Keep release candidates longer.
  • Keep production releases according to rollback and compliance requirements.
  • Protect currently deployed versions from deletion.
  • Apply lifecycle policies to old artifacts.

Amazon S3 lifecycle rules can automate retention when S3 is used as artifact storage. The Amazon S3 lifecycle documentation explains the available controls.

Without retention, storage grows forever. With careless deletion, rollback disappears exactly when somebody needs it. Humans have somehow managed to make both extremes common.


A production-ready artifact workflow

A practical Jenkins pipeline should follow a predictable sequence:

  1. Check out a specific Git revision.
  2. Compile and package the application.
  3. Run automated tests and quality checks.
  4. Assign the artifact an immutable version.
  5. Record build and source metadata.
  6. Generate integrity information such as a checksum.
  7. Archive the artifact with the Jenkins build.
  8. Publish it to durable artifact storage.
  9. Deploy that exact artifact to downstream environments.
  10. Retain previous production versions for rollback.

The important part is not any individual Jenkins command.

It is the chain of identity:

Git Commit
    |
    v
CI Build
    |
    v
Immutable Artifact
    |
    v
Tested Version
    |
    v
Deployed Version

Every arrow should be traceable.

What this changes during an incident

Suppose version 184-a91f62c is deployed and causes failures.

Without proper artifact management, rollback may involve checking out old source code and rebuilding it under pressure. That introduces new variables during an incident, which is precisely when adding new variables is least intelligent.

With immutable artifacts, rollback becomes conceptually simple:

Current: 184-a91f62c
Previous: 183-f29ca71

Redeploy:

183-f29ca71

No source rebuild is required. No guessing which dependencies were originally downloaded. No searching through abandoned workspace directories.

That is the operational payoff.

The takeaway

Artifact management is not merely about saving WAR files after Jenkins finishes a build. It is about creating a trustworthy connection between source code, CI execution, testing, deployment, and rollback.

A production-ready pipeline should make one question trivial to answer:

Exactly what software is running right now, and where did it come from?

If answering that question requires rebuilding code, inspecting random directories, or trusting somebody's memory, the pipeline is incomplete.

Build once. Give the result an immutable identity. Preserve its metadata and integrity. Promote the same artifact through every environment.

A build you cannot reliably identify and reproduce in deployment is not a release artifact. It is just a file.

Last updated Sep 7, 2026

ShareLink copied

Enjoyed this? I write about what I'm learning.

DevOps, cloud, Linux, and automation - one honest post at a time. Follow along on my learning journey.