Library / SDK
containers/crun avatar
containers/crun

crun: the C OCI runtime for Podman and memory-tight containers

A fast and lightweight fully featured OCI runtime and C library for running containers

4,136 stars449 forksCGPL-2.0

At a glance

What is it?
crun is an OCI container runtime written in C, usable both as a command line tool and as a library. It fits teams that want a smaller memory footprint than runc, and it is the runtime Podman can be pointed at with --runtime.
Who is it for?
Adopt crun if you run Podman or another OCI engine on Linux and care about per-container memory overhead, or if you want to embed an OCI runtime as a library instead of spawning an external process for every container. Skip it if you need a Windows or macOS runtime, or if you want a language you can patch without touching C, autotools and libseccomp.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What crun solves, and who it is for

crun implements the OCI Container Runtime specifications, the same specification runc implements. The README frames the project as an answer to a specific complaint: most tools in the Linux container ecosystem are written in Go, and the author argues C is a better fit for a tool that sits this low in the stack. The README states that runc re-execs itself and uses a module written in C to set up the environment before the container process starts. crun removes that split. It is a single C binary that does the whole job.

The second audience is programmatic. The README says crun aims to be usable as a library that can be included in programs without requiring an external process for managing OCI containers. That is a different integration model from the usual one, where an engine forks a runtime binary per container. If you are building something that creates containers, libcrun is the part of the project aimed at you, and the repository ships a libcrun.lds linker script and a separate COPYING.libcrun licence file for it.

Neither of those goals is about features. crun is not trying to be a better container engine. It is a runtime: it takes an OCI bundle and starts a process under the isolation the config asks for. Everything above that, image pulling, networking, storage, belongs to the engine that calls it.

How crun sits between the engine and the kernel

The data flow is the OCI one. An engine such as Podman prepares a bundle: a directory with a config.json describing the process, mounts, namespaces, cgroups, capabilities and seccomp rules, plus a root filesystem. It then invokes a runtime binary with that bundle path. crun parses the config, sets up the namespaces and cgroup, applies the seccomp filter, and execs the container process. The README lists libseccomp, libcap, systemd and json-c among the build dependencies, which matches that job: seccomp filters, capabilities, cgroup delegation through systemd, and JSON parsing of the OCI config.

The build-time detail worth knowing is that crun does not hand-write its config parser. The repository contains a libocispec submodule, and the README notes that Python is needed only by libocispec to generate the C parser at build time and is not used afterwards. So the OCI spec types are generated from the specification rather than maintained by hand. That is a deliberate trade: you need Python and a submodule checkout to build, and in exchange the parser tracks the spec.

A second binary appears in the repository root: krun.1 and krun.1.md sit alongside crun.1 and crun.1.md, so the project ships man pages for a second command as well. The README does not describe krun, so treat that as something to read the man page for rather than infer from the repository listing.

Installing crun from source on Ubuntu

crun has no homepage and the README points at the releases page for official artifacts. The documented path is a source build with autotools. On Ubuntu, the README lists this dependency set:

bash
sudo apt-get install -y make git gcc build-essential pkgconf libtool \
   libsystemd-dev libprotobuf-c-dev libcap-dev libseccomp-dev libjson-c-dev \
   go-md2man autoconf python3 automake

After that, the README gives a three-command build. Note that the repository uses a git submodule for libocispec, so clone with --recursive or the generated parser will not be there:

bash
git clone --recursive https://github.com/containers/crun.git
cd crun
./autogen.sh
./configure
make

To install into the default prefix, which the README gives as /usr/local:

bash
sudo make install

One configure flag matters. The README states that the default build does not enable shared libraries, so libcrun is unavailable. If you want to link against it, the same instructions say to change the configure line:

bash
./configure --enable-shared

For a first real use, point an engine at the binary. The README's own example uses Podman with an explicit runtime path and a memory limit, and shows crun succeeding where runc fails at 4M:

bash
podman --runtime /usr/bin/crun run --rm --memory 512k fedora echo it works

What you should see is the container printing it works. The point of the example is not the output, it is that the memory ceiling is low enough that the container still starts.

The memory ceiling claim and what it actually measures

The README's performance section reports an elapsed time for running 100 containers sequentially, each running /bin/true: 0:01.69 for crun against 0:3.34 for runc, which the table expresses as -49.4%. That is the author's machine and the author's measurement, not a benchmark suite, and the README does not publish the hardware, kernel or configuration behind it. Read it as an order-of-magnitude signal from the maintainer, not as a number you can quote in a capacity plan.

The more interesting claim is the memory one, because it is demonstrated rather than tabulated. The README shows runc failing under podman with --memory 4M and an error reading /proc for a pid that no longer exists, while crun starts the same container under --memory 512k. That is an eightfold difference in the ceiling at which the example still works. The README's explanation is that crun requires fewer resources, so stricter limits are possible. The mechanism is not spelled out beyond that, but the design follows from it: a C runtime that does not re-exec itself has less resident state to account for when the kernel decides whether the container fits.

There is a practical consequence that cuts the other way. If you set limits that tight, you are budgeting for the runtime's own footprint inside the container's cgroup. That is fine when it works and confusing when it does not, because the failure looks like a container that will not start rather than a runtime that is too large.

Where crun is the wrong choice

The first boundary is the platform. The dependency lists in the README are Fedora, RHEL and CentOS Stream 9 and 10, Ubuntu, Alpine and Tumbleweed. Every install path is a Linux package manager or a Linux source build, and the static build produces an x86_64/amd64 ELF binary for glibc. Nothing in the README describes a Windows or macOS runtime. If that is your target, crun is not the tool.

