crun(1)
๐ธ crun โ a fast and lightweight OCI runtime ๐ธ
okay so this is crun(1) from the General Commands Manual, and bestie, it's literally the whole vibe for running Linux containers in the OCI (Open Container Initiative) format. Let's get into it ๐
โจ NAME โจ
crun โ a fast and lightweight OCI runtime. no notes, that's the whole pitch ๐ซถ
๐ฆ SYNOPSIS ๐ฆ
crun [global options] command [command options] [arguments...]
๐ DESCRIPTION ๐
crun is a command line program for running Linux containers that follow the Open Container Initiative (OCI) format. that's it. that's the whole icon behavior โจ
๐ COMMANDS ๐
createโ Create a container. the runtime detaches from the container process once the container environment is created, so heads up: you have to usestartafterward to actually get it going, bestie. no skipping steps ๐deleteโ Remove definition for a container.execโ Exec a command in a running container.listโ List known containers.mounts addโ Add mounts while the container is running. it needs two arguments: the container ID and a JSON file containing themountssection of the OCI config file. each mount listed there gets added to the running container. โ ๏ธ this command is experimental and can be changed without notice, so don't get too attached ๐ญmounts removeโ Remove mounts while the container is running. also two arguments: the container ID and a JSON file with themountssection of the OCI config file. only thedestinationattribute for each mount is used. also experimental, also can change without notice ๐killโ Send the specified signal to the container init process. if no signal is specified,SIGTERMis used. periodt.psโ Show the processes running in a container.runโ Create and immediately start a container. the combo meal ๐ฅspecโ Generate a configuration file.startโ Start a container that was previously created. ๐จ a container cannot be started multiple times, so don't try it.stateโ Output the state of a container.pauseโ Pause all the processes in the container.resumeโ Resume the processes in the container.updateโ Update container resource constraints.checkpointโ Checkpoint a running container using CRIU.restoreโ Restore a container from a checkpoint.
๐ STATE ๐
By default, when running as root user, crun saves its state under the /run/crun directory. as an unprivileged user, though? it honors the XDG_RUNTIME_DIR environment variable instead, and the directory $XDG_RUNTIME_DIR/crun is used. the global option --root overrides this setting. slay ๐
โญ GLOBAL OPTIONS โญ
--debugโ Produce verbose output. like, it tells you everything ๐--log=LOG-DESTINATIONโ Define the destination for the error and warning messages generated bycrun. if the error happens late in the container init process, whencrunalready stopped watching it, then it will be printed to the container stderr. it's specified in the formBACKEND:SPECIFIER. these following backends are supported:file:PATHjournald:IDENTIFIERsyslog:IDENTIFIER
if no backend is specified, then file: is used by default.
--log-format=FORMATโ Define the format of the log messages. it can either betext, orjson. the default istext.--log-level=LEVELโ Define the log level. it can either bedebug,warningorerror. the default iserror.--root=DIRโ Defines where to store the state forcruncontainers.--systemd-cgroupโ Use systemd for configuring cgroups. if not specified, the cgroup is created directly using thecgroupfsbackend.--cgroup-manager=MANAGERโ Specify what cgroup manager must be used. permitted values arecgroupfs,systemdanddisabled.-?,--helpโ Print a help list.--usageโ Print a short usage message.-V,--versionโ Print program version
๐ CREATE OPTIONS ๐
crun [global options] create [options] CONTAINER
--bundle=PATHโ Path to the OCI bundle, by default it is the current directory.--config=FILEโ Override the configuration file to use. the default value isconfig.json.--console-socket=SOCKETโ Path to a UNIX socket that will receive theptmxend of the tty for the container.--no-new-keyringโ Keep the same session key--no-pivotโ Usechroot(2)instead ofpivot_root(2)when creating the container. ๐จ this option is not safe, and should be avoided. like, fr fr, don't.--preserve-fds=Nโ Additional number of FDs to pass into the container.--pid-file=PATHโ Path to the file that will contain the container process PID.
๐ฆ RUN OPTIONS ๐ฆ
crun [global options] run [options] CONTAINER
--bundle=BUNDLEโ Path to the OCI bundle, by default it is the current directory.--config=FILEโ Override the configuration file to use. the default value isconfig.json.--console-socket=SOCKETโ Path to a UNIX socket that will receive theptmxend of the tty for the container.--no-new-keyringโ Keep the same session key.--no-pivotโ Usechroot(2)instead ofpivot_root(2)when creating the container. again: not safe, and should be avoided ๐ฌ--preserve-fds=Nโ Additional number of FDs to pass into the container.--pid-file=PATHโ Path to the file that will contain the container process PID.--detachโ Detach the container process from the current session.
๐ DELETE OPTIONS ๐
crun [global options] delete [options] CONTAINER
--forceโ Delete the container even if it is still running. menace behavior, but okay ๐--regexโ TreatCONTAINERas a regular expression and delete all matching containers.
๐ง EXEC OPTIONS ๐ง
crun [global options] exec [options] CONTAINER CMD
--apparmor=PROFILEโ Set the apparmor profile for the process.--console-socket=SOCKETโ Path to a UNIX socket that will receive theptmxend of the tty for the container.--cwd=PATHโ Set the working directory for the process toPATH.--cap=CAPโ Specify an additional capability to add to the process.--detachโ Detach the container process from the current session.--cgroup=PATHโ Specify a sub-cgroup path inside the container cgroup. the path must already exist in the container cgroup, so no inventing directories bestie.--env=ENVโ Specify an environment variable.--no-new-privsโ Set the no new privileges value for the process.--preserve-fds=Nโ Additional number of FDs to pass into the container.--process=FILEโ Path to a file containing the process JSON configuration.--process-label=VALUEโ Set the asm process label for the process commonly used with selinux.--pid-file=PATHโ Path to the file that will contain the new process PID.-t,--ttyโ Allocate a pseudo TTY.- *
-u*USERSPEC,--user=*USERSPEC*** โ Specify the user in the formUID[:GID]`.
๐ LIST OPTIONS ๐
crun [global options] list [options]
-q,--quietโ Show only the container ID. minimalism โจ
๐ฅ KILL OPTIONS ๐ฅ
crun [global options] kill [options] CONTAINER SIGNAL
--allโ Kill all the processes in the container.--regexโ TreatCONTAINERas a regular expression and send the specified signal to all matching containers.
๐ PS OPTIONS ๐
crun [global options] ps [options]
--format=FORMATโ Specify the output format. it must be eithertableorjson. by defaulttableis used.
๐ SPEC OPTIONS ๐
crun [global options] spec [options]
-bDIR,--bundle=DIRโ Path to the root of the bundle dir (default".").--rootlessโ Generate aconfig.jsonfile that is usable by an unprivileged user.
๐ธ UPDATE OPTIONS ๐ธ
crun [global options] update [options] CONTAINER
--blkio-weight=VALUEโ Specifies per cgroup weight.--cpu-period=VALUEโ CPU CFS period to be used for hardcapping.--cpu-quota=VALUEโ CPU CFS hardcap limit.--cpu-rt-period=VALUEโ CPU realtime period to be used for hardcapping.--cpu-rt-runtime=VALUEโ CPU realtime hardcap limit.--cpu-share=VALUEโ CPU shares.--cpuset-cpus=VALUEโ CPU(s) to use.--cpuset-mems=VALUEโ Memory node(s) to use.--kernel-memory=VALUEโ Kernel memory limit.--kernel-memory-tcp=VALUEโ Kernel memory limit for TCP buffer.--memory=VALUEโ Memory limit.--memory-reservation=VALUEโ Memory reservation orsoft_limit.--memory-swap=VALUEโ Total memory usage.--pids-limit=VALUEโ Maximum number of pids allowed in the container.-r,--resources=FILEโ Path to the file containing the resources to update.
๐ฆ CHECKPOINT OPTIONS ๐ฆ
crun [global options] checkpoint [options] CONTAINER
--image-path=DIRโ Path for saving CRIU image files--work-path=DIRโ Path for saving work files and logs--leave-runningโ Leave the process running after checkpointing--tcp-establishedโ Allow open TCP connections--ext-unix-skโ Allow external UNIX sockets--shell-jobโ Allow shell jobs--pre-dumpโ Only checkpoint the container's memory without stopping the container. ๐จ it is not possible to restore a container from a pre-dump. a pre-dump always needs a final checkpoint (without--pre-dump). it is possible to make as many pre-dumps as necessary. for a second pre-dump or for a final checkpoint it is necessary to use--parent-pathto pointcrun(and thus CRIU) to the pre-dump.--parent-path=DIRโ Doing multiple pre-dumps or the final checkpoint after one or multiple pre-dumps requires thatcrun(and thus CRIU) knows the location of the pre-dump. it is important to use a relative path from the actual checkpoint directory specified via--image-path. it will fail if an absolute path is used ๐ญ--manage-cgroups-mode=MODEโ Specify which CRIU manage cgroup mode should be used. permitted values aresoft,ignore,fullorstrict. default issoft.
โญ RESTORE OPTIONS โญ
crun [global options] restore [options] CONTAINER
-bDIR,--bundle=DIRโ Container bundle directory (default".")--image-path=DIRโ Path for saving CRIU image files--work-path=DIRโ Path for saving work files and logs--tcp-establishedโ Allow open TCP connections--ext-unixโ Allow external UNIX sockets--shell-jobโ Allow shell jobs--detachโ Detach from the container's process--pid-file=FILEโ Where to write the PID of the container--manage-cgroups-mode=MODEโ Specify which CRIU manage cgroup mode should be used. permitted values aresoft,ignore,fullorstrict. default issoft.--lsm-profile=TYPE:NAMEโ Specify an LSM profile to be used during restore.TYPEcan be eitherapparmororselinux.--lsm-mount-context=VALUEโ Specify a new LSM mount context to be used during restore. this option replaces an existing mount context information with the specified value. this is useful when restoring a container into an existing Pod and selinux labels need to be changed during restore.
๐ซถ Extensions to OCI ๐ซถ
run.oci.mount_context_type=context
Set the mount context type on volumes mounted with SELinux labels.
valid context types are:
context(default)fscontextdefcontextrootcontext
more information on how the context mount flags work can be found in the mount(8) man page.
run.oci.seccomp.receiver=PATH
if the annotation run.oci.seccomp.receiver=PATH is specified, the seccomp listener is sent to the UNIX socket listening on the specified path. it can also be set with the RUN_OCI_SECCOMP_RECEIVER environment variable. โ ๏ธ it is an experimental feature, and the annotation will be removed once it is supported in the OCI runtime specs. it must be an absolute path.
run.oci.seccomp.plugins=PATH
Okay so if the annotation run.oci.seccomp.plugins=PLUGIN1[:PLUGIN2]... is there, then the seccomp listener fd gets handled through the plugins you named. Each plugin has to either be an absolute path or a file name that gets looked up by dlopen(3) โ and if you wanna know exactly how that lookup goes down, the ld.so(8) man page has the deets. ๐ซถ
run.oci.seccomp_fail_unknown_syscall=1
If the annotation run.oci.seccomp_fail_unknown_syscall is present, then crun will legit just fail when it hits an unknown syscall in the seccomp configuration. No quietly ignoring it, bestie โ it's a hard fail. ๐
run.oci.seccomp_bpf_data=PATH
If the annotation run.oci.seccomp_bpf_data is present, then crun ignores the seccomp section in the OCI configuration file entirely and uses the specified data as the raw data for the seccomp(SECCOMP_SET_MODE_FILTER) syscall. That data has to be encoded in base64, no exceptions.
Heads up bb: this is an experimental feature, and the annotation is gonna get removed once it's supported in the OCI runtime specs. So don't get too attached. ๐
run.oci.keep_original_groups=1
If the annotation run.oci.keep_original_groups is present, then crun skips the setgroups syscall. You know the one โ it's normally used to either set the additional groups from the OCI configuration, or reset the list of additional groups when none is specified. Skipped. Bye. โจ
run.oci.pidfd_receiver=PATH
Experimental feature alert ๐ โ this one gets removed once the feature lands in the OCI runtime specs, fr fr.
If present, you specify the path to the UNIX socket that's gonna receive the pidfd for the container process.
run.oci.systemd.force_cgroup_v1=/PATH
If the annotation run.oci.systemd.force_cgroup_v1=/PATH is present, then crun overrides the specified mount point /PATH with a cgroup v1 mount made of a single hierarchy none,name=systemd. It's iconic for running on a cgroup v2 system with containers that use older systemd versions that just don't have cgroup v2 support. ๐
Note: your container host has to have the cgroup v1 mount already present, otherwise this literally will not work. And if you wanna run the container rootless, the user it runs under needs permissions to that mountpoint. No perms, no vibes. ๐ซ
For example, as root:
mkdir /sys/fs/cgroup/systemd
mount cgroup -t cgroup /sys/fs/cgroup/systemd -o none,name=systemd,xattr
chown -R the_user.the_user /sys/fs/cgroup/systemd
run.oci.systemd.subgroup=SUBGROUP
This overrides the name for the systemd sub cgroup created under the systemd scope, so the final cgroup ends up looking like:
/sys/fs/cgroup/$PATH/$SUBGROUP
When it's set to the empty string, no sub cgroup gets created at all. โจ
If you don't specify it, it defaults to container on cgroup v2, and to "" on cgroup v1.
e.g.
/sys/fs/cgroup//system.slice/foo-352700.scope/container
run.oci.delegate-cgroup=DELEGATED-CGROUP
If the run.oci.systemd.subgroup annotation is specified, yet another sub-cgroup gets created and the container process is moved in there. Like, nesting levels, bestie. ๐ช
If a cgroup namespace is used, the cgroup namespace is created before moving the container to the delegated cgroup.
/sys/fs/cgroup/$PATH/$SUBGROUP/$DELEGATED-CGROUP
The runtime does not apply any limit to the $DELEGATED-CGROUP sub-cgroup โ it only uses $PATH/$SUBGROUP.
The container payload fully manages $DELEGATE-CGROUP, but the limits applied to $PATH/$SUBGROUP still apply to $DELEGATE-CGROUP. So the vibes trickle down. ๐
Since cgroup delegation is not safe on cgroup v1, this option is supported only on cgroup v2. Periodt. ๐
run.oci.hooks.stdout=FILE
If the annotation run.oci.hooks.stdout is present, then crun opens the specified file and uses it as the stdout for the hook processes. The file is opened in append mode and it gets created if it doesn't already exist. ๐
run.oci.hooks.stderr=FILE
If the annotation run.oci.hooks.stderr is present, then crun opens the specified file and uses it as the stderr for the hook processes. The file is opened in append mode and it gets created if it doesn't already exist. Same energy, different stream. ๐
run.oci.handler=HANDLER
It's an experimental feature, okay? ๐ฆ
If specified, it runs the specified handler for execing the container. The only supported values are krun and wasm.
krunโ whenkrunis specified, thelibkrun.soshared object is loaded and it's used to launch the container using libkrun.wasmโ if specified, run the wasm handler for the container. It allows running wasm workload natively. Accepts a.wasmbinary as input, and if.watis provided it gets automatically compiled into a wasm module. The stdout of the wasm module is relayed back viacrun.
org.criu.config=FILE
This annotation lets you specify a CRIU RPC configuration file. If you don't provide one, crun looks for /etc/criu/crun.conf. If neither the annotation nor crun.conf exists, then /etc/criu/runc.conf is used for compatibility with runc. This whole thing requires CRIU version 4.2 or newer. โจ
The options specified in the configuration file override the default values of CRIU options specified by crun. For example, specifying tcp-established or tcp-close can be used to checkpoint/restore containers with TCP established connections, and log-file=<file> sets a custom CRIU logs file. iykyk ๐ซถ
tmpcopyup mount options
If the tmpcopyup option is specified for a tmpfs, then the path that's shadowed by the tmpfs mount gets recursively copied up to the tmpfs itself. Copying it up so it's not just... gone. ๐ญ
copy-symlink mount options
If the copy-symlink option is specified, and the source of a bind mount is a symlink, then the symlink is recreated at the specified destination instead of attempting a mount that would resolve the symlink itself. If the destination already exists and it is not a symlink with the expected content, crun will return an error. No silent overwrites here, bestie. ๐
dest-nofollow
When this option is specified for a bind mount, and the destination of the bind mount is a symbolic link, crun will mount the symbolic link itself at the target destination. Yes, the link itself. Iconic.
src-nofollow
When this option is specified for a bind mount, and the source of the bind mount is a symbolic link, crun will use the symlink itself rather than the file or directory the symbolic link points to. ๐
r$FLAG mount options
If a r$FLAG mount option is specified then the flag $FLAG is set recursively for each child mount. Yeah. Every child. It's giving generational. ๐
These flags are supported:
"rro""rrw""rsuid""rnosuid""rdev""rnodev""rexec""rnoexec""rsync""rasync""rdirsync""rmand""rnomand""ratime""rnoatime""rdiratime""rnodiratime""rrelatime""rnorelatime""rstrictatime""rnostrictatime"
idmap mount options
If the idmap option is specified then the mount is ID mapped using the container target user namespace.
This is an experimental feature and can change at any time without notice. Fr fr, no promises. ๐
The idmap option supports a custom mapping that can be different than the user namespace used by the container.
The mapping can be specified after the idmap option like: idmap=uids=0-1-10#10-11-10;gids=0-100-10.
For each triplet, the first value is the start of the backing file system IDs that are mapped to the second value on the host. The length of this mapping is given in the third value.
Multiple ranges are separated with #.
These values get written to the /proc/$PID/uid_map and /proc/$PID/gid_map files to create the user namespace for the idmapped mount.
The only two options currently supported after idmap are uids and gids.
When a custom mapping is specified, a new user namespace is created for the idmapped mount. ๐ฆ
If no option is specified, then the container user namespace is used.
If the specified mapping is prepended with a @ then the mapping is considered relative to the container user namespace. The host ID for the mapping gets changed to account for the relative position of the container user in the container user namespace.
For example, the mapping: uids=@1-3-10, given a configuration like
"uidMappings": [
{
"containerID": 0,
"hostID": 0,
"size": 1
},
{
"containerID": 1,
"hostID": 2,
"size": 1000
}
]
will be converted to the absolute value uids=1-4-10, where 4 is calculated by adding 3 (container ID in the uids= mapping) and 1 (hostID - containerID for the user namespace mapping where containerID = 1 is found).
Caveat time, bestie ๐ญ: the current implementation doesn't take into account multiple user namespace ranges, so it is the caller's responsibility to split a mapping if it overlaps multiple ranges in the user namespace. And in such a case, there won't be any error reported. So like... it'll just do a thing. Pay attention. ๐
Automatically create user namespace
When running as a user other than root, a user namespace is automatically created even if it isn't specified in the config file. The current user is mapped to the ID 0 in the container, and any additional id specified in the files /etc/subuid and /etc/subgid is automatically added starting with ID 1. Automatic. Main character energy. โจ
CGROUP v1
Support for cgroup v1 is deprecated and will be removed in a future release. ๐
CGROUP v2
Note: cgroup v2 does not yet support control of realtime processes, and thecpucontroller can only be enabled when all RT processes are in the root cgroup. This will makecrunfail while running alongside RT processes. Not a vibe. ๐
If the cgroup configuration found is for cgroup v1, crun attempts a conversion when running on a cgroup v2 system.
These are the OCI resources currently supported with cgroup v2 and how they are converted when needed from the cgroup v1 configuration. ๐
๐ง Memory controller
okay bestie, here's how OCI memory limits map onto cgroup v2 values โ x is the OCI side, y is what actually lands in cgroup 2:
limitโmemory.max, conversiony = x(no notes, clean, we love to see it โจ)swapโmemory.swap.max, conversiony = x - memory_limitโ heads up: the swap limit on cgroup v1 includes the memory usage too ๐ซreservationโmemory.low, conversiony = x
๐งฎ PIDs controller
limitโpids.max, conversiony = x
short and sweet, no drama ๐
โก CPU controller
sharesโcpu.weight, conversiony=10^((log2(x)^2 + 125 * log2(x)) / 612.0 - 7.0 / 34.0)โ this is converting from[2-262144]to[1-10000], math girlies rise ๐periodโcpu.max, conversiony = xโ note that period and quota are written togetherquotaโcpu.max, conversiony = xโ same deal, period and quota are written together
๐พ blkio controller
weightโio.bfq.weight, conversiony = xweight_deviceโio.bfq.weight, conversiony = xweightโio.weight(fallback), conversiony = 1 + (x-10)*9999/990โ convert linearly from[10-1000]to[1-10000]weight_deviceโio.weight(fallback), conversiony = 1 + (x-10)*9999/990โ convert linearly from[10-1000]to[1-10000]rbpsโio.max, conversiony=xwbpsโio.max, conversiony=xriopsโio.max, conversiony=xwiopsโio.max, conversiony=x
look at all those io.max mappings, they really said "same energy" ๐
๐ cpuset controller
cpusโcpuset.cpus, conversiony = xmemsโcpuset.mems, conversiony = x
๐ hugetlb controller
.limit_in_bytesโhugetlb..max, conversiony = xโ yes the double dot is exactly how it's written, don't come for me, I'm just the messenger ๐
aaaand that's a wrap on the cgroup mapping section, bestie!! ๐ฆ you made it through the whole page, i'm so proud of you fr fr ๐ธ
this has been the crun(1) manual page from the User Commands section โ the source/version and the date at the bottom of the page are your receipts for exactly which build and day you're reading, so peek at the footer for those, and go slay your containers ๐๐
๐ฆ where this page comes from
- alpine 3.24: crun-doc 1.30.1-r0 ยท GPL-2.0-or-later AND LGPL-2.1-or-later
- arch rolling: crun 1.30.1-1 ยท LGPL-2.1-or-later
- centos-stream 10: crun 1.29.1-1.el10 ยท GPL-2.0-only
- centos-stream 10: crun 1.30.1-1.el10 ยท GPL-2.0-only
- centos-stream 9: crun 1.29.1-1.el9 ยท GPL-2.0-only
- fedora 44: crun 1.30-1.fc44 ยท GPL-2.0-only
- nixpkgs unstable: crun 1.30.1
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms