SKOPEO-COPY(1)
π skopeo-copy(1) β the copy glow-up π
okay bestie, welcome to skopeo-copy(1), aka the one command that takes an image from here and puts it over there and doesn't lose a single crumb on the way. it's giving delivery service but for manifests π
πΈ NAME πΈ
skopeo-copy β copy an image (manifest, filesystem layers, signatures) from one location to another. that's it, that's the whole job, and it does it flawlessly π
β¨ SYNOPSIS β¨
skopeo copy [options] source-image destination-image
like. two names. source and destination. nothing else needed. iconic.
π¦ DESCRIPTION π¦
Copy an image (manifest, filesystem layers, signatures) from one location to another. everything travels together β the manifest, the layers, the signatures β like a friend group that refuses to split up π«Ά
It uses the system's trust policy to validate images, and rejects images not trusted by the policy. no cap, if the policy says no, it's a no. don't argue with the policy.
- source-image β use the "image name" format described above β¨
- destination-image β use the "image name" format described above β¨
and here's the part people get wrong like every single time, so listen up:
source-image and destination-image are interpreted completely independently β e.g. the destination name does not automatically inherit any parts of the source name.
fr fr. they're two separate names, two separate vibes, no inheritance, no hand-me-downs. you spell out the destination yourself or you get surprises. π
π OPTIONS π
also peep skopeo(1) for options placed before the subcommand name β those are the global ones, and they live upstream of this whole situation.
--additional-tag=strings β additional tags. (supportsdocker-archive.) π·οΈ--all,-aβ if source-image refers to a list of images, instead of copying just the image which matches the current OS and architecture (subject to the use of the global--override-os,--override-archand--override-variantoptions), attempt to copy all of the images in the list, and the list itself. go big or go home, bestie π₯--authfilepath β path of the primary registry credentials file. On Linux, the default is${XDG_RUNTIME_DIR}/containers/auth.json. seecontainers-auth.json(5)for more details about the credential search mechanism and defaults on other platforms. Useskopeo loginto manage the credentials. π The default value of this option is read from theREGISTRY_AUTH_FILEenvironment variable. (yes, env var, keep it in mind.)--src-authfilepath β path of the primary registry credentials file for the source registry. uses path given by--authfile, if not provided. π«Ά--dest-authfilepath β path of the primary registry credentials file for the destination registry. uses path given by--authfile, if not provided. π«Ά--dest-shared-blob-dirdirectory β directory to use to share blobs across OCI repositories. sharing is caring π--digestfilepath β after copying the image, write the digest of the resulting image to the file. lil receipt of what you just did π§Ύ--preserve-digestsβ preserve the digests during copying. fail if the digest cannot be preserved. no silent swaps, she said what she said. This option does not change what will be copied; consider using--allat the same time. π--encrypt-layerints β experimental: the 0-indexed layer indices, with support for negative indexing (e.g.0is the first layer,-1is the last layer). counting backwards is highkey a superpower π§--format,-fmanifest-type β MANIFEST TYPE (oci,v2s1, orv2s2) to use in the destination (default is manifest type of source, with fallbacks). pick your fighter β¨--help,-hβ print usage statement. the classic. π--multi-archoption β control what is copied if source-image refers to a multi-architecture image. default issystem. Options:system: copy only the image that matches the system architectureall: copy the full multi-architecture imageindex-only: copy only the indexplatform-list: copy only specific platforms (comma-separated list of OS/Architecture pairs, e.g.,linux/amd64,linux/arm64)
β οΈ theindex-onlyoption andplatform-listboth create sparse manifest lists, which usually fail unless the referenced per-architecture images are already present in the destination, or the target registry supports sparse indexes. like, usually fail. we warned you π
When specifying a platform list, all compression variants and other variations for each platform are copied. no picking and choosing inside a platform, bestie π
--quiet,-qβ suppress output information when copying images. stealth mode π€«--remove-signaturesβ do not copy signatures, if any, from source-image. Necessary when copying a signed image to a destination which does not support signatures. it's not optional-optional, it's required-optional π«--remove-list-signaturesβ do not copy the manifest list signature while preserving per-instance signatures. this provides more granular control than--remove-signatures, which removes all signatures. surgical, not scorched earth πͺβ¨--strip-removed-platformsβ when copying only a subset of platforms from a multi-architecture image (using--multi-archwith a platform list), write a manifest list containing only the copied platforms, instead of leaving entries that refer to per-platform images not present in the destination. this is useful for registries that reject manifest lists containing references to missing images. no ghosts in the list π»π«
Note: this option requires signatures to be removed using either--remove-signaturesor--remove-list-signatures, as stripping instances invalidates the manifest list signature and changes the manifest list digest. The operation will fail if signatures are present and not explicitly removed. read it twice, it's a whole thing π
--sign-bykey-id β add a βsimple signingβ signature using that key ID for an image name corresponding to destination-image. sign it, seal it, deliver it βοΈ--sign-by-sigstoreparam-file β add a sigstore signature based on the options in the specified containers sigstore signing parameter file, param-file. seecontainers-sigstore-signing-params.yaml(5)for details about the file format. π§Ύ--sign-by-sigstore-private-keypath β add a sigstore signature using a private key at path for an image name corresponding to destination-image. π--sign-by-sq-fingerprintfingerprint β add a βsimple signingβ signature using a Sequoia-PGP key with the specified fingerprint. sequoia said slay π²--sign-passphrase-filepath β the passphrase to use when signing with--sign-by,--sign-by-sigstore-private-keyor--sign-by-sq-fingerprint. Only the first line will be read. A passphrase stored in a file is of questionable security if other users can read this file. Do not use this option if at all avoidable.
π¬ that's not me being dramatic, that's the manpage. "of questionable security." "if at all avoidable." take the hint.
--sign-identityreference β the identity to use when signing the image. The identity must be a fully specified docker reference. If the identity is not specified, the target docker reference will be used. πͺͺ--src-shared-blob-dirdirectory β directory to use to share blobs across OCI repositories. yes, the source version of the sharing thing up there π--encryption-keyprotocol:keyfile β specifies the encryption protocol, which can beJWE(RFC7516),PGP(RFC4880), andPKCS7(RFC2315) and the key material required for image encryption. For instance,jwe:/path/to/key.pemorpgp:admin@example.comorpkcs7:/path/to/x509-file. πβ¨--decryption-keykey[:passphrase] β key to be used for decryption of images. Key can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and omitted otherwise. ποΈ--src-credsusername[:password] β credentials for accessing the source registry. π«Ά--dest-compressβ compress tarball image layers when saving to directory using the'dir'transport. (default is same compression type as source). squeeze it ποΈ--dest-decompressβ decompress tarball image layers when saving to directory using the'dir'transport. (default is same compression type as source). un-squeeze it π«§--dest-oci-accept-uncompressed-layersβ allow uncompressed image layers when saving to an OCI image using the'oci'transport. (default is to compress things that aren't compressed). let the layers be free π--dest-credsusername[:password] β credentials for accessing the destination registry. π«Ά--src-cert-dirpath β use certificates at path (*.crt,*.cert,*.key) to connect to the source registry or daemon. π--src-no-credsβ access the registry anonymously. incognito mode, no name no face πΆοΈ--src-tls-verify=bool β require HTTPS and verify certificates when talking to container source registry or daemon. Default to source registry setting. don't turn this off unless you know exactly what you're doing, bestie π--dest-cert-dirpath β use certificates at path (*.crt,*.cert,*.key) to connect to the destination registry or daemon. π--dest-no-credsβ access the registry anonymously. πΆοΈ--dest-tls-verify=bool β require HTTPS and verify certificates when talking to container destination registry or daemon. Default to destination registry setting. π--src-daemon-hosthost β copy from docker daemon at host. If host starts withtcp://, HTTPS is enabled by default. To use plain HTTP, use the formhttp://(default isunix:///var/run/docker.sock). π³--dest-daemon-hosthost β copy to docker daemon at host. If host starts withtcp://, HTTPS is enabled by default. To use plain HTTP, use the formhttp://(default isunix:///var/run/docker.sock). π³--dest-compress-formatformat β specifies the compression format to use. Supported values are:gzip,zstdandzstd:chunked.zstd:chunkedis incompatible with encrypting images, and will be treated aszstdwith a warning in that case. read that again if you're encrypting π«--dest-compress-levelformat β specifies the compression level to use. The value is specific to the compression algorithm used, e.g. for zstd the accepted values are in the range1-20(inclusive), while for gzip it is1-9(inclusive). different numbers for different algorithms, don't mix them up π’--dest-force-compress-formatβ ensures that the compression algorithm set in--dest-compress-formatis used exclusively. no backups, no substitutes, only the one you picked π--src-registry-tokentoken β bearer token for accessing the source registry. ποΈ--dest-registry-tokentoken β bearer token for accessing the destination registry. ποΈ--dest-precompute-digestsβ precompute digests to ensure layers are not uploaded that already exist on the destination registry. Layers with initially unknown digests (ex. compressing "on the fly") will be temporarily streamed to disk. doing the math first so you don't upload the same layer twice β efficiency icon π§ β¨--retry-timesβ the number of times to retry. By default, no retries are attempted. zero. none. first try is the only try unless you say otherwise π€--retry-delayβ fixed delay between retries. If not set (or set to0s), retry wait time will be exponentially increased based on the number of failed attempts. backoff era π--src-usernameβ the username to access the source registry. π--src-passwordβ the password to access the source registry. π€--dest-usernameβ the username to access the destination registry. π--dest-passwordβ the password to access the destination registry. π€--image-parallel-copiesn β maximum number of image layers to be copied (pulled/pushed) simultaneously. Not setting this field will fall back tocontainers/imagedefaults. multitasking, but make it a number πββοΈπββοΈπββοΈ
π« EXAMPLES π«
Ok bestie, this is the part where it all clicks. Real commands, real copy-paste energy. π«Ά
β¨ just... copy a thing β¨
Wanna yeet an image from one registry to another? That's literally it:
$ skopeo copy docker://quay.io/skopeo/stable:latest docker://registry.example.com/skopeo:latest
No drama, no local daemon, just vibes and bytes moving. π
π copy layers to a local directory π
Say you want the actual layers of the docker.io busybox image just sitting in a dir on your machine. Make the dir first (it's giving responsible):
$ mkdir -p /var/lib/images/busybox
$ skopeo copy docker://busybox:latest dir:/var/lib/images/busybox
$ ls /var/lib/images/busybox/*
/tmp/busybox/2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749.tar
/tmp/busybox/manifest.json
/tmp/busybox/8ddc19f16526912237dd8af81971d5e4dd0587907234be2b83e249518d5b673f.tar
So you end up with the .tar layers plus manifest.json chilling together. Iconic. π¦
π¦ the docker-archive moment π¦
Wanna build an archive that docker load can eat? Bet:
$ skopeo copy docker://busybox:latest docker-archive:archive-file.tar:busybox:latest
β οΈ heads up though β the manpage is very clear on this: using a registry is almost always more efficient than doing the archive thing. So like, it works, but it's not the main character here. π
π copy AND sign π
Feeling cryptography-coded today? Sign it on the way through:
$ skopeo copy --sign-by dev@example.com containers-storage:example/busybox:streaming docker://example/busybox:gold
Signed, sealed, slayed. βοΈ
π multi-arch: pick your platforms π
Copy only specific platforms out of a multi-architecture image β this creates a sparse manifest list:
$ skopeo copy --multi-arch=linux/amd64,linux/arm64 docker://quay.io/skopeo/stable:latest docker://registry.example.com/skopeo:latest
And if your registry is being a hater and doesn't support sparse lists? Strip it and drop the list signatures:
$ skopeo copy --multi-arch=linux/amd64,linux/arm64 --strip-removed-platforms --remove-list-signatures docker://quay.io/skopeo/stable:latest docker://registry.example.com/skopeo:latest
Fixed. Periodt. π₯
π encrypt an image π
First, get your image into oci: land:
$ skopeo copy docker://docker.io/library/nginx:1.17.8 oci:local_nginx:1.17.8
Then generate yourself a key pair, because we're doing security today:
$ openssl genrsa -out private.key 1024
$ openssl rsa -in private.key -pubout > public.key
And now encrypt with the public key:
$ skopeo copy --encryption-key jwe:./public.key oci:local_nginx:1.17.8 oci:try-encrypt:encrypted
Obsessed with how clean that is. π§
π decrypt an image π
Got the private key? Then you get the image back:
$ skopeo copy --decryption-key ./private.key oci:try-encrypt:encrypted oci:try-decrypt:decrypted
π copy it still encrypted π
Sometimes you don't even wanna peek inside. Just move it, encrypted as-is:
$ skopeo copy oci:try-encrypt:encrypted oci:try-encrypt-copy:encrypted
Zero decryption, zero curiosity. Respect. π
π decrypt with multiple keys π
Some encrypted images need more than one key to unlock. Stack those --decryption-key flags like the absolute legend you are:
$ skopeo copy --decryption-key ./private1.key --decryption-key ./private2.key --decryption-key ./private3.key oci:try-encrypt:encrypted oci:try-decrypt:decrypted
Three keys, one copy. It's giving heist movie. π
β partial encryption (yes, really) β
Container images can also be partially encrypted β you do it by specifying the index of the layer. And this is the important bit, so listen up:
- Layers are 0-indexed indices
- Negative indexing is supported too
- So
0is the first layer, and-1is the last layer
Let's say the image docker.io/library/nginx:1.17.8 is made up of 3 layers, and out of those 3 you only want to encrypt the 2nd layer:
$ skopeo copy --encryption-key jwe:./public.key --encrypt-layer 1 oci:local_nginx:1.17.8 oci:try-encrypt:encrypted
See how the 2nd layer is 1? That's the 0-indexing doing its thing. π§ β¨
π SEE ALSO π
Go read these next, iykyk:
skopeo(1)skopeo-login(1)docker-login(1)containers-auth.json(5)containers-policy.json(5)containers-transports(5)containers-signature(5)
π AUTHORS π
Absolute icons who made this happen, put some respect on their names:
- Antonio Murdaca
runcom@redhat.comβ¨mailto:runcom@redhat.comβ© - Miloslav Trmac
mitr@redhat.comβ¨mailto:mitr@redhat.comβ© - Jhon Honce
jhonce@redhat.comβ¨mailto:jhonce@redhat.comβ©
That's the whole page bestie! π The footer on this one is just skopeo-copy(1) with a big ol' empty () where a version and date would normally go β so no version stamp, no date stamp, just the page name doing its thing. Cute, mysterious, leaves you wanting more. ππ«
Now go copy something. Slay. π¦π₯
π¦ where this page comes from
- arch rolling: skopeo 1.24.1-1 Β· Apache-2.0
- nixpkgs unstable: skopeo 1.24.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms