| 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 artifact — add, 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:
local files
↓
podman artifact add
↓
local artifact store
↓
podman artifact push
↓
OCI registry
↓
podman artifact pull
↓
local artifact store
↓
extract or mount in a containerpodman 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:
podman artifact --helpSample output on Podman 5.8.2:
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 artifactsRecord your version — artifact flags evolved quickly between 5.5, 5.6, and 5.8:
podman --versionSample output:
podman version 5.8.2Podman 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:
printf '%s\n' 'application configuration' > app.confAdd it to the local artifact store under a name you will push or mount later:
podman artifact add localhost/example/config:v1 app.confSample output (artifact digest):
3b75cf9e572184feb16d462969513c9a10b088caa40d3122b86b48afb2b4edd9The 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:
podman artifact lsSample output:
REPOSITORY TAG DIGEST CREATED SIZE
localhost/example/config v1 3b75cf9e5721 Less than a second ago 26BColumns map to repository path, tag, truncated digest, age, and total artifact size.
Inspect an artifact
Pull full metadata for one artifact:
podman artifact inspect localhost/example/config:v1Sample output (trimmed):
{
"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:
podman artifact inspect --format '{{.Digest}}' localhost/example/config:v1Sample output:
sha256:3b75cf9e572184feb16d462969513c9a10b088caa40d3122b86b48afb2b4edd9Set org.opencontainers.image.title
The title annotation names a blob for extract and mount operations. Add it at creation time:
podman artifact add --annotation org.opencontainers.image.title=app.conf localhost/example/config:v2 app.confWhen 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:
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.jsonInspect the manifest layers:
podman artifact inspect localhost/example/config-bundle:v1Each 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:
podman artifact add --type application/vnd.example.config --file-type text/plain localhost/example/config-typed:v1 app.confInspect what Podman stored:
podman artifact inspect localhost/example/config-typed:v1Sample fields from this host:
"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:
printf '%s\n' 'extra setting' > extra.conf
podman artifact add --append localhost/example/config-bundle:v1 extra.confThe 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:
printf '%s\n' 'replaced configuration' > new-app.conf
podman artifact add --replace localhost/example/config:v1 new-app.confInspect 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:
# /etc/containers/registries.conf.d/999-lab-insecure.conf
[[registry]]
location = "localhost:5000"
insecure = trueCreate the artifact with the registry name you intend to push:
podman artifact add localhost:5000/demo/config:v1 app.confPush to the registry:
podman artifact push localhost:5000/demo/config:v1Sample output:
Getting image source signatures
Copying blob sha256:402a6822e5afdc1d8c044d7241ae22b6ff590cb146f07adbf854477f14ae7476
Copying config sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
Writing manifest to image destinationTLS 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:
podman artifact rm localhost:5000/demo/config:v1Confirm it disappeared from the local store:
podman artifact lsPull it back from the registry:
podman artifact pull localhost:5000/demo/config:v1Sample output matches push — blob and manifest copy lines, then success. Inspect again and the digest matches the pre-removal value:
podman artifact inspect --format '{{.Digest}}' localhost:5000/demo/config:v1Sample output:
sha256:049a22c49bebe8d7a2ff199964d1374cf796a5feab99ac563bbe4638b42e56f8Extract an OCI artifact
Single blob to a file
Extract one artifact to a path on disk:
podman artifact extract localhost:5000/demo/config:v1 ./restored.confextract exits silently on success. Read the file to confirm:
cat restored.confSample output:
application configurationMultiple blobs to a directory
Extract every blob from a bundle:
mkdir -p extracted
podman artifact extract localhost/example/config-bundle:v1 extracted/List what appeared — filenames come from title annotations when present:
ls extracted/Sample output:
app.conf extra.conf feature-flags.json logging.confExtract one blob by title
Pull a single file out of a bundle:
podman artifact extract --title app.conf localhost/example/config-bundle:v1 ./app-from-title.confSample content:
application configurationExtract one blob by digest
Select an immutable blob when several layers exist:
podman artifact extract --digest sha256:402a6822e5afdc1d8c044d7241ae22b6ff590cb146f07adbf854477f14ae7476 localhost:5000/demo/config:v1 ./from-digest.confUse 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 blob —
dstis treated as the mounted file path - Non-existent destination, multiple blobs —
dstis 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:
podman run --rm --mount type=artifact,src=localhost:5000/demo/config:v1,dst=/config registry.access.redhat.com/ubi9/ubi-minimal:latest ls -l /configSample output:
-rw-r--r--. 1 root root 26 Aug 23 00:09 /configRead the mounted content:
podman run --rm --mount type=artifact,src=localhost:5000/demo/config:v1,dst=/config registry.access.redhat.com/ubi9/ubi-minimal:latest cat /configSample output:
application configurationArtifact 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:
podman run --rm --mount type=artifact,src=localhost/example/config-bundle:v1,dst=/config registry.access.redhat.com/ubi9/ubi-minimal:latest ls /configSample output:
app.conf logging.confSelect one blob with title= — Podman mounts it as a single file at dst:
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 /configSample output:
application configurationMount by digest
Pin the mount to one immutable blob:
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 /configSample output:
application configurationUse 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 whendstis a directory - If
dstis 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=:
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 /configSample output:
-rw-r--r--. 1 root root 26 Aug 23 00:14 /configTo use name=myapp.conf, mount into an existing directory such as /mnt:
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 /mntSample output:
-rw-r--r--. 1 root root 26 Aug 23 00:14 myapp.confOCI 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:
podman artifact rm localhost/example/config:v2Remove by digest when you track immutable IDs:
podman artifact rm sha256:049a22c49bebe8d7a2ff199964d1374cf796a5feab99ac563bbe4638b42e56f8Clear the entire local artifact store:
podman artifact rm --allOnly 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
- podman-artifact(1) — subcommands and options
- podman-artifact-add(1) — add, append, replace, annotations
- podman-artifact-push(1) — registry push
- podman-run(1) — mount type=artifact — runtime artifact mounts
- Red Hat documentation — Building, running, and managing containers — RHEL Podman overview
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.

