Skip to content
DevOps devops cicd 5 min read

Artifact & Dependency Management

Every CI/CD pipeline produces something: a compiled .jar file, a Docker image, an npm package, or a zipped bundle of your website. These outputs are called artifacts (the finished, ready-to-ship results of a build). At the same time, your build consumes dependencies (third-party libraries your code needs, like Spring Boot or React). Managing both — storing them, versioning them, and serving them reliably — is what artifact management is about. Get this right and your deployments become fast, repeatable, and trustworthy.

Artifacts vs dependencies

These two words get mixed up, so let’s pin them down clearly.

A dependency is code written by someone else that your project pulls in to build. When you run npm install or mvn package, your tool downloads dependencies from a registry (a server that hosts packages). Examples: express, lodash, junit.

An artifact is the thing your build produces. After compiling and packaging, you get a single deployable unit. Examples: myapp-1.4.2.jar, a Docker image myapp:1.4.2, or a dist.tar.gz.

TermWho makes itExampleWhere it lives
DependencyA third party[email protected]A public/private registry
ArtifactYour pipelinemyapp-1.4.2.jarAn artifact repository

The same tool — an artifact repository (a storage server for build outputs and packages) — usually handles both jobs. It caches the dependencies you download and stores the artifacts you produce.

Why store and version artifacts

It is tempting to just rebuild your app on every server. Don’t. Here is why you store artifacts instead.

  • Reproducibility. If you rebuild from source on each machine, tiny differences (a newer library, a different OS patch) can change the output. A stored artifact is byte-for-byte identical everywhere.
  • Speed. Downloading a finished 40 MB jar is far faster than recompiling for five minutes.
  • Traceability. Every version is kept, so you can answer “what exactly is running in production?” and roll back instantly.
  • Offline safety. If a public registry goes down (it happens), your cached dependencies keep your builds working.

Versioning means giving each artifact a unique, ordered name like 1.4.2. The common scheme is semantic versioning (MAJOR.MINOR.PATCH): bump PATCH for bug fixes, MINOR for new features, MAJOR for breaking changes. Never overwrite a released version — once 1.4.2 is published, it stays frozen forever.

Never reuse a version number for a changed artifact. If 1.4.2 means two different things on two days, every rollback and audit becomes a guessing game. Publish a new version instead.

Build once, promote everywhere

This is the single most important principle on this page.

Build once, promote everywhere means you build your artifact a single time, then move that exact same file through your environments — staging, then production — without rebuilding. You “promote” it by deploying the same artifact to the next stage once it passes tests.

The opposite (rebuilding for each environment) is dangerous: the artifact you tested in staging is not the one you ship to production, so your tests prove nothing about what users actually run.

[ Build once ]  -->  staging  -->  production
   myapp-1.4.2.jar      (same file, promoted, never rebuilt)

In practice: your CI pipeline builds myapp-1.4.2.jar, uploads it to the artifact repository, and your deploy jobs download that exact version for each environment.

The main tools

You have several solid options. Pick based on what you ship and where.

ToolBest forHostingNotes
Sonatype NexusMixed stacks (Java, npm, Docker, PyPI)Self-hostedFree OSS version, easy on Ubuntu
JFrog ArtifactoryLarge enterprises, many formatsSelf-hosted or cloudPowerful, paid tiers
GitHub PackagesTeams already on GitHubCloud (managed)Tight GitHub Actions integration
Language registriesA single ecosystemCloud (managed)npm, Maven Central, PyPI, Docker Hub

Installing Nexus on Ubuntu

Nexus is the most common self-hosted choice. Here is a real install on Ubuntu 22.04/24.04 LTS. It needs Java, so we install that first.

sudo apt update
sudo apt install -y openjdk-17-jre-headless
sudo useradd -r -m -d /opt/nexus-home nexus
cd /opt
sudo curl -L -o nexus.tar.gz https://download.sonatype.com/nexus/3/latest-unix.tar.gz
sudo tar xzf nexus.tar.gz
sudo mv nexus-3* nexus
sudo chown -R nexus:nexus /opt/nexus /opt/sonatype-work

Now create a systemd service (systemd is Ubuntu’s tool for managing background services) so Nexus starts on boot.

# /etc/systemd/system/nexus.service
[Unit]
Description=Nexus Repository Manager
After=network.target

[Service]
Type=forking
User=nexus
ExecStart=/opt/nexus/bin/nexus start
ExecStop=/opt/nexus/bin/nexus stop
Restart=on-abort

[Install]
WantedBy=multi-user.target

Start it and open the firewall:

sudo systemctl daemon-reload
sudo systemctl enable --now nexus
sudo ufw allow 8081/tcp
sudo systemctl status nexus

Output:

● nexus.service - Nexus Repository Manager
     Loaded: loaded (/etc/systemd/system/nexus.service; enabled)
     Active: active (running) since Mon 2026-06-15 10:12:04 UTC; 30s ago
   Main PID: 4187 (java)

Nexus is now reachable at http://your-server:8081. The first admin password is stored on disk:

sudo cat /opt/sonatype-work/nexus3/admin.password

Nexus listens on all interfaces by default. On a public server, put it behind a reverse proxy (a server that sits in front and forwards requests) with HTTPS, and never expose port 8081 to the open internet via ufw.

Publishing and pulling artifacts

Once a repository exists, your tools talk to it. For Maven (Java), point at your repo in pom.xml:

<distributionManagement>
  <repository>
    <id>nexus</id>
    <url>http://your-server:8081/repository/maven-releases/</url>
  </repository>
</distributionManagement>

Then publish from CI:

mvn deploy -DskipTests

For npm, set the registry and authenticate (this also works with GitHub Packages):

npm config set registry http://your-server:8081/repository/npm-group/
npm publish

For Docker images, you log in and push to the repository’s hosted registry:

docker login your-server:8082
docker tag myapp:1.4.2 your-server:8082/myapp:1.4.2
docker push your-server:8082/myapp:1.4.2

Best Practices

  • Use immutable versions. Once published, never change an artifact under the same version number.
  • Apply build-once-promote-everywhere. Deploy the same artifact to every environment; never rebuild per stage.
  • Cache dependencies through a proxy repository. It speeds up builds and protects you from upstream outages.
  • Separate snapshot and release repos. Keep work-in-progress (-SNAPSHOT) builds out of your stable release repo.
  • Set retention/cleanup policies so old snapshots don’t fill the disk — Nexus and Artifactory both support automatic cleanup.
  • Store secrets safely. Publish credentials belong in your CI secret store, never in pom.xml or committed config.
  • Pin dependency versions in lockfiles (package-lock.json, pom.xml) so builds are reproducible.
Last updated June 15, 2026
Was this helpful?