cloudparts

runc

The reference OCI runtime: a small Linux command-line tool that turns a bundle into a running container, and the default runtime underneath containerd and Docker Engine.

Linux Foundation project
What it is
Containers
Type
Open source
Made by
Open Container Initiative (Linux Foundation), donated by Docker
Foundation
The Linux Foundation, joined June 2015. Part of the Open Container Initiative
License
Apache 2.0
Facts checked
28 October 2026

What it is

runc does one job: given an OCI bundle, a directory with a root filesystem and a config.json, it creates the container the configuration describes and runs its process. It sets up Linux namespaces, cgroups (v1 or v2, directly or through systemd), mounts, capabilities, seccomp filters and AppArmor or SELinux labels, then starts the program and gets out of the way. Its commands follow the OCI Runtime Specification’s lifecycle: runc create builds the container without starting the program, runc start runs it, and runc kill, runc delete and runc state follow; runc run does create and start in one go. It also runs rootless containers as an ordinary user and checkpoints and restores running containers with CRIU. It is written mostly in Go, runs only on Linux, and its own README says it is “a low level tool not designed with an end user in mind”: it does not pull images, set up networks or keep logs.

Those jobs belong to the engines above it. Docker Engine hands containers to containerd, and containerd, the most common runtime behind Kubernetes nodes, uses runc by default through its io.containerd.runc.v2 shim. CRI-O switched its default to crun, a runtime written in C, in version 1.31 (September 2024), and Podman picks crun first when both are installed. Since runc 1.3 (April 2025) a new minor release comes about every six months, with published end-of-support dates: 1.5.0 arrived in June 2026 and 1.5.2 in September, 1.6.0 is due at the end of October 2026, and support for 1.3 ends the same month. Because every container on a host is started by the same runc binary with host privileges, its security record matters: high-severity container escapes were fixed in 2019, 2021, 2024 and three at once in November 2025, and runc can now be built with libpathrs, a Rust library for safe path handling.

Why teams pick it

  • The reference implementation of the OCI Runtime Specification, and the default runtime in containerd and so in Docker Engine and many Kubernetes nodes: the most widely exercised Linux container runtime
  • Covers the Linux container feature set: cgroup v2 and the systemd cgroup driver, rootless containers, seccomp, AppArmor and SELinux, and checkpoint/restore with CRIU
  • Predictable releases since 2025: a minor version about every six months, with published support windows for older branches
  • Signed releases and an independent security audit by Cure53

Watch out for

  • Containers share the host kernel and runc runs with host privileges, so a runc or kernel bug can mean an escape; for untrusted code, gVisor or Kata Containers add a second boundary
  • Only the lowest layer: no image pulling, networking, logging or restart policies, so you almost always use it through containerd, Docker, CRI-O or Podman
  • Linux only, and written in Go, which the CRI-O maintainers cite when choosing crun for faster starts and lower memory use
  • Older branches drop off quickly under the six-month cadence: the 1.2 branch and earlier are already unsupported, and 1.3 support ends in October 2026

Alternatives

What all containers have in common →Compare side by side →

How it fits with other technologies

Run a container with runc alone, from its README (needs root, or `runc spec --rootless`) · bash

mkdir -p /mycontainer/rootfs && cd /mycontainer

# fill the root filesystem, here by exporting busybox from Docker
docker export $(docker create busybox) | tar -C rootfs -xvf -

runc spec                 # writes a template config.json for the bundle
runc run mycontainerid    # create + start, attached to the terminal

# or step by step, as an engine does
runc create mycontainerid && runc start mycontainerid
runc list
runc kill mycontainerid KILL && runc delete mycontainerid

Notable container escapes fixed in runc#

All high severity. Each one let code inside a container, or a malicious image, reach the host.

CVEPublishedWhat it allowed
CVE-2019-5736February 2019A container running as root could overwrite the host’s runc binary through /proc/self/exe and gain root on the host; affected runc up to 1.0-rc6 and Docker before 18.09.2
CVE-2021-30465May 2021Swapping a mount destination for a symlink at the right moment put mounts outside the container’s root filesystem
CVE-2024-21626January 2024A leaked file descriptor gave a new process a working directory on the host filesystem, through runc exec or a malicious image
CVE-2025-31133, -52565, -52881November 2025Mount races around masked paths and /dev/console, and redirected writes to procfs, allowed escapes and denial of service

See it in a real stack

No architecture uses runc yet. Drop it into the playground and wire it up yourself.Open playground →
Where it came from2014–2026

Docker first started containers with LXC. In Docker 0.9, on March 10, 2014, it made its own library the default: libcontainer, a Go library that called the kernel’s container features directly, which the team said “drastically reduces the number of moving parts” and protected Docker from differences between LXC versions and distributions.

On June 22, 2015, when Docker, CoreOS and others announced the Open Container Project, Docker donated a command-line wrapper around libcontainer as runC, with all the “plumbing” Docker used to talk to the kernel, to be the reference implementation of a common runtime standard. libcontainer moved into the runc repository. The first 1.0 release candidate came in June 2016, but runc 1.0.0 only shipped on June 22, 2021, after a long run of release candidates.

Over the same years runc became the layer nearly every Linux container engine called. The CVE-2019-5736 escape in February 2019 showed what that meant: one bug exposed Docker, Kubernetes and other platforms at once. Minor releases stayed irregular (1.1 in January 2022, 1.2 in October 2024) until the maintainers adopted a six-month cadence with published support windows, starting with 1.3 in April 2025.

Why it’s built this way

Call the kernel directly instead of going through LXC
LXC behaved differently across versions and distributions. A Go library managing namespaces, cgroups, capabilities and AppArmor itself removed that dependency and the bugs that came with it.
A separate binary started per container
Engines start runc to set up a container and let it exit, so the engine can be upgraded or restarted without touching running containers, and any OCI-compatible runtime can replace runc.
Reference implementation of an open specification
Donating the runtime to the OCI and tying it to the Runtime Specification let other runtimes, such as crun, youki, gVisor and Kata Containers, accept the same bundles, so engines aren’t bound to one implementation.

Timeline

  1. 2014

    Docker 0.9 makes libcontainer its default instead of LXC (March 10)

  2. 2015

    Docker donates runC to the new Open Container Project (June 22)

  3. 2019

    CVE-2019-5736: a container can overwrite the host’s runc binary (February)

  4. 2021

    runc 1.0.0 released (June 22)

  5. 2024

    CRI-O makes crun its default instead of runc (1.31, September)

  6. 2025

    runc 1.3 starts a six-month release cadence (April); three escapes fixed (November)

  7. 2026

    runc 1.5.0 (June); 1.6.0-rc.1 (October 6)

Sources14 · facts checked 28 October 2026

Improve this page on GitHub