The second boundary is the build itself. crun is C with autotools, a libocispec submodule, libseccomp, libcap, systemd and json-c. That is a normal stack for a systems component and an unusual one for a platform team that has standardised on Go. If your team cannot patch a C codebase, adopting crun means depending on upstream for anything that goes wrong inside the runtime, and the runtime is the component closest to the kernel.

The third is scope. crun runs containers. It does not build images, manage networks, or pull from a registry. Choosing crun does not replace your engine, it replaces the runtime underneath it, and the engine has to support being pointed at a different one. The README's example does exactly that with podman --runtime. If your engine has no such switch, crun is not installable in a useful way for you.

Finally, the Tumbleweed instructions carry a warning the other distributions do not: you must pass libseccomp's header location as a compiler flag, via ./configure CFLAGS='-I/usr/include/libseccomp'. If your distribution is not one of the six listed, expect to work that out yourself.

crun and runc: same spec, different implementation

The honest alternative is runc, and the difference is not a feature list. Both implement the OCI runtime specification, so an OCI bundle that works with one should describe the same container to the other. The split is in the implementation language and the process model. runc is Go and, per the README, re-execs itself and relies on a C module to set up the environment before the container process starts. crun is C end to end and does not re-exec, which is the stated basis for the smaller footprint and the tighter memory limits.

The second difference is embeddability. runc is a binary you invoke. crun is also a binary, but the project explicitly aims at being a library that a program can include without an external process per container. That is a real architectural choice, not a performance tweak: a library integration means no fork and no exec for the runtime itself, at the cost of linking your program against libcrun and its GPL-2.0-or-later terms.

If you are choosing between them for a Podman deployment, the README's own example is the comparison: same engine, same command, different --runtime path, and a memory ceiling that one meets and the other does not. If you are choosing for a language-ecosystem reason, runc is Go and crun is C, and that decides more day-to-day questions than any benchmark.

Licence, release verification and upgrade cost

crun is GPL-2.0, and the repository carries two licence files: COPYING and COPYING.libcrun. The existence of a separate file for libcrun, together with the libcrun.lds linker script, tells you the library is treated as a distinct artefact with its own terms. If you intend to link libcrun into a proprietary program, that separate file is the one to read with your own counsel. Nothing here is legal advice, and the README does not discuss linking exceptions.

Releases are signed. The README states that all release artifacts are signed by one of the keys in crun.keyring in the repository root, and gives the verification commands:

bash
gpg --no-default-keyring --keyring ./crun.gpg --import crun.keyring
gpg --no-default-keyring --keyring ./crun.gpg --verify crun-1.29.1.tar.zst.asc crun-1.29.1.tar.zst

That is a complete verification path from the README, which is more than many projects of this size document. The most recent releases listed are 1.28 on 2026-05-27, 1.29 on 2026-08-07 and 1.29.1 on 2026-08-13, and the last push to the repository was on 2026-09-23. The cadence is roughly a release every few months, so upgrading means tracking a moving target with a small number of versions per year, not continuous churn.

The upgrade cost that actually bites is the build environment, not the runtime. Because the OCI parser is generated by libocispec at build time, a rebuild after a submodule update can change generated code even when crun's own sources are unchanged. Pin the submodule revision along with the crun version, and verify the signed tarball rather than tracking main.

Editorial conclusion

Adopt crun if you run Podman or another OCI engine on Linux and care about per-container memory overhead, or if you want to embed an OCI runtime as a library instead of spawning an external process for every container. Skip it if you need a Windows or macOS runtime, or if you want a language you can patch without touching C, autotools and libseccomp. Before rolling it out, check that your engine accepts a runtime path, verify your kernel and libseccomp version against the configure step, and decide whether you need libcrun at all, since the default build leaves shared libraries off.

Frequently asked questions

Does Podman use runc or crun?

The README does not state which runtime Podman defaults to. It documents passing an explicit runtime path, as in podman --runtime /usr/bin/crun run --rm --memory 512k fedora echo it works, which implies the runtime is selectable rather than fixed.

What are the differences between crun and runc container runtimes?

Both implement the OCI Container Runtime specifications. runc is written in Go and, according to the README, re-execs itself and uses a C module to set up the environment before the container process starts; crun is written in C and does not re-exec, which the README presents as the reason it is faster and has a lower memory footprint.

What is crun?

crun is an OCI container runtime written in C. The README describes it as a fast, low-memory-footprint runtime that conforms to the OCI Container Runtime specifications and can also be used as a library without an external process for managing OCI containers.

How do I install crun on Ubuntu?

Install the dependency list from the README with apt-get, including libsystemd-dev, libcap-dev, libseccomp-dev and libjson-c-dev, then clone the repository with --recursive and run ./autogen.sh, ./configure and make, followed by sudo make install for the default /usr/local prefix.

Does crun support running containers with a very low memory limit?

The README gives an example where runc fails under podman --memory 4M while crun starts the same container under --memory 512k. Those are the README's numbers on the author's machine, not a published benchmark.

How do I verify a crun release artifact?

The README states that all release artifacts are signed by one of the keys in crun.keyring in the repository root, and shows importing that keyring and running gpg --verify against the .asc file next to the tarball.

Official sources

  1. containers/crun on GitHub
  2. Issues
  3. License: GPL-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/containers-crun.svg)](https://hysenlabs.com/projects/containers-crun)