skopeo copy: Move Images Between Registries
skopeo copy streams an image from a source transport to a destination transport without pulling it into a local daemon.
Mirroring images between registries is a common CI job. skopeo copy does it directly, registry to registry, with no docker pull/push round trip and no daemon.
What it does
skopeo copy reads an image from a source transport (for example docker://) and writes it to a destination transport, copying manifests and blobs directly. Because it never unpacks into a daemon, it runs in a plain container or a rootless CI runner.
Common usage
# registry to registry, no local pull
skopeo copy docker://docker.io/library/nginx:1.25 docker://ghcr.io/acme/nginx:1.25
# with per-side credentials
skopeo copy \
--src-creds "$SRC_USER:$SRC_PASS" \
--dest-creds "$DST_USER:$DST_PASS" \
docker://registry.example.com/app:sha docker://ghcr.io/acme/app:sha
# copy every arch of a multi-arch index
skopeo copy --all docker://alpine:3.20 docker://ghcr.io/acme/alpine:3.20Options
| Flag | What it does |
|---|---|
| --src-creds user:pass | Credentials for the source registry |
| --dest-creds user:pass | Credentials for the destination registry |
| --all | Copy the whole manifest list (every architecture), not just one |
| --src-tls-verify=false | Skip TLS verification on the source (insecure registries) |
| --dest-tls-verify=false | Skip TLS verification on the destination |
| --format oci|v2s2 | Output manifest format (OCI or Docker schema 2) |
| --retry-times N | Retry failed blob transfers N times |
In CI
skopeo copy is the canonical way to promote an image between registries without pulling it locally: no docker pull then docker push, no daemon, and it works rootless. Prefer --src-creds/--dest-creds (or skopeo login) over a shared docker config when the two sides use different accounts.
Common errors in CI
"unauthorized: authentication required" means missing or wrong credentials for that side; set --src-creds/--dest-creds or run skopeo login. "manifest unknown" means the source tag or digest does not exist. "error copying image: ... x509: certificate signed by unknown authority" on an internal registry needs --src-tls-verify=false or a mounted CA. "Requested but not honored" on --all against a single-arch image is harmless.