Manage OCI Artifacts with `podman artifact`

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
Applies to Linux hosts with Podman 5.6 or later
Privilege Normal user for local artifact commands; registry push may need login or administrator registry configuration
Scope OCI artifacts with podman artifactadd, ls, inspect, push, pull, extract, rm, annotations and media types, --append and --replace, registry round-trip, extract by title or digest, and --mount type=artifact. Does not cover OCI specification theory, full registry setup, image signing, SBOM generation, or Quadlet .artifact unit depth.
Related guides Push images to a registry
Build images with Podman
Podman volumes

An OCI registry can distribute more than runnable container images. Configuration bundles, policy documents, model weights, SBOM files, and other application assets can travel through the same push and pull machinery — without becoming image layers you have to extract or rebuild around.

podman artifact is Podman's CLI for that workflow: package local files into the artifact store, push them to a registry, pull them back, extract blobs to disk, or mount them directly into containers.


What is an OCI artifact?

Think of the flow as:

text
local files
podman artifact add
local artifact store
podman artifact push
OCI registry
podman artifact pull
local artifact store
extract or mount in a container

podman artifact add does not create a runnable container image. It writes an OCI artifact manifest and blobs into Podman's separate artifact store. You manage artifacts with podman artifact, not podman images.


Check artifact support on your host

Confirm the subcommand exists before following the rest of this guide:

bash
podman artifact --help

Sample output on Podman 5.8.2:

output
Available Commands:
  add         Add an OCI artifact to the local store
  extract     Extract an OCI artifact to a local path
  inspect     Inspect an OCI artifact
  ls          List OCI artifacts
  pull        Pull an OCI artifact
  push        Push an OCI artifact
  rm          Remove one or more OCI artifacts

Record your version — artifact flags evolved quickly between 5.5, 5.6, and 5.8:

bash
podman --version

Sample output:

output
podman version 5.8.2

Podman 5.5 documentation carried an explicit experimental warning for podman artifact. That warning is absent from 5.6-era documentation, so treat 5.6+ as the practical baseline for new work.


Create your first OCI artifact

Create a sample configuration file:

bash
printf '%s\n' 'application configuration' > app.conf

Add it to the local artifact store under a name you will push or mount later:

bash
podman artifact add localhost/example/config:v1 app.conf

Sample output (artifact digest):

output
3b75cf9e572184feb16d462969513c9a10b088caa40d3122b86b48afb2b4edd9

The command returns a digest, not a container ID. Nothing in podman images changes — the artifact lives in a separate store.


List artifacts

List what is in the local artifact store:

bash
podman artifact ls

Sample output:

output
REPOSITORY                TAG         DIGEST        CREATED                 SIZE
localhost/example/config  v1          3b75cf9e5721  Less than a second ago  26B

Columns map to repository path, tag, truncated digest, age, and total artifact size.


Inspect an artifact

Pull full metadata for one artifact:

bash
podman artifact inspect localhost/example/config:v1

Sample output (trimmed):

output
{
    "Digest": "sha256:3b75cf9e572184feb16d462969513c9a10b088caa40d3122b86b48afb2b4edd9",
    "Manifest": {
        "layers": [
            {
                "annotations": {
                    "org.opencontainers.image.title": "app.conf"
                },
                "digest": "sha256:402a6822e5afdc1d8c044d7241ae22b6ff590cb146f07adbf854477f14ae7476",
                "mediaType": "application/vnd.oci.empty.v1+json",
                "size": 26
            }
        ]
    }
}

Fields worth scripting against include digest, manifest layers, per-blob media types, annotations, and sizes. For automation, print only the digest:

bash
podman artifact inspect --format '{{.Digest}}' localhost/example/config:v1

Sample output:

output
sha256:3b75cf9e572184feb16d462969513c9a10b088caa40d3122b86b48afb2b4edd9

Set org.opencontainers.image.title

The title annotation names a blob for extract and mount operations. Add it at creation time:

bash
podman artifact add --annotation org.opencontainers.image.title=app.conf localhost/example/config:v2 app.conf

When you later extract or mount with title=app.conf, Podman selects that blob from a multi-file artifact. You do not need a full tour of OCI annotations — this one key is the filename handle you will use most often.


Add multiple files to one artifact

Pass more than one path to pack a bundle:

bash
printf '%s\n' 'logging level=info' > logging.conf
printf '%s\n' '{"feature_x":true}' > feature-flags.json
podman artifact add localhost/example/config-bundle:v1 app.conf logging.conf feature-flags.json

Inspect the manifest layers:

bash
podman artifact inspect localhost/example/config-bundle:v1

Each input file becomes a separate layer. Podman can associate a title with each layer, and org.opencontainers.image.title is the annotation used by extract and mount operations to identify filenames.


Set artifact and file media types

Describe the artifact and blob content with explicit media types:

bash
podman artifact add --type application/vnd.example.config --file-type text/plain localhost/example/config-typed:v1 app.conf

Inspect what Podman stored:

bash
podman artifact inspect localhost/example/config-typed:v1

Sample fields from this host:

output
"artifactType": "application/vnd.example.config"
...
"mediaType": "text/plain"

--type describes the overall artifact. --file-type sets the layer media type for the file content. Use vendor-specific types for your own bundles — do not invent standardized media types for arbitrary application content and expect every registry tool to understand them.


Append files to an existing artifact

Add another file to an existing bundle without recreating the reference from scratch:

bash
printf '%s\n' 'extra setting' > extra.conf
podman artifact add --append localhost/example/config-bundle:v1 extra.conf

The digest changes and inspect shows an additional layer (extra.conf alongside the original files). On 5.8.2, --append adds blobs to the existing manifest. --append cannot be combined with --type or --replace — use --append to extend the existing artifact; use --replace when you want to replace it entirely.


Replace an artifact

Overwrite an existing artifact name with new content:

bash
printf '%s\n' 'replaced configuration' > new-app.conf
podman artifact add --replace localhost/example/config:v1 new-app.conf

Inspect confirms the old app.conf layer is gone and new-app.conf is the sole blob. --replace is the right flag when the tag should point at entirely new content rather than an appended bundle.


Push an artifact to a registry

This lab uses a local HTTP registry on localhost:5000. Podman speaks HTTPS to registries by default, so mark the lab endpoint insecure before push:

toml
# /etc/containers/registries.conf.d/999-lab-insecure.conf
[[registry]]
location = "localhost:5000"
insecure = true

Create the artifact with the registry name you intend to push:

bash
podman artifact add localhost:5000/demo/config:v1 app.conf

Push to the registry:

bash
podman artifact push localhost:5000/demo/config:v1

Sample output:

output
Getting image source signatures
Copying blob sha256:402a6822e5afdc1d8c044d7241ae22b6ff590cb146f07adbf854477f14ae7476
Copying config sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
Writing manifest to image destination

TLS registries, login, and trust configuration follow the same patterns as image push — see Configure a private registry when you move beyond a local HTTP lab.


Remove the local copy and pull it back

Prove registry round-trip by deleting only the local artifact:

bash
podman artifact rm localhost:5000/demo/config:v1

Confirm it disappeared from the local store:

bash
podman artifact ls

Pull it back from the registry:

bash
podman artifact pull localhost:5000/demo/config:v1

Sample output matches push — blob and manifest copy lines, then success. Inspect again and the digest matches the pre-removal value:

bash
podman artifact inspect --format '{{.Digest}}' localhost:5000/demo/config:v1

Sample output:

output
sha256:049a22c49bebe8d7a2ff199964d1374cf796a5feab99ac563bbe4638b42e56f8

Extract an OCI artifact

Single blob to a file

Extract one artifact to a path on disk:

bash
podman artifact extract localhost:5000/demo/config:v1 ./restored.conf

extract exits silently on success. Read the file to confirm:

bash
cat restored.conf

Sample output:

output
application configuration

Multiple blobs to a directory

Extract every blob from a bundle:

bash
mkdir -p extracted
podman artifact extract localhost/example/config-bundle:v1 extracted/

List what appeared — filenames come from title annotations when present:

bash
ls extracted/

Sample output:

output
app.conf  extra.conf  feature-flags.json  logging.conf

Extract one blob by title

Pull a single file out of a bundle:

bash
podman artifact extract --title app.conf localhost/example/config-bundle:v1 ./app-from-title.conf

