SKOPEO-SYNC(1)

πŸ’… skopeo-sync(1): sync images, bestie edition πŸ’…

🌸 NAME 🌸

skopeo-sync β€” synchronize images between registry repositories and local directories. πŸ’–

✨ SYNOPSIS ✨

skopeo sync [options] --src transport --dest transport source
destination

πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so skopeo sync synchronizes images between registry repositories and local directories. It does this by copying all the images it finds at source over to destination β€” which is lowkey perfect for syncing a local container registry mirror, or for populating registries running inside air-gapped environments. 🌈

Unlike other skopeo commands, skopeo sync makes you specify both the source and destination transports separately from source and destination. Why? Because if you prefix a destination with its transport, the registry docker://hostname:port would be wrongly interpreted as an image reference at a non-fully qualified registry, with hostname and port becoming the image name and tag. Messy. πŸ™ˆ So we keep them separate, periodt.

🚚 Available source transports

  • docker (i.e. --src docker) β€” source is a repository hosted on a container registry (e.g.: registry.example.com/busybox). If no image tag is specified, skopeo sync copies all the tags found in that repository. ✨
  • dir (i.e. --src dir) β€” source is a local directory path (e.g.: /media/usb/). Refer to skopeo(1) dir:path for the local image format. πŸ“
  • yaml (i.e. --src yaml) β€” source is a local YAML file path. The YAML file should specify the list of images copied from different container registries (local directories are not supported). Refer to EXAMPLES for the file format. πŸŽ€

🎯 Available destination transports

  • docker (i.e. --dest docker) β€” destination is a container registry (e.g.: my-registry.local.lan). 🐳
  • dir (i.e. --dest dir) β€” destination is a local directory path (e.g.: /media/usb/). One directory per source image:tag is created for each copied image. πŸ’Ύ

When the --scoped option is specified, images are prefixed with the source image path so that multiple images with the same name can be stored at destination. Slay. πŸ‘‘


✨ OPTIONS ✨

Also check out skopeo(1) for options placed before the subcommand name. πŸ‘€

  • --all, -a β€” If one of the images in src 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-arch and --override-variant options), attempt to copy all of the images in the list, and the list itself. 🫢
  • --authfile path β€” Path of the primary registry credentials file. On Linux, the default is ${XDG_RUNTIME_DIR}/containers/auth.json. See containers-auth.json(5) for more details about the credential search mechanism and defaults on other platforms. Use skopeo login to manage the credentials. The default value of this option is read from the REGISTRY_AUTH_FILE environment variable. πŸ”
  • --src-authfile path β€” Path of the primary registry credentials file for the source registry. Uses path given by --authfile, if not provided. πŸ’–
  • --dest-authfile path β€” Path of the primary registry credentials file for the destination registry. Uses path given by --authfile, if not provided. πŸ’–
  • --dry-run β€” Run the sync without actually copying data to the destination. πŸ‘€
  • --src, -s transport β€” Transport for the source repository. 🚚
  • --dest, -d transport β€” Destination transport. 🎯
  • --format, -f manifest-type β€” Manifest Type (oci, v2s1, or v2s2) to use when syncing image(s) to a destination (default is manifest type of source, with fallbacks). 🧚
  • --help, -h β€” Print usage statement. πŸ™‹
  • --scoped β€” Prefix images with the source image path, so that multiple images with the same name can be stored at destination. πŸ‘‘
  • --append-suffix tag-suffix β€” String to append to destination tags. πŸŽ€
  • --digestfile path β€” After copying the images from source, write the digest of the resulting images along with Image Reference. Like this:
sha256:bf91f90823248017a4f920fb541727fa8368dc6cf377a7debbd271cf6a31c8a7 docker://myhost.com/alpine:edge
sha256:31603596830fc7e56753139f9c2c6bd3759e48a850659506ebfb885d1cf3aef5 docker://myhost.com/postgres:14.3
  • --preserve-digests β€” Preserve the digests during copying. Fail if the digest cannot be preserved. This option does not change what will be copied; consider using --all at the same time. πŸ”₯
  • --remove-signatures β€” Do not copy signatures, if any, from source-image. This is necessary when copying a signed image to a destination which does not support signatures. 🚫
  • --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. 🎯
  • --sign-by key-id β€” Add a β€œsimple signing” signature using that key ID for an image name corresponding to destination-image. ✍️
  • --sign-by-sigstore param-file β€” Add a sigstore signature based on the options in the specified containers sigstore signing parameter file, param-file. See containers-sigstore-signing-params.yaml(5) for details about the file format. πŸ”
  • --sign-by-sigstore-private-key path β€” Add a sigstore signature using a private key at path for an image name corresponding to destination-image. πŸ”‘
  • --sign-by-sq-fingerprint fingerprint β€” Add a β€œsimple signing” signature using a Sequoia-PGP key with the specified fingerprint. 🐧
  • --sign-passphrase-file path β€” The passphrase to use when signing with --sign-by, --sign-by-sigstore-private-key or --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. 😭
  • --src-creds username[:password] β€” for accessing the source registry. πŸ”
  • --dest-creds username[:password] β€” for accessing the destination registry. πŸ”
  • --src-cert-dir path β€” Use certificates (*.crt, *.cert, *.key) at path to connect to the source registry or daemon. πŸ“œ
  • --src-no-creds β€” Access the registry anonymously. πŸ•΅οΈ
  • --src-tls-verify=bool β€” Require HTTPS and verify certificates when talking to a container source registry or daemon. Default to source registry entry in registry.conf setting. πŸ”’
  • --dest-cert-dir path β€” Use certificates (*.crt, *.cert, *.key) at path 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 a container destination registry or daemon. Default to destination registry entry in registry.conf setting. πŸ”’
  • --src-registry-token β€” Bearer token for accessing the source registry. 🎟️
  • --dest-registry-token β€” Bearer token for accessing the destination registry. 🎟️
  • --retry-times β€” The number of times to retry. By default, no retries are attempted. πŸ”
  • --retry-delay β€” Fixed delay between retries. If not set (or set to 0s), retry wait time will be exponentially increased based on the number of failed attempts. ⏳
  • --keep-going β€” If any errors occur during copying of images, those errors are logged and the process continues syncing rest of the images and finally fails at the end. πŸš€
  • --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. πŸ”‘

🌸 EXAMPLES 🌸

okay bestie, this is the receipts section πŸ’–. Every command below is copy-pasteable exactly as written β€” no cap.

πŸŽ€ Synchronizing to a local directory πŸŽ€

$ skopeo sync --src docker --dest dir registry.example.com/busybox /media/usb

Images are located at:

/media/usb/busybox:1-glibc
/media/usb/busybox:1-musl
/media/usb/busybox:1-ubuntu
...
/media/usb/busybox:latest

The ... is literally part of the list, so don’t come for me πŸ’… β€” it’s giving β€œthere are more tags, obviously.”

πŸŽ€ Synchronizing to a container registry from local πŸŽ€

Images are located at:

/media/usb/busybox:1-glibc

Sync run:

$ skopeo sync --src dir --dest docker /media/usb/busybox:1-glibc my-registry.local.lan/test/

Destination registry content:

REPO                                 TAGS
my-registry.local.lan/test/busybox   1-glibc

Local dir to registry pipeline? Ate. πŸ”₯

πŸŽ€ Synchronizing to a local directory, scoped πŸŽ€

$ skopeo sync --src docker --dest dir --scoped registry.example.com/busybox /media/usb

Images are located at:

/media/usb/registry.example.com/busybox:1-glibc
/media/usb/registry.example.com/busybox:1-musl
/media/usb/registry.example.com/busybox:1-ubuntu
...
/media/usb/registry.example.com/busybox:latest

Scoped means the registry hostname is right there in the path, so your filesystem knows exactly where these babies came from 🫢.

