skopeo copy: Move Images Between Registries
skopeo copy moves a container image from one location to another without needing a Docker daemon.
skopeo works directly with registries and image transports, so it can mirror or promote images in CI without pulling them into a local daemon. copy is its most-used command.
What it does
skopeo copy transfers an image between two transports, each named with a prefix such as docker:// (a registry), oci: (an OCI layout), or dir: (a directory). It streams layers between source and destination, optionally copying every architecture in a manifest list with --all.
Common usage
skopeo copy docker://docker.io/library/alpine:3.19 \
docker://ghcr.io/org/alpine:3.19
# copy a full multi-arch manifest list
skopeo copy --all docker://src/app:ci docker://dst/app:ci
# with per-registry credentials
skopeo copy --dest-creds "$USER:$TOKEN" \
docker://src/app:ci docker://ghcr.io/org/app:ciOptions
| Flag | What it does |
|---|---|
| --all | Copy every image in a manifest list (multi-arch) |
| --src-creds / --dest-creds | user:password for each side |
| --dest-tls-verify=false | Disable TLS verification on the destination |
| --format oci|v2s2 | Output manifest format |
| --retry-times <n> | Retry transient transport failures |
In CI
Use skopeo copy to promote an image between registries (for example dev to prod) without a daemon, which suits rootless and ephemeral runners. Add --all so multi-arch images are copied whole, and --retry-times to ride out flaky registry connections.
Common errors in CI
"unauthorized: authentication required" means missing or wrong creds; use --src-creds/--dest-creds or skopeo login. "manifest unknown" means the source tag does not exist. "x509: certificate signed by unknown authority" on an internal registry needs the CA in the trust store or, for testing only, --dest-tls-verify=false.