Sample content:

output
application configuration

Extract one blob by digest

Select an immutable blob when several layers exist:

bash
podman artifact extract --digest sha256:402a6822e5afdc1d8c044d7241ae22b6ff590cb146f07adbf854477f14ae7476 localhost:5000/demo/config:v1 ./from-digest.conf

Use podman artifact inspect to read layer digests. The inspect template for the first layer digest is {{(index .Manifest.Layers 0).Digest}}.


Mount an artifact directly into a container

Mounting avoids writing an extra copy on the host — the container reads from the local artifact store. The artifact must already exist locally (add or pull first).

How dst is interpreted depends on whether the path already exists and how many blobs you mount:

  • Existing directory — the blob is placed inside using its title or digest as the filename
  • Existing file — the blob mounts directly over that file
  • Non-existent destination, single blobdst is treated as the mounted file path
  • Non-existent destination, multiple blobsdst is treated as a directory

For a single-blob artifact, a non-existent dst is treated as the mounted file path. If dst already exists as a directory, Podman places the blob inside that directory using its title or digest as the filename.

Mount a single-blob artifact at /config:

bash
podman run --rm --mount type=artifact,src=localhost:5000/demo/config:v1,dst=/config registry.access.redhat.com/ubi9/ubi-minimal:latest ls -l /config

Sample output:

output
-rw-r--r--. 1 root root 26 Aug 23 00:09 /config

Read the mounted content:

bash
podman run --rm --mount type=artifact,src=localhost:5000/demo/config:v1,dst=/config registry.access.redhat.com/ubi9/ubi-minimal:latest cat /config

Sample output:

output
application configuration

Artifact mounts are read-only. They suit packaged inputs you pull from a registry — not writable application state.


Mount one blob by title

Multi-blob artifacts mount as a directory when you omit title:

bash
podman run --rm --mount type=artifact,src=localhost/example/config-bundle:v1,dst=/config registry.access.redhat.com/ubi9/ubi-minimal:latest ls /config

Sample output:

output
app.conf  logging.conf

Select one blob with title= — Podman mounts it as a single file at dst:

bash
podman run --rm --mount type=artifact,src=localhost/example/config-bundle:v1,dst=/config,title=app.conf registry.access.redhat.com/ubi9/ubi-minimal:latest cat /config

Sample output:

output
application configuration

Mount by digest

Pin the mount to one immutable blob:

bash
podman run --rm --mount type=artifact,src=localhost:5000/demo/config:v1,dst=/config,digest=sha256:402a6822e5afdc1d8c044d7241ae22b6ff590cb146f07adbf854477f14ae7476 registry.access.redhat.com/ubi9/ubi-minimal:latest cat /config

Sample output:

output
application configuration

Use digest= when a bundle contains several blobs and you need an exact layer, not just a title match.


Override the mounted filename with name=

The name= option changes how a selected blob appears inside the mount. On Podman 5.6+, the behavior is documented:

  • For a single blob, name= changes the filename only when dst is a directory
  • If dst is a direct file target, name= is ignored
  • For multi-blob artifacts, names become <name>-0, <name>-1, and so on

Because /config does not exist and only one blob is selected, Podman treats /config as the file target and ignores name=:

bash
podman run --rm \
  --mount type=artifact,src=localhost/example/config-bundle:v1,dst=/config,title=app.conf,name=myapp.conf \
  registry.access.redhat.com/ubi9/ubi-minimal:latest \
  ls -l /config

Sample output:

output
-rw-r--r--. 1 root root 26 Aug 23 00:14 /config

To use name=myapp.conf, mount into an existing directory such as /mnt:

bash
podman run --rm \
  --mount type=artifact,src=localhost/example/config-bundle:v1,dst=/mnt,title=app.conf,name=myapp.conf \
  registry.access.redhat.com/ubi9/ubi-minimal:latest \
  ls -l /mnt

Sample output:

output
-rw-r--r--. 1 root root 26 Aug 23 00:14 myapp.conf

OCI artifact vs container image

Both use OCI registries and manifests, but they solve different problems:

Container image OCI artifact
Content Root filesystem + image config Arbitrary file blobs
Runnable Yes — podman run IMAGE No — not a container rootfs
List command podman images podman artifact ls
Pull command podman pull podman artifact pull
Storage Image store Artifact store
Typical use Deploy workloads Distribute config, models, policy, signatures

Artifact mount vs volume or bind mount

Artifact mount Volume Bind mount
Source Local artifact store (often pulled from a registry) Podman-managed volume Host directory or file
Mutability Read-only Read-write persistent data Host-controlled
Distribution Push/pull through OCI registry Local to host Local to host
Best for Versioned inputs, bundles, registry-delivered assets Database files, writable app data Developer trees, existing host paths

Artifacts complement images — they do not replace volumes for persistent writable storage.


Remove artifacts

Remove one artifact by name:

bash
podman artifact rm localhost/example/config:v2

Remove by digest when you track immutable IDs:

bash
podman artifact rm sha256:049a22c49bebe8d7a2ff199964d1374cf796a5feab99ac563bbe4638b42e56f8

Clear the entire local artifact store:

bash
podman artifact rm --all

Only the local artifact store is affected. Registry copies remain until deleted at the registry.


Quadlet .artifact units

Quadlet can pull artifacts declaratively before a workload starts — an .artifact unit runs podman artifact pull into the local store. That workflow is experimental on Podman 5.8.2 and documented in Podman Quadlet build, image, and artifact. This page covers the CLI; Quadlet wiring stays there.


Troubleshooting

Symptom Likely cause Fix
podman artifact unknown Podman older than 5.6 Upgrade Podman or use image-based distribution instead
Push/pull HTTPS error on localhost:5000 HTTP registry, HTTPS client Set insecure = true for the lab location in registries.conf.d
artifact does not exist on mount Not in local store podman artifact add or pull first
cat /config/app.conf fails on single-blob mount dst is the file itself Use cat /config when the artifact has one layer
Empty directory after bundle mount Wrong artifact name or single-blob artifact Confirm podman artifact ls; use title= for one blob
--append or --replace error Invalid flag combination --append cannot be used with --type or --replace; use one mode per add

References


Summary

podman artifact lets you treat ordinary files like registry-distributable OCI content. Add local paths into the artifact store, inspect manifests and digests, push and pull through the same registry infrastructure you already use for images, then extract blobs to disk or mount them with --mount type=artifact.

The mount behavior is worth remembering: an existing directory receives blobs by title or digest filename; a non-existent dst with one blob becomes that file path; multi-blob mounts without a single-file target use directory semantics. Use title= or digest= to select one layer from a bundle, and name= only when dst is a directory. Annotations such as org.opencontainers.image.title tie human-readable names to blobs for extract and mount.

This is a 5.6+ feature set that matured on 5.8.2 — verify podman artifact --help on your hosts before baking automation around flags that changed early in the 5.x line. For declarative pulls, pair the CLI with experimental Quadlet .artifact units; for TLS registries and authentication, reuse your existing registry setup rather than relearning it here.


Frequently Asked Questions

1. What is the difference between a Podman image and an OCI artifact?

A container image packages a root filesystem and image config for running containers. An OCI artifact packages arbitrary file blobs in an OCI manifest — configuration bundles, policy files, models, or other assets. Images appear in podman images; artifacts appear in podman artifact ls.

2. Which Podman version supports podman artifact?

The podman artifact subcommand is available on Podman 5.6 and later. Podman 5.5 documentation marked it experimental; that warning is absent from 5.6-era docs. This guide targets Podman 5.8.2.

3. How do I mount an OCI artifact into a running container?

Add or pull the artifact into the local artifact store, then use podman run --mount type=artifact,src=ARTIFACT,dst=/path. A non-existent dst with one blob mounts as that file path; multiple blobs mount as a directory. An existing dst directory receives blobs by title or digest filename. Use title= to select one blob from a bundle.

4. Does podman artifact push work with a plain HTTP registry?

Podman defaults to HTTPS for registry endpoints. For a lab HTTP registry on localhost:5000, mark the location insecure in registries.conf or use a TLS registry as described in the private registry guide.

5. Does removing a local artifact delete it from the registry?

No. podman artifact rm only removes the copy from Podman's local artifact store. The registry blob and manifest remain until you delete them at the registry.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)