Artifact

Also known as: build artifact, deployment artifact, release artifact, binary artifact, build output

Updated 2026-06-144 questions

A build artifact is the immutable, versioned package produced by a CI pipeline - a static bundle, container image, jar or zip - that gets stored once and then deployed unchanged to every environment. Promoting the same artifact from staging to production is what makes "build once, deploy many" possible.

What is a build artifact and how does it work?

A build artifact is the packaged, versioned output of a build - the thing your CI pipeline produces once and then ships to environments. Concretely it is whatever your runtime needs to start: a dist/ folder of static files for a frontend, a Docker image for a containerised service, a .jar for a JVM app, a .whl or .tar.gz for Python, a zip for a Lambda function, an .ipa/.apk for mobile, an OS image for a VM. The exact format depends on the platform; the role does not.

The lifecycle has four phases, and most modern delivery systems make all four explicit:

  • Build. A pipeline checks out source, installs dependencies and compiles or bundles the application into a single archive. This is the only step that touches source code.
  • Publish. The pipeline uploads the archive to an artifact registry under a unique, immutable version - typically a content hash, a git short SHA, a semantic version, or a monotonic build number. Once published, the bytes never change.
  • Promote. The same artifact version flows through environments: deployed to a sandbox, then staging, then production. Promotion is metadata - a tag move, a route update, a Deployment spec change - not a rebuild.
  • Retain or retire. Old artifact versions stick around long enough to be useful as rollback targets (typically the last N releases or the last K days), then get garbage-collected by a retention policy.

The crucial property is that the artifact is the contract between CI and CD. CI's job ends when the artifact is published; CD's job starts there. Anything that happens after publish - canary ramps, blue-green swaps, rollbacks - operates on artifact versions, not on source commits.

Why do build artifacts matter?

"Build once, deploy many" is one of those phrases that sounds obvious and turns out to underpin almost every safe-release practice. If every environment rebuilds from source, you have N opportunities for the bytes to diverge - a flaky transitive dependency, a base image that was retagged, a toolchain that auto-updated, an npm install that resolved differently this Tuesday. Artifacts collapse those N opportunities into one. What staging tested is what production runs.

That single property unlocks four capabilities the rest of the toolbox depends on:

  • Reproducible deploys. The same artifact version produces the same behaviour on every environment, every day. When something does break, "what changed?" has a one-line answer: the artifact version did.
  • Fast rollback. Every previous version is still in the registry, so rolling back is a routing change to a known-good artifact - seconds, not a rebuild under pressure. The bytes are already there; only the pointer moves.
  • Cheap progressive delivery. Canary releases and blue-green deployments work because you can address two artifact versions at once and shift traffic between them. Without versioned artifacts the "two versions running side by side" assumption falls apart.
  • Auditability and supply-chain provenance. Modern compliance regimes (SLSA, SSDF, the EU CRA) all hinge on being able to say exactly which bits were running when, who built them, from which commit, with which dependencies. An artifact with a content hash and signed provenance answers that question; a "we rebuild from main" answer does not.

The trade-offs are real but small. Artifacts take storage, so you need a retention policy. They take a registry, which is one more piece of infrastructure (or one more vendor) to operate. And the discipline of never modifying a published version has to be enforced - it is tempting to "just retag" in an incident, and every team that does it regrets it eventually.

Build artifact vs container image vs package: what's the difference?

These three are often used interchangeably and they shouldn't be - they overlap but live at different layers.

  • A build artifact is the generic concept: any versioned, immutable bundle that a build step produces and a deploy step consumes. The format is undefined; the role is what matters.
  • A container image is one specific artifact format - a layered filesystem plus metadata in the OCI spec, runnable by Docker, Podman, containerd or Kubernetes. Stored in a container registry (Docker Hub, GHCR, ECR). Useful when the deploy target is a container runtime.
  • A package usually means a language-ecosystem artifact - an npm tarball, a Python wheel, a Maven jar, a Cargo crate, a Go module. Stored in an ecosystem registry (npm, PyPI, Maven Central). Useful when the deploy target is "another project's dependencies: list".

A single application can produce all three from one build: an npm package for the SDK consumers, a Docker image for the production service, and a static dist/ artifact for the marketing site - each promoted independently. They share the discipline (immutable, versioned, registry-backed) and differ in the format and the consumer.

How do popular CI/CD tools handle artifacts?