πŸŽ€ Synchronizing to a container registry πŸŽ€

$ skopeo sync --src docker --dest docker registry.example.com/busybox my-registry.local.lan

Destination registry content:

REPO                         TAGS
registry.local.lan/busybox   1-glibc, 1-musl, 1-ubuntu, ..., latest

Keep the REPO and TAGS exactly as shown, bestie β€” this is the vibe check and the fact check at the same time ✨.

πŸŽ€ Synchronizing to a container registry keeping the repository πŸŽ€

$ skopeo sync --src docker --dest docker registry.example.com/repo/busybox my-registry.local.lan/repo

Destination registry content:

REPO                              TAGS
registry.local.lan/repo/busybox   1-glibc, 1-musl, 1-ubuntu, ..., latest

The repo path stays the repo path: repo/busybox does not lose its main character energy. πŸ‘‘

πŸŽ€ Synchronizing to a container registry with tag suffix πŸŽ€

$ skopeo sync --src docker --dest docker --append-suffix '-mirror' registry.example.com/busybox my-registry.local.lan

Destination registry content:

REPO                         TAGS
registry.local.lan/busybox   1-glibc-mirror, 1-musl-mirror, 1-ubuntu-mirror, ..., latest-mirror

Suffixes go brrr πŸŒ€ β€” every tag gets -mirror appended, exactly like it says.

πŸŽ€ YAML file content (used source for --src yaml) πŸŽ€

Okay this is the big YAML moment 🧚. Here’s the content, verbatim:

registry.example.com:
    images:
        busybox: []
        redis:
            - "1.0"
            - "2.0"
            - "sha256:0000000000000000000000000000000011111111111111111111111111111111"
    images-by-tag-regex:
        nginx: ^1\.13\.[12]-alpine-perl$
    images-by-semver:
        alpine: ">= 3.12.0"
    credentials:
        username: john
        password: this is a secret
    tls-verify: true
    cert-dir: /home/john/certs
quay.io:
    tls-verify: false
    images:
        coreos/etcd:
            - latest

If the yaml filename is sync.yml, sync run:

$ skopeo sync --src yaml --dest docker sync.yml my-registry.local.lan/repo/

This will copy the following images:

  • Repository registry.example.com/busybox: all images, as no tags are specified.
  • Repository registry.example.com/redis: images tagged "1.0" and "2.0" along with image with digest "sha256:0000000000000000000000000000000011111111111111111111111111111111".
  • Repository registry.example.com/nginx: images tagged "1.13.1-alpine-perl" and "1.13.2-alpine-perl".
  • Repository quay.io/coreos/etcd: images tagged "latest".
  • Repository registry.example.com/alpine: all images with tags match the semantic version constraint ">= 3.12.0" ("3.12.0, "3.12.1", ... ,"4.0.0", ...)

The full list of possible semantic version comparisons can be found in the upstream library’s documentation: https://github.com/Masterminds/semver/tree/v3.2.0#basic-comparisons.

Version ordering and precedence is understood as defined here: https://semver.org/#spec-item-11.

For the registry registry.example.com, the "john"/"this is a secret" credentials are used, with server TLS certificates located at /home/john/certs.

TLS verification is normally enabled, and it can be disabled setting tls-verify to false. In the above example, TLS verification is enabled for registry.example.com, while it is disabled for quay.io.


πŸ‘€ SEE ALSO πŸ‘€

Go read these too, bestie β€” they’re all part of the same Unix friend group 🫢:

πŸ’– AUTHORS πŸ’–

  • Flavio Castelli fcastelli@suse.com ⟨mailto:fcastelli@suse.com⟩
  • Marco Vedovati mvedovati@suse.com ⟨mailto:mvedovati@suse.com⟩

xoxo, your Unix bestie πŸ’… β€” this page’s footer source/version is skopeo-sync(1)(), and the date is right there in the footer, periodt 🫢.

πŸ“¦ where this page comes from