| Tested on | Red Hat Enterprise Linux 10.2 (Coughlan) |
|---|---|
| Package | podman-5.8.2-5.el10_2.x86_64 |
| Applies to | Any Linux host with Podman installed |
| Privilege | Rootful examples on the lab host; rootless uses the same push syntax with a separate local image store |
| Scope | Publishing local images and manifest lists with podman push and podman manifest push, registry naming for Docker Hub and Quay, private-registry TLS boundaries, --digestfile, retries, brief --authfile usage, transport overview, verification by pull, and podman image scp for direct transfer. Does not cover registry deployment, login internals, multi-architecture image builds, or image signing. |
| Related guides | Pull images with podman pull Manage images with podman images |
You already built or pulled an image into local Podman storage. podman push uploads that image — its layers and manifest — to a remote registry so other hosts, CI jobs, or orchestrators can pull it. This guide walks through the two common push shapes, registry-specific naming, flags that help in production, and podman image scp when a registry is not the right hop.
Push an image to a registry
Start from a local reference you can see in storage:
podman images localhost/myapp:v1Sample output:
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/myapp v1 5dd467fce50b 2 years ago 787 kBThe destination must include the registry hostname. A name like myapp:v1 alone is not enough — Podman needs to know which registry API to contact:
registry.example.com/project/myapp:v1The tag-first workflow creates that remote-shaped name locally, then pushes it:
podman tag localhost/myapp:v1 registry.example.com:5001/project/myapp:v1podman tag adds a second name for the same image ID; it does not copy layers. Push the tagged reference:
podman push registry.example.com:5001/project/myapp:v1Sample output:
Getting image source signatures
Copying blob sha256:2114fc8b70586b9325dde6fd26066d9951414dcdfb3995f41d51d1995cf3bd9d
Copying config sha256:5dd467fce50b56951185da365b5feee75409968cbab5767b9b59e325fb2ecbc0
Writing manifest to image destinationEach Copying blob line is a layer upload (or a registry-side blob reuse). Writing manifest to image destination means the tag on the registry now points at the manifest you just published.
Push without retagging locally
Podman accepts separate source and destination arguments, so you do not need another local tag just to spell the remote path:
podman push localhost/myapp:v1 registry.example.com:5001/project/myapp:v2-directThe push transcript matches the tag-first flow — same blobs, same manifest write line. Think of the arguments as:
source image → local storage reference (what you have now)
destination → target registry reference (where it should land)| Workflow | Best when |
|---|---|
| Tag first, then push | You want the remote name to stay in podman images for later runs |
Source + destination on one push |
One-off publication or scripts that should not clutter local tags |
The local localhost/myapp:v1 image is unchanged either way; only the registry receives a new tag.
Authenticate before pushing
Registries that require credentials reject anonymous pushes. Log in to the registry host first — see Log in to a container registry for auth.json paths, --password-stdin, and rootless vs rootful stores:
podman login registry.example.com:5001After Login Succeeded!, retry the push. Podman reads stored credentials for that registry hostname on each layer upload.
Push to Docker Hub
Docker Hub lives at the docker.io registry. User images use your Docker ID as the namespace:
docker.io/USERNAME/myapp:v1Example:
podman push localhost/myapp:v1 docker.io/USERNAME/myapp:v1Replace USERNAME with your Docker Hub account. The docker.io/library/... path is reserved for official-library-style images on Docker Hub; personal and organization images use docker.io/<namespace>/....
Log in with podman login docker.io (or podman login with no host, which defaults to Docker Hub) before pushing. Rate limits and credential storage behave the same as for pulls — only the direction changes.
Push to Quay
Red Hat Quay uses the quay.io hostname:
podman push localhost/myapp:v1 quay.io/USERNAME/myapp:v1Log in with podman login quay.io, then push. Organization projects extend the repository path (quay.io/myorg/myapp:v1) the same way as pulls — the registry hostname and full repository path must match what Quay expects for your account.
Push to a private registry
Private registries often include a port and a project path:
podman push localhost/myapp:v1 registry.example.com:5000/project/myapp:v1When the registry presents a certificate signed by your organization's CA, install that CA into the system or container trust store and keep TLS verification enabled. Configure a private registry covers lab and production registry setup; this page assumes the registry already exists and is reachable.
Use --tls-verify=false only in a controlled lab
Some lab registries use self-signed TLS or plain HTTP. Podman can skip certificate verification for a single push:
podman push --tls-verify=false localhost/myapp:v1 localhost:5000/myapp:v1That flag is appropriate only when you deliberately accept insecure transport — for example a throwaway registry on loopback during learning. For a real private registry:
- Install the registry CA where Podman trusts it
- Configure
registries.confwhen the registry is insecure HTTP - Leave TLS verification on for production pushes
Persistent trust and registries.conf entries belong in the private-registry guide, not repeated here.
Point push at a specific auth file
When credentials live outside the default runtime auth.json, pass the file explicitly:
podman push --authfile ~/.config/containers/private-auth.json localhost/myapp:v1 registry.example.com:5001/project/myapp:v1The same path works for podman login --authfile and podman pull --authfile. CI jobs often set REGISTRY_AUTH_FILE to the temporary credentials file the pipeline created so every registry command in the job reads one store. Full auth-file layout and helper configuration stay in the login article — here the point is that push honors the same overrides as pull.
Retry failed pushes
Transient network blips or brief registry outages can interrupt a push. Podman can retry automatically:
podman push --retry 5 localhost/myapp:v1 registry.example.com:5001/project/myapp:v1Add a delay between attempts when the registry asks you to back off:
podman push --retry 5 --retry-delay 10s localhost/myapp:v1 registry.example.com:5001/project/myapp:v1Retries help with transport and temporary server errors. They do not fix:
- Missing or wrong login (
authentication required,unauthorized) - Repository permission denied
- Unknown CA or hostname TLS failures
- Registry policy rejection (quota, immutability rules)
Fix credentials, trust, or repository settings first; then retries matter.
Capture the remote digest with --digestfile
Tags move; digests identify exact manifest content. After a successful push, write the remote manifest digest to a file:
podman push --digestfile pushed-digest.txt localhost/myapp:v1 registry.example.com:5001/project/myapp:v3-digestRead what was recorded:
cat pushed-digest.txtSample output:
sha256:ccfc769b27e3717ea97b8262cd3e6f88cb2327ebf33e2f97275667a30149264dThat value is the digest of the manifest that landed on the registry — useful for release pipelines, comparing what you published against what a cluster pulled, and building an immutable reference:
registry.example.com:5001/project/myapp@sha256:ccfc769b27e3717ea97b8262cd3e6f88cb2327ebf33e2f97275667a30149264dThe tag v3-digest can still be moved to a different build later; only the @sha256:... form pins content.
Verify the pushed image
The straightest check is to remove the local copy of the remote tag and pull it back:
podman rmi registry.example.com:5001/project/myapp:v1If Podman refuses because a container still references the image, remove or retag that container first. Pull from the registry:
podman pull registry.example.com:5001/project/myapp:v1Sample output:
Trying to pull registry.example.com:5001/project/myapp:v1...
Getting image source signatures
Copying blob sha256:ce980a8f5545faa3125a489aad32c00d6cf13d80a302308c3963b524085657af
Copying config sha256:5dd467fce50b56951185da365b5feee75409968cbab5767b9b59e325fb2ecbc0
Writing manifest to image destination
5dd467fce50b56951185da365b5feee75409968cbab5767b9b59e325fb2ecbc0The image ID should match the source you pushed (5dd467fce50b in this lab). Skopeo can inspect a remote manifest without a full pull when it is installed; this lab verified with pull only.
Push a multi-architecture manifest list
When you already built a manifest list locally — see Create multi-architecture images for construction — publish it with podman manifest push:
podman manifest inspect localhost/myapp-multi:v1Sample output (truncated):
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f",
"platform": {
"architecture": "amd64",
"os": "linux"
}
}
]
}Push the index and its member images (default --all behavior):
podman manifest push localhost/myapp-multi:v1 docker://registry.example.com:5001/project/myapp-multi:v1Sample output:
Getting image list signatures
Copying 1 images generated from 1 images in list
Copying image sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f (1/1)
Getting image source signatures
Copying blob sha256:2114fc8b70586b9325dde6fd26066d9951414dcdfb3995f41d51d1995cf3bd9d
Copying config sha256:5dd467fce50b56951185da365b5feee75409968cbab5767b9b59e325fb2ecbc0
Writing manifest to image destination
Writing manifest list to image destination
Storing list signaturespodman manifest push is the dedicated command for publishing and controlling manifest lists or OCI indexes. Plain podman push can also push a manifest list or image index, but this guide uses podman manifest push for the explicit multi-architecture workflow.
podman push transports
Registry pushes are the common case, but Podman speaks several destination transports. Prefix or scheme tells the copy engine where blobs should land:
| Destination | Typical use |
|---|---|
docker://REGISTRY/... |
Container registry (explicit transport; optional when the name is unambiguous) |
oci-archive:/path/file.tar |
OCI layout tar archive on disk |
docker-archive:/path/file.tar |
Legacy Docker save tarball |
dir:/path |
containers/image directory transport |
Archive and directory exports are covered in Save, load, export and import images. This article stays on registry publication; the table is here so a docker:// prefix on podman manifest push is not surprising.
Copy an image between hosts with podman image scp
podman image scp transfers images between Podman stores by saving an archive, transferring it when necessary, and loading it at the destination. Remote-host copies typically use SSH; same-host rootful/rootless transfers do not require sshd. Syntax from Podman 5.8.2:
podman image scp [options] IMAGE [HOST::]Copy into rootful storage on the same host (rootless → rootful on localhost):
sudo podman image scp localhost/myapp:v1 root@localhost::localhost/myapp-scp:v1Sample output:
Copying blob sha256:2114fc8b70586b9325dde6fd26066d9951414dcdfb3995f41d51d1995cf3bd9d
Copying config sha256:5dd467fce50b56951185da365b5feee75409968cbab5767b9b59e325fb2ecbc0
Writing manifest to image destination
Getting image source signatures
Copying blob sha256:2114fc8b70586b9325dde6fd26066d9951414dcdfb3995f41d51d1995cf3bd9d
Copying config sha256:5dd467fce50b56951185da365b5feee75409968cbab5767b9b59e325fb2ecbc0
Writing manifest to image destination
Loaded image: localhost/myapp:v1Confirm the image arrived under the new name in the destination store:
sudo podman images localhost/myapp-scp:v1Sample output:
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/myapp-scp v1 5dd467fce50b 2 years ago 787 kBRemote host copies use the same HOST:: suffix — for example podman image scp myimage:latest user@server:: pushes from local storage to the remote host's default Podman socket. Rootful-to-rootless on one machine follows the documented sudo podman image scp root@localhost::IMAGE user@localhost:: pattern from the man page.
Compare podman image scp and registry push
podman image scp |
Registry push |
|
|---|---|---|
| Path | Direct host-to-host over SSH | Central registry API |
| Setup | SSH access to the destination | Running registry + login |
| Scale | Few hosts, ad hoc | Many consumers, CI/CD |
| Distribution model | Archive transfer / load | Content-addressed registry blobs |
| Digest workflow | Not a substitute for registry promotion | Normal production path |
Because image scp uses save/transfer/load semantics, it is a poor fit when you need exact registry blob promotion or a single canonical digest across environments. Use a registry when anything beyond a one-off copy depends on that image.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
manifest unknown after push or pull |
Wrong repository path or tag does not exist on that registry | Double-check hostname, port, project path, and tag spelling |
| Pushed the wrong image | Source reference typo | podman images; verify the first argument to podman push |
| Push OK but deployment unchanged | Mutable tag; node did not re-pull | Pin @sha256:... from --digestfile or enforce pull-on-deploy in the orchestrator |
References
- podman-push(1) — push options, transports, retries, digest file
- podman-manifest-push(1) — manifest list publication
- podman-image-scp(1) — secure copy between hosts
- Red Hat documentation — Transferring container images with
skopeoandpodman— scp and registry workflows on RHEL
Summary
podman push publishes a local image to a registry when the destination reference includes the registry hostname. You can tag first or pass source and destination on one command; both paths upload the same layers. Log in before pushing to private registries, Quay, or Docker Hub, and keep TLS verification on everywhere except deliberate lab setups.
--digestfile captures the manifest digest the registry stored — the hook for immutable deploy references — while --retry and --retry-delay smooth over transient failures that are not auth or trust problems. Manifest lists go up with podman manifest push, and verifying with podman pull confirms the tag resolves on the far side.
When SSH is available but a registry is not, podman image scp transfers an image between Podman stores by save/load semantics, including rootless-to-rootful copies on one host. For anything that looks like production distribution, prefer registry push so consumers share one canonical repository and digest story.