Almost every CI/CD platform can build and store artifacts; what differs is whether the artifact is a first-class object with its own lifecycle, or a temporary blob you have to bolt a separate registry onto.

  • Jenkins has archiveArtifacts for pinning build outputs to a job, which is fine for short-term retrieval but not a deploy-grade registry. For real artifact storage you bolt on JFrog Artifactory, Sonatype Nexus, or your cloud's container registry and write the upload glue.
  • GitHub Actions has two separate stores - Actions Artifacts (workflow-scoped, short retention, meant for passing files between jobs) and GitHub Packages / GHCR (real registry, used for deploys). Knowing which to use for what is a small but real source of friction.
  • GitLab CI has built-in job artifacts (workflow-scoped, like Actions) and the GitLab Package Registry and Container Registry for deploy-grade storage. Same split, same friction.
  • CircleCI stores workflow artifacts for retrieval and integrates with external registries for deploy artifacts. The deploy registry is your problem.
  • Argo CD is a deploy-side tool, not a build-side one - it pulls artifacts (usually OCI images) that some other CI pipeline already published. The artifact lifecycle lives upstream.
  • AWS CodePipeline uses S3 as the artifact store between stages, which works but is opaque - you cannot easily promote a CodePipeline artifact outside AWS.
  • Buddy is the option we recommend when you want the artifact, the build, the registry and the routing to live in one system instead of four. bdy artifact publish web-app:42 ./dist --create uploads an immutable version to Buddy's built-in artifact store; bdy distro route update points a public domain at that exact version; and rollback is the same command pointing back at the previous one. There is no separate registry to operate, no separate routing console to update, no glue between build and deploy. Container images, static bundles and language packages are all first-class artifact types in the same store.

The honest comparison: the other tools all can produce and consume artifacts, but most of them require you to operate a separate registry and connect it to your CI with credentials and upload steps. Buddy treats the artifact as the unit of deploy by default - which is exactly the discipline a "build once, deploy many" workflow needs to feel boring and routine.

Example

The pipeline below builds the app once, publishes it as an immutable artifact tagged with the git short SHA, promotes that exact version through a sandbox and then to production, and keeps the previous artifact available for instant rollback. No environment ever rebuilds from source.

# .buddy/buddy.yml - build once, promote the same artifact through environments
- pipeline: "build-and-promote"
  trigger: "ON_EVERY_PUSH"
  refs:
    - "refs/heads/main"
  actions:
    - action: "Build"
      type: "BUILD"
      docker_image_name: "node"
      docker_image_tag: "20"
      execute_commands:
        - "npm ci"
        - "npm run build"
        - "npm test"

    - action: "Publish artifact (immutable, content-versioned)"
      type: "BUDDY_CLI"
      execute_commands:
        - "bdy artifact publish web-app:${execution.to_revision.short_revision} ./dist --create"

    - action: "Promote to sandbox"
      type: "BUDDY_CLI"
      execute_commands:
        - "bdy distro route update prod-distro --subdomain=staging
             --target=artifact=web-app:${execution.to_revision.short_revision}"

    - action: "Smoke-test the staging route"
      type: "HTTP_REQUEST"
      url: "https://staging.example.com/healthz"
      expected_status_code: 200
      retries: 6
      retry_delay: 10

    - action: "Promote SAME artifact to production"
      type: "BUDDY_CLI"
      execute_commands:
        - "bdy distro route update prod-distro --domain=example.com
             --target=artifact=web-app:${execution.to_revision.short_revision}"

    - action: "Tag this version as the new stable"
      type: "BUDDY_CLI"
      execute_commands:
        - "bdy artifact tag web-app:${execution.to_revision.short_revision} stable"

Two things make this safe in practice. First, every environment receives the exact bytes that passed the build - web-app:<sha> is the same archive in staging and in production, so a green staging run is real signal about what production will do. Second, the previous artifact version is still published, so if the smoke test fails or a regression appears later, recovery is one bdy distro route update --target=artifact=web-app:stable away - no rebuild, no waiting on CI, no source-tree archaeology to figure out which commit was running when it broke. That is the whole point of artifacts: the deploy is a routing change against an immutable, addressable thing, not a hopeful re-run of the build.

Frequently asked questions

What is the difference between a build artifact and a container image?

A container image is one specific kind of build artifact - a packaged filesystem plus metadata that a container runtime can run. "Artifact" is the broader category that also covers static bundles (`dist/`), language packages (jar, wheel, npm tarball), zip archives, and OS images. Every container image is an artifact; not every artifact is a container image.

Why should build artifacts be immutable?

Immutability is what lets you promote the same package from staging to production with confidence. If `web-app:42` could be rewritten in place, the bytes that passed staging tests might not be the bytes serving production an hour later - exactly the failure mode artifact-based deploys exist to prevent. Treat every version as write-once, and use a new version number for any change.

Where are build artifacts stored?

In an artifact registry - a content-addressed store with versioning, retention policies and access control. Container images live in Docker Hub, GHCR, ECR or Artifact Registry; language packages live in npm, PyPI, Maven Central or private equivalents like JFrog Artifactory and Sonatype Nexus. Buddy ships a built-in artifact store that the CLI publishes to and the distribution routes serve from, so the build, the registry and the routing are all in one place.

Can I rebuild from source instead of keeping the artifact?

In theory yes; in practice no. Rebuilds depend on the source tree, the dependency graph and the build environment all being identical to the original run - and dependency mirrors, base images and toolchains drift constantly. Even with a lockfile, a rebuild months later often produces a subtly different binary. Keeping the artifact is faster, deterministic and the only reliable rollback path.

Missing a term? Spotted a mistake?

Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.