Skip to main content
Redhat Developers  Logo
  • Products

    Platforms

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat AI
      Red Hat AI
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • View All Red Hat Products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat Developer Hub
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat OpenShift Local
    • Red Hat Developer Sandbox

      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Secure Development & Architectures

      • Security
      • Secure coding
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • Product Documentation
    • API Catalog
    • Legacy Documentation
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Skopeo: The unsung hero of Linux container-tools

The Skopeo tool enables streamlined workflows to manage container images during development and support air-gapped deployments.

September 24, 2025
Fernando Lozano
Related topics:
ContainersLinuxOpen source
Related products:
Red Hat Enterprise Linux

Share:

    The container-tools meta-package, found on Fedora Linux and derivatives such as Red Hat Enterprise Linux, is a collection of tools designed to work with Open Container Initiative (OCI) container images. They originated in the containers project on GitHub, which was donated to the Cloud Native Computing Foundation (CNCF) and is now known as the Podman Container Tools project.

    The most famous member of the containers project is Podman. It provides a rootless container engine with a reduced attack surface. Podman also provides advanced capabilities such as integration with systemd and SELinux, quadlets, and pods.

    I love Podman—as everyone who works with containers should—but on a typical workday, I use a different member of the containers project more frequently, particularly when running my containers on a Kubernetes cluster, such as Red Hat OpenShift.

    The tool I am talking about is Skopeo, which enables you to inspect and copy container images between container registry servers along with a number of local storage formats.

    To demonstrate why Skopeo is so powerful, let me share a few examples of scenarios where Skopeo provides faster and simpler alternatives to tasks you may be used to performing with Podman. Developers used to working with Podman as just a drop-in replacement for the Docker CLI might be amazed by some of those examples.

    The commands in this article were tested against a Linux system. They should work unchanged on Windows and macOS, assuming you know how to make required changes for path names and the like. Close to the end of the article, I'll provide some comments on feature parity among the ports of Skopeo for non-Linux operating systems.

    Inspecting remote container images

    You don't need to download (pull) a container image just to check its metadata. Skopeo can fetch information about an image, such as the manifest, and display its information, without spending the time waiting to download all its image layers:

    skopeo inspect docker://quay.io/centos-bootc/centos-bootc:stream10
    {
        "Name": "quay.io/centos-bootc/centos-bootc",
        "Digest": "sha256:42fc456c6eeee3aa999c320febeaf33f62c9f0e50a37acdece90c3b264ac4bc1",
        "RepoTags": [      "00be1c9cb099184045ed54ced09433503d886460e8fdf459280eff233922eadd",
    ...

    Skopeo is able to filter and format the metadata, which is nice for scripting, or for helping you visualize only the information you actually need. For example, to check if a container image is actually a bootc container image, you would check for the presence of the containers.bootc label:

    skopeo inspect --format '{{ index .Labels "containers.bootc" }}' 
    docker://quay.io/centos-bootc/centos-bootc:stream10
    1

    By convention, all bootc container images should set this property to the value 1, which you can query by using the --format flag.

    Another example of the skopeo inspect subcommand enables you to format the output of arrays and maps as JSON, which can then be piped to the jq tool for easy visualization of the contents:

    skopeo inspect --format '{{ json .Labels }}' docker://quay.io/centos-bootc/centos-bootc:stream10 | jq
    {
      "architecture": "x86_64",
      "bootc.diskimage-builder": "quay.io/centos-bootc/bootc-image-builder",
      "build-date": "2025-09-12T07:56:38Z",
    ...

    Inspecting local container images

    The protocol designator docker:// in previous commands is Skopeo's way of stating that you want it to communicate with a remote container registry server. It could be an older registry that implements only Docker APIs or a newer registry that implements OCI standards. Skopeo and other container tools call this the transport.

    Most container tools support a number of different transports, and some of them enable reading or writing container images to local disk using either standard formats (defined by the Open Container Initiative) or proprietary formats (defined by Docker).

    For example, this Containerfile builds a trivial (and suboptimal) container image that runs a static website, powered by the Apache HTTP Server.

    FROM registry.access.redhat.com/ubi10/ubi:10.0
    RUN dnf install -y systemd httpd && \
      systemctl enable httpd && \
      dnf clean all
    RUN sed -i 's/Listen 80/Listen 8080/' /etc/httpd/conf/httpd.conf
    RUN echo "Static web site" > /var/www/html/index.html
    ENTRYPOINT ["/lib/systemd/systemd"]

    Even though I did not explicitly set any labels on my image, it actually inherited a number of labels and other metadata from its parent image, which is the Red Hat Universal Base Image (UBI). If you build the image as httpd-ubi, you can inspect its labels by using the containers-storage transport:

    skopeo inspect --format '{{ json .Labels }}' containers-storage:localhost/httpd-ubi  | jq

    In this last example, it would have actually been easier to simply use the podman inspect command. This illustrates the flexibility of Skopeo to access image metadata from sources outside of a container registry. 

    Single-command push

    When first learning about container tools, most developers are taught to push their container images to a remote registry server using a sequence of two commands:

    podman tag localhost/httpd-ubi quay.io/flozanorht/httpd-ubi:latest
    podman push quay.io/flozanorht/httpd-ubi:latest

    The first command identifies the local image as one intended for the remote registry quay.io and defines the repository and tag it should have there. The second actually copies that image to the remote location specified. Did you know you only need one command to accomplish this?

    podman push localhost/httpd-ubi quay.io/flozanorht/httpd-ubi:latest

    Or, using Skopeo:

    skopeo copy containers-storage:localhost/httpd-ubi docker://quay.io/flozanorht/httpd-ubi:latest

    Done this way, any images identified with a registry name, such as quay.io on my work machine, are images I actually downloaded from a remote registry. I prefer having a clear distinction between images I produced myself and images I downloaded.

    Also, consider the fact that at this time Containerfile builds are not reproducible. Building twice from the same Containerfile, RPM packages, and other input files produces different image layers and, consequently a different image manifest and digests. This is because files inside a container image record their last accessed time, which is usually the build time.

    Update a floating tag

    If my trivial image was built by a continuous integration (CI) system, it is recommended to tag the image with something that resembles a build number or a timestamp. The CI system (for example, Tekton) would perform a task similar to this:

    skopeo copy containers-storage:localhost/httpd-ubi docker://quay.io/flozanorht/httpd-ubi:1234

    Then, after a number of integration tests, performance tests, and security scans, my image would be promoted to a "release" image and tagged accordingly:

    skopeo copy containers-storage:localhost/httpd-ubi docker://quay.io/flozanorht/httpd-ubi:v1.0

    Better yet, the CI system would just tag the image on the remote registry, because the locally built image might not be available anymore, or the "promotion" step could be performed by a different machine than the machines which performed build and test steps.

    skopeo copy docker://quay.io/flozanorht/httpd-ubi:1234  docker://quay.io/flozanorht/httpd-ubi:v1.0

    This second approach, copying from a registry to the same registry to create a new tag, is preferable because it guarantees the images are the same for both tags. When you copy container images from one machine to another, a number of settings (for example, compression) could end up producing different image manifests.

    The source and destination registries could even be different registries: one server for use by the CI system, and another to host only release images.

    skopeo copy docker://quay.example.com/flozanorht/httpd-ubi:1234 docker://quay.io/flozanorht/httpd-ubi:v1.0

    If you wish, also tag that release image as latest:

    skopeo copy docker://quay.example.com/flozanorht/httpd-ubi:1234 docker://quay.io/flozanorht/httpd-ubi:latest

    In all of these examples, the skopeo copy command is smart enough to not copy again or overwrite image layers that already exist on the destination registry. If the copy operation is performed only to update a floating tag, the only change that is made is to an image manifest.

    Mirror container images to a disconnected container registry

    Most medium and large organizations have strict security policies that disallow downloading any software artifact from the internet. Everything must be vetted, not only for compatibility and licensing issues, but also to ensure there was no tampering and no malware is present on the software artifact. Such policies affect container images as well.

    There might be dedicated systems that have access to the internet, allowing them to automatically copy container images for internal usage in a similar way to an HTTP proxy cache. But even that might be forbidden, and the expected workflow would be:

    1. On a system connected to the internet, copy the container images to a local directory.
    2. Perform all tests and scans on the local directory, but from a system that is disconnected from the internet. That means the local directory is either removable media or a network file share.
    3. Once the container images are vetted for internal usage, copy them to an internal container registry that is accessed by internal developers and potentially also internal production systems.

    You can perform steps 1 and 3 above with the skopeo copy command using either of the transports named dir or oci. The former is intended to store a single container image in a directory. The latter is intended to store multiple container images in the same directory and includes a JSON index of all images available in the directory.

    So, on the internet-connected system:

    mkdir /var/images/httpd-ubi:v1.0
    skopeo copy docker://quay.io/flozanorht/httpd-ubi:v1.0 dir:/var/images/httpd-ubi:v1.0

    And on the disconnected system:

    skopeo copy dir:/var/images/httpd-ubi:v1.0 docker://quay.example.com/flozanorht/httpd-ubi:v1.0 

    If you need to copy several container images at once, the oci transport can simplify things:

    mkdir /var/images/webapp
    skopeo copy docker://quay.io/flozanorht/httpd-ubi:v1.0 oci:/var/images/webapp:httpd-ubi:v1.0
    skopeo copy docker://quay.io/flozanorht/php-ubi:latest oci:/var/images/webapp:php-ubi:latest

    And to publish to a container registry:

    skopeo copy oci:/var/images/webapp:httpd-ubi:v1.0 docker://quay.example.com/flozanorht/httpd-ubi:v1.0 
    skopeo copy oci:/var/images/webapp:php-ubi:latest docker://quay.example.com/flozanorht/php-ubi:latest 

    Notice that you must use colons (:) between the name of the directory and the name of the image, and another colon for the image tag.

    Warning

    Important: If you use the oci transport with images stored using older image formats, their manifests might be converted to the newer image formats, which will change their digests. Using the dir transport avoids this risk.

    Sync multiple remote images

    The skopeo sync command can be used in many scenarios, such as:

    • To copy all tags from a remote container image, which is a great way of keeping a mirror up-to-date, as only new tags and layers will be effectively copied.
    • To copy a subset of tags from one or more remote container images.

    In the former case, you specify the name of your source and destination registries, for example:

    skopeo sync docker://registry.redhat.io/ubi10/ubi docker://quay.example.com/ubi10/ubi

    In the latter case, you must provide a YAML file that describes which images to sync. For example:

    registry.redhat.io:
      images:
        ubi10/ubi:
          - latest
          - "10.0"
          - 10.0-1745487123
          - 10.0-1747220028

    There are many more variations of the syntax for specifying images, and you can check Skopeo's manual pages with the man skopeo sync command. For example, you can specify image digests or ranges of tags (to specify a range of semantic versions).

    More importantly, notice that the YAML syntax allows you to specify different source registries and different image repositories from each registry. 

    No matter how large or complex your YAML file is, you can mirror all images with a single command:

    skopeo sync --src yaml --dest docker sync.yaml quay.example.com

    Notice that you specify the transport type for source and destination using their own command-line options instead of including it as a component of the source or destination. This enables the skopeo sync command to accept the yaml transport, which other commands do not recognize.

    The skopeo sync command has some limitations compared to the skopeo copy command. For example, not all transports can be used for the destination. In the disconnected mirror scenario, I find that I can use skopeo sync to create a directory of offline container images. However, I cannot use skopeo sync to copy the source directory to my disconnected container registry. My solution in this situation is to use a script similar to the script used for the skopeo copy of multiple images.

    Skopeo on Non-Linux systems

    My only "complaint" about Skopeo is that it didn't receive the same cross-platform love from the community and its corporate sponsor as Podman did.

    It is arguably easier to port Skopeo to macOS and Windows than it is to port Podman, because outside of Linux systems, Podman also needs to maintain a Linux VM. On the other hand, there's no point porting the remaining set of the container tools unless Podman is working fine and with all its features, which it already is, by the way.

    The Skopeo releases GitHub page provides no binaries, but they are included with most Linux distributions. macOS users can use brew to install readily available binaries that are equivalent to the Linux binaries.

    Windows users can get native Windows binaries from the winskopeo repository on GitHub. All of the Skopeo capabilities are included with the exception of some more advanced features, such as handling container image signatures.

    An alternative for Windows users is to run the original Skopeo for Linux using Windows Subsystem for Linux (WSL). This is particularly useful for developers already working from WSL, but is less than ideal for developers using native Windows tools.

    There is much more to Skopeo

    This article only scratches the surface of what Skopeo can do. For example, the tool can also:

    • Convert from older image manifests to current OCI standards, so you can keep your image digests unchanged as you mirror images.
    • Convert older Docker archives (from the docker save command) to OCI standards, or just copy them to a container registry, without having to first load them into a local container engine.
    • Handle two kinds of container image signatures: the older GPG standard from early releases of Podman and the newer standard based on Sigstore.
    • Handle other kinds of artifacts that you can nowadays store in container registries, such as Helm charts and AI models.

    Not to mention that Skopeo can handle private registries that require authentication. It can also manage TLS certificates, including disabling TLS validation if desired (but this is not recommended).

    In fact, Skopeo is commonly used in continuous integration (CI) systems, be it based on Kubernetes-native Tekton pipelines (using the Skopeo container image and its associated Tekton task) or traditional pipelines (based on Jenkins, using the Skopeo binary installed in a Jenkins node).

    If you are not using Skopeo today, you are missing many opportunities to improve your container image workflows. There's more to containers than just container engines and Kubernetes, and Skopeo is a tool that should be part of your cloud-native toolbelt.

    Acknowledgements

    Thanks a lot to Andrew Block, Dan Walsh, Dave Darrah, and Miloslav Trmač for reviewing a draft of this article.

    Related Posts

    • bootc: Getting started with bootable containers

    • Working with Kubernetes in Podman Desktop

    • A Practical Introduction to Container Terminology

    • Rootless containers with Podman: The basics

    • Best practices for running Buildah in a container

    • How to transition from Docker to Podman

    Recent Posts

    • Skopeo: The unsung hero of Linux container-tools

    • Automate certificate management in OpenShift

    • Customize RHEL CoreOS at scale: On-cluster image mode in OpenShift

    • How to set up KServe autoscaling for vLLM with KEDA

    • How I used Cursor AI to migrate a Bash test suite to Python

    What’s up next?

    Read Podman in Action for easy-to-follow examples to help you learn Podman quickly, including steps to deploy a complete containerized web service.

    Get the e-book
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue