What is OCI? The Container Image Standard Explained Simply
Learn what OCI (Open Container Initiative) is, what it standardizes, and why it matters for DevOps engineers. Covers image format, runtime spec, distribution spec, and practical tools like skopeo.
If you have ever wondered why a Docker image built on your laptop runs fine on Kubernetes with containerd — even though Docker and containerd are completely different projects — the answer is OCI. Understanding OCI takes about 15 minutes and saves a lot of confusion when things break.
Before OCI: The Docker Lock-in Problem
When Docker launched in 2013, the container image format was a proprietary Docker format. To run a "Docker container" you needed the Docker runtime. This was fine when Docker was the only player, but by 2015 rkt (by CoreOS), lxc, and others were trying to build container runtimes and they could not agree on a shared format.
The Linux Foundation stepped in and created the Open Container Initiative (OCI) in June 2015. Docker donated the container image format spec as the starting point.
What OCI Standardizes
OCI defines three specifications:
1. Image Format Specification — how a container image is structured (layers, manifests, config JSON). Any tool that produces an OCI image can be run by any OCI-compliant runtime.
2. Runtime Specification (runc) — how to actually run a container (filesystem setup, namespace creation, cgroups, lifecycle hooks). runc is the reference implementation and is what containerd and CRI-O call under the hood.
3. Distribution Specification — how to push and pull images from a registry (HTTP API for manifests and blobs). Any OCI-compliant registry (Docker Hub, ECR, GCR, Quay, Harbor) speaks the same protocol.
How OCI Image Layers Work
An OCI image is a stack of read-only layers plus a config file:
Image manifest (JSON)
├── config.json ← env vars, entrypoint, labels
├── layer 1 ← base OS (sha256:abc...)
├── layer 2 ← app dependencies (sha256:def...)
└── layer 3 ← your app code (sha256:ghi...)
When you run the image, the runtime (containerd/runc) downloads these layers, stacks them using an overlay filesystem (overlayfs), and adds a writable layer on top. Layers are content-addressed by SHA256 digest — this is why the same layer is never downloaded twice across different images.
Check the layers of any image:
docker image inspect nginx:latest --format '{{json .RootFS.Layers}}'Output:
[
"sha256:2a1e4e1b....",
"sha256:8e4b9e3a....",
"sha256:0f2d0c2c...."
]Why This Matters in Practice
Podman builds Docker-compatible images:
podman build -t myapp:latest .
podman push myapp:latest docker://registry.example.com/myapp:latestThat image runs on Kubernetes with containerd — zero changes needed because they all speak OCI.
CRI-O on Kubernetes pulls from Docker Hub: Kubernetes used to require Docker as the runtime. Since Kubernetes 1.24 removed dockershim, clusters use containerd or CRI-O directly. Both are OCI-compliant runtimes, so they pull and run OCI images from any OCI registry without Docker installed.
Buildah builds images without a daemon:
buildah from ubuntu:22.04
buildah run mycontainer -- apt-get install -y curl
buildah commit mycontainer myapp:latest
buildah push myapp:latestNo Docker daemon, no root required. Output is a valid OCI image.
skopeo: Copy OCI Images Between Registries
skopeo is the most useful OCI tool most engineers do not know about. It copies images between any OCI-compliant registry without pulling them locally (no docker pull + docker push needed).
Install:
# Ubuntu/Debian
sudo apt install skopeo
# macOS
brew install skopeoCopy from Docker Hub to ECR:
aws ecr get-login-password --region ap-south-1 | \
skopeo login --username AWS --password-stdin \
123456789012.dkr.ecr.ap-south-1.amazonaws.com
skopeo copy \
docker://nginx:1.25 \
docker://123456789012.dkr.ecr.ap-south-1.amazonaws.com/nginx:1.25This streams the image layers directly between registries — nothing is stored on your machine.
Inspect an image without pulling it:
skopeo inspect docker://nginx:latestOutput:
{
"Name": "docker.io/library/nginx",
"Digest": "sha256:...",
"RepoTags": ["1.25", "1.25.3", "latest", "mainline"],
"Architecture": "amd64",
"Os": "linux",
"Layers": ["sha256:...", "sha256:...", "sha256:..."],
"LayersData": [...]
}Copy between two private registries (air-gapped migration):
skopeo copy \
docker://old-registry.company.com/myapp:v2.1 \
docker://new-registry.company.com/myapp:v2.1 \
--src-creds user:pass \
--dest-creds user:passOCI Artifacts: Beyond Container Images
The OCI distribution spec is not limited to container images. Helm charts, WASM modules, and even Terraform modules can be stored in OCI registries as "OCI artifacts."
Push a Helm chart to an OCI registry:
helm package ./mychart
helm push mychart-0.1.0.tgz oci://registry.example.com/helm-chartsPull and install:
helm install myapp oci://registry.example.com/helm-charts/mychart --version 0.1.0The same registry API, the same authentication, the same layer caching — for any artifact type.
Summary: What to Remember
- OCI standardizes image format, runtime, and distribution — not Docker's problem to solve anymore
- Any OCI image runs on any OCI runtime (containerd, CRI-O, podman, runc)
- Any OCI registry (ECR, GCR, Docker Hub, Harbor) speaks the same API
skopeois the best tool for moving images between registries without a daemon- OCI artifacts extend this to Helm charts, Wasm, and anything else
The next time someone asks "will this Docker image work on our Kubernetes cluster?" the answer is: if it is built to OCI spec (and anything built with Docker is), yes.
Learn More
- OCI Spec GitHub — the actual specs, surprisingly readable
- Skopeo docs — full usage guide
- Harbor — self-hosted OCI registry with scanning, replication, and RBAC
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Kind vs K3d vs Minikube — Best Local Kubernetes in 2026
Three tools for running Kubernetes on your laptop. They're not all equal — kind is fastest for CI, k3d is lightest for dev, minikube has the best feature set. Here's the full comparison.
What Is a Container Runtime? Explained Simply (2026)
Every container needs a runtime to actually run. Docker has one. Kubernetes has one. They're different. Here's what a container runtime is, why it matters, and how containerd, runc, CRI-O, and Docker relate to each other.
What Are Linux cgroups and Namespaces? The Foundation of Containers Explained
Docker and Kubernetes containers are built on Linux cgroups and namespaces. Understanding these fundamentals helps you debug container issues and set resource limits properly.