1---
2title: Container Interface
3category: Interfaces
4layout: default
5SPDX-License-Identifier: LGPL-2.1-or-later
6---
7
8# The Container Interface
9
10Also consult [Writing Virtual Machine or Container
11Managers](https://www.freedesktop.org/wiki/Software/systemd/writing-vm-managers).
12
13systemd has a number of interfaces for interacting with container managers,
14when systemd is used inside of an OS container. If you work on a container
15manager, please consider supporting the following interfaces.
16
17## Execution Environment
18
191. If the container manager wants to control the hostname for a container
20   running systemd it may just set it before invoking systemd, and systemd will
21   leave it unmodified when there is no hostname configured in `/etc/hostname`
22   (that file overrides whatever is pre-initialized by the container manager).
23
242. Make sure to pre-mount `/proc/`, `/sys/`, and `/sys/fs/selinux/` before
25   invoking systemd, and mount `/sys/`, `/sys/fs/selinux/` and `/proc/sys/`
26   read-only (the latter via e.g. a read-only bind mount on itself) in order
27   to prevent the container from altering the host kernel's configuration
28   settings. (As a special exception, if your container has network namespaces
29   enabled, feel free to make `/proc/sys/net/` writable. If it also has user, ipc,
30   uts and pid namespaces enabled, the entire `/proc/sys` can be left writable).
31   systemd and various other subsystems (such as the SELinux userspace) have
32   been modified to behave accordingly when these file systems are read-only.
33   (It's OK to mount `/sys/` as `tmpfs` btw, and only mount a subset of its
34   sub-trees from the real `sysfs` to hide `/sys/firmware/`, `/sys/kernel/` and
35   so on. If you do that, make sure to mark `/sys/` read-only, as that
36   condition is what systemd looks for, and is what is considered to be the API
37   in this context.)
38
393. Pre-mount `/dev/` as (container private) `tmpfs` for the container and bind
40   mount some suitable TTY to `/dev/console`. If this is a pty, make sure to
41   not close the controlling pty during systemd's lifetime. PID 1 will close
42   ttys, to avoid being killed by SAK. It only opens ttys for the time it
43   actually needs to print something. Also, make sure to create device nodes
44   for `/dev/null`, `/dev/zero`, `/dev/full`, `/dev/random`, `/dev/urandom`,
45   `/dev/tty`, `/dev/ptmx` in `/dev/`. It is not necessary to create `/dev/fd`
46   or `/dev/stdout`, as systemd will do that on its own. Make sure to set up a
47   `BPF_PROG_TYPE_CGROUP_DEVICE` BPF program — on cgroupv2 — or the `devices`
48   cgroup controller — on cgroupv1 — so that no other devices but these may be
49   created in the container. Note that many systemd services use
50   `PrivateDevices=`, which means that systemd will set up a private `/dev/`
51   for them for which it needs to be able to create these device nodes.
52   Dropping `CAP_MKNOD` for containers is hence generally not advisable, but
53   see below.
54
554. `systemd-udevd` is not available in containers (and refuses to start), and
56   hence device dependencies are unavailable. The `systemd-udevd` unit files
57   will check for `/sys/` being read-only, as an indication whether device
58   management can work. Therefore make sure to mount `/sys/` read-only in the
59   container (see above). Various clients of `systemd-udevd` also check the
60   read-only state of `/sys/`, including PID 1 itself and `systemd-networkd`.
61
625. If systemd detects it is run in a container it will spawn a single shell on
63   `/dev/console`, and not care about VTs or multiple gettys on VTs. (But see
64   `$container_ttys` below.)
65
666. Either pre-mount all cgroup hierarchies in full into the container, or leave
67   that to systemd which will do so if they are missing. Note that it is
68   explicitly *not* OK to just mount a sub-hierarchy into the container as that
69   is incompatible with `/proc/$PID/cgroup` (which lists full paths). Also the
70   root-level cgroup directories tend to be quite different from inner
71   directories, and that distinction matters. It is OK however, to mount the
72   "upper" parts read-only of the hierarchies, and only allow write-access to
73   the cgroup sub-tree the container runs in. It's also a good idea to mount
74   all controller hierarchies with exception of `name=systemd` fully read-only
75   (this only applies to cgroupv1, of course), to protect the controllers from
76   alteration from inside the containers. Or to turn this around: only the
77   cgroup sub-tree of the container itself (on cgroupv2 in the unified
78   hierarchy, and on cgroupv1 in the `name=systemd` hierarchy) may be writable
79   to the container.
80
817. Create the control group root of your container by either running your
82   container as a service (in case you have one container manager instance per
83   container instance) or creating one scope unit for each container instance
84   via systemd's transient unit API (in case you have one container manager
85   that manages all instances. Either way, make sure to set `Delegate=yes` in
86   it. This ensures that the unit you created will be part of all cgroup
87   controllers (or at least the ones systemd understands). The latter may also
88   be done via `systemd-machined`'s `CreateMachine()` API. Make sure to use the
89   cgroup path systemd put your process in for all operations of the container.
90   Do not add new cgroup directories to the top of the tree. This will not only
91   confuse systemd and the admin, but also prevent your implementation from
92   being "stackable".
93
94## Environment Variables
95
961. To allow systemd (and other programs) to identify that it is executed within
97   a container, please set the `$container` environment variable for PID 1 in
98   the container to a short lowercase string identifying your
99   implementation. With this in place the `ConditionVirtualization=` setting in
100   unit files will work properly. Example: `container=lxc-libvirt`
101
1022. systemd has special support for allowing container managers to initialize
103   the UUID for `/etc/machine-id` to some manager supplied value. This is only
104   enabled if `/etc/machine-id` is empty (i.e. not yet set) at boot time of the
105   container. The container manager should set `$container_uuid` as environment
106   variable for the container's PID 1 to the container UUID. (This is similar
107   to the effect of `qemu`'s `-uuid` switch). Note that you should pass only a
108   UUID here that is actually unique (i.e. only one running container should
109   have a specific UUID), and gets changed when a container gets duplicated.
110   Also note that systemd will try to persistently store the UUID in
111   `/etc/machine-id` (if writable) when this option is used, hence you should
112   always pass the same UUID here. Keeping the externally used UUID for a
113   container and the internal one in sync is hopefully useful to minimize
114   surprise for the administrator.
115
1163. systemd can automatically spawn login gettys on additional ptys. A container
117   manager can set the `$container_ttys` environment variable for the
118   container's PID 1 to tell it on which ptys to spawn gettys. The variable
119   should take a space separated list of pty names, without the leading `/dev/`
120   prefix, but with the `pts/` prefix included. Note that despite the
121   variable's name you may only specify ptys, and not other types of ttys. Also
122   you need to specify the pty itself, a symlink will not suffice. This is
123   implemented in
124   [systemd-getty-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-getty-generator.html).
125   Note that this variable should not include the pty that `/dev/console` maps
126   to if it maps to one (see below). Example: if the container receives
127   `container_ttys=pts/7 pts/8 pts/14` it will spawn three additional login
128   gettys on ptys 7, 8, and 14.
129
1304. To allow applications to detect the OS version and other metadata of the host
131   running the container manager, if this is considered desirable, please parse
132   the host's `/etc/os-release` and set a `$container_host_<key>=<VALUE>`
133   environment variable for the ID fields described by the [os-release
134   interface](https://www.freedesktop.org/software/systemd/man/os-release.html), eg:
135   `$container_host_id=debian`
136   `$container_host_build_id=2020-06-15`
137   `$container_host_variant_id=server`
138   `$container_host_version_id=10`
139
1405. systemd supports passing immutable binary data blobs with limited size and
141   restricted access to services via the `LoadCredential=` and `SetCredential=`
142   settings. The same protocol may be used to pass credentials from the
143   container manager to systemd itself. The credential data should be placed in
144   some location (ideally a read-only and non-swappable file system, like
145   'ramfs'), and the absolute path to this directory exported in the
146   `$CREDENTIALS_DIRECTORY` environment variable. If the container managers
147   does this, the credentials passed to the service manager can be propagated
148   to services via `LoadCredential=` (see ...). The container manager can
149   choose any path, but `/run/host/credentials` is recommended.
150
151## Advanced Integration
152
1531. Consider syncing `/etc/localtime` from the host file system into the
154   container. Make it a relative symlink to the containers's zoneinfo dir, as
155   usual. Tools rely on being able to determine the timezone setting from the
156   symlink value, and making it relative looks nice even if people list the
157   container's `/etc/` from the host.
158
1592. Make the container journal available in the host, by automatically
160   symlinking the container journal directory into the host journal directory.
161   More precisely, link `/var/log/journal/<container-machine-id>` of the
162   container into the same dir of the host. Administrators can then
163   automatically browse all container journals (correctly interleaved) by
164   issuing `journalctl -m`. The container machine ID can be determined from
165   `/etc/machine-id` in the container.
166
1673. If the container manager wants to cleanly shutdown the container, it might
168   be a good idea to send `SIGRTMIN+3` to its init process. systemd will then
169   do a clean shutdown. Note however, that since only systemd understands
170   `SIGRTMIN+3` like this, this might confuse other init systems.
171
1724. To support [Socket Activated
173   Containers](http://0pointer.de/blog/projects/socket-activated-containers.html)
174   the container manager should be capable of being run as a systemd
175   service. It will then receive the sockets starting with FD 3, the number of
176   passed FDs in `$LISTEN_FDS` and its PID as `$LISTEN_PID`. It should take
177   these and pass them on to the container's init process, also setting
178   $LISTEN_FDS and `$LISTEN_PID` (basically, it can just leave the FDs and
179   `$LISTEN_FDS` untouched, but it needs to adjust `$LISTEN_PID` to the
180   container init process). That's all that's necessary to make socket
181   activation work. The protocol to hand sockets from systemd to services is
182   hence the same as from the container manager to the container systemd. For
183   further details see the explanations of
184   [sd_listen_fds(1)](http://0pointer.de/public/systemd-man/sd_listen_fds.html)
185   and the [blog story for service
186   developers](http://0pointer.de/blog/projects/socket-activation.html).
187
1885. Container managers should stay away from the cgroup hierarchy outside of the
189   unit they created for their container. That's private property of systemd,
190   and no other code should modify it.
191
1926. systemd running inside the container can report when boot-up is complete
193   using the usual `sd_notify()` protocol that is also used when a service
194   wants to tell the service manager about readiness. A container manager can
195   set the `$NOTIFY_SOCKET` environment variable to a suitable socket path to
196   make use of this functionality. (Also see information about
197   `/run/host/notify` below.)
198
199## Networking
200
2011. Inside of a container, if a `veth` link is named `host0`, `systemd-networkd`
202   running inside of the container will by default run DHCPv4, DHCPv6, and
203   IPv4LL clients on it. It is thus recommended that container managers that
204   add a `veth` link to a container name it `host0`, to get an automatically
205   configured network, with no manual setup.
206
2072. Outside of a container, if a `veth` link is prefixed "ve-", `systemd-networkd`
208   will by default run DHCPv4 and DHCPv6 servers on it, as well as IPv4LL. It
209   is thus recommended that container managers that add a `veth` link to a
210   container name the external side `ve-` + the container name.
211
2123. It is recommended to configure stable MAC addresses for container `veth`
213   devices, for example hashed out of the container names. That way it is more
214   likely that DHCP and IPv4LL will acquire stable addresses.
215
216## The `/run/host/` Hierarchy
217
218Container managers may place certain resources the manager wants to provide to
219the container payload below the `/run/host/` hierarchy. This hierarchy should
220be mostly immutable (possibly some subdirs might be writable, but the top-level
221hierarchy — and probably most subdirs should be read-only to the
222container). Note that this hierarchy is used by various container managers, and
223care should be taken to avoid naming conflicts. `systemd` (and in particular
224`systemd-nspawn`) use the hierarchy for the following resources:
225
2261. The `/run/host/incoming/` directory mount point is configured for `MS_SLAVE`
227   mount propagation with the host, and is used as intermediary location for
228   mounts to establish in the container, for the implementation of `machinectl
229   bind`. Container payload should usually not directly interact with this
230   directory: it's used by code outside the container to insert mounts inside
231   it only, and is mostly an internal vehicle to achieve this. Other container
232   managers that want to implement similar functionality might consider using
233   the same directory.
234
2352. The `/run/host/inaccessible/` directory may be set up by the container
236   manager to include six file nodes: `reg`, `dir`, `fifo`, `sock`, `chr`,
237   `blk`. These nodes correspond with the six types of file nodes Linux knows
238   (with the exceptions of symlinks). Each node should be of the specific type
239   and have an all zero access mode, i.e. be inaccessible. The two device node
240   types should have major and minor of zero (which are unallocated devices on
241   Linux). These nodes are used as mount source for implementing the
242   `InaccessiblePath=` setting of unit files, i.e. file nodes to mask this way
243   are overmounted with these "inaccessible" inodes, guaranteeing that the file
244   node type does not change this way but the nodes still become
245   inaccessible. Note that systemd when run as PID 1 in the container payload
246   will create these nodes on its own if not passed in by the container
247   manager. However, in that case it likely lacks the privileges to create the
248   character and block devices nodes (there are fallbacks for this case).
249
2503. The `/run/host/notify` path is a good choice to place the `sd_notify()`
251   socket in, that may be used for the container's PID 1 to report to the
252   container manager when boot-up is complete. The path used for this doesn't
253   matter much as it is communicated via the `$NOTIFY_SOCKET` environment
254   variable, following the usual protocol for this, however it's suitable, and
255   recommended place for this socket in case ready notification is desired.
256
2574. The `/run/host/os-release` file contains the `/etc/os-release` file of the
258   host, i.e. may be used by the container payload to gather limited
259   information about the host environment, on top of what `uname -a` reports.
260
2615. The `/run/host/container-manager` file may be used to pass the same
262   information as the `$container` environment variable (see above), i.e. a
263   short string identifying the container manager implementation. This file
264   should be newline terminated. Passing this information via this file has the
265   benefit that payload code can easily access it, even when running
266   unprivileged without access to the container PID 1's environment block.
267
2686. The `/run/host/container-uuid` file may be used to pass the same information
269   as the `$container_uuid` environment variable (see above). This file should
270   be newline terminated.
271
2727. The `/run/host/credentials/` directory is a good place to pass credentials
273   into the container, using the `$CREDENTIALS_DIRECTORY` protocol, see above.
274
275## What You Shouldn't Do
276
2771. Do not drop `CAP_MKNOD` from the container. `PrivateDevices=` is a commonly
278   used service setting that provides a service with its own, private, minimal
279   version of `/dev/`. To set this up systemd in the container needs this
280   capability. If you take away the capability, then all services that set this
281   flag will cease to work. Use `BPF_PROG_TYPE_CGROUP_DEVICE` BPF programs — on
282   cgroupv2 — or the `devices` controller — on cgroupv1 — to restrict what
283   device nodes the container can create instead of taking away the capability
284   wholesale. (Also see the section about fully unprivileged containers below.)
285
2862. Do not drop `CAP_SYS_ADMIN` from the container. A number of the most
287   commonly used file system namespacing related settings, such as
288   `PrivateDevices=`, `ProtectHome=`, `ProtectSystem=`, `MountFlags=`,
289   `PrivateTmp=`, `ReadWriteDirectories=`, `ReadOnlyDirectories=`,
290   `InaccessibleDirectories=`, and `MountFlags=` need to be able to open new
291   mount namespaces and the mount certain file systems into them. You break all
292   services that make use of these options if you drop the capability. Also
293   note that logind mounts `XDG_RUNTIME_DIR` as `tmpfs` for all logged in users
294   and that won't work either if you take away the capability. (Also see
295   section about fully unprivileged containers below.)
296
2973. Do not cross-link `/dev/kmsg` with `/dev/console`. They are different things,
298   you cannot link them to each other.
299
3004. Do not pretend that the real VTs are available in the container. The VT
301   subsystem consists of all the devices `/dev/tty*`, `/dev/vcs*`, `/dev/vcsa*`
302   plus their `sysfs` counterparts. They speak specific `ioctl()`s and
303   understand specific escape sequences, that other ptys don't understand.
304   Hence, it is explicitly not OK to mount a pty to `/dev/tty1`, `/dev/tty2`,
305   `/dev/tty3`. This is explicitly not supported.
306
3075. Don't pretend that passing arbitrary devices to containers could really work
308   well. For example, do not pass device nodes for block devices to the
309   container. Device access (with the exception of network devices) is not
310   virtualized on Linux. Enumeration and probing of meta information from
311   `/sys/` and elsewhere is not possible to do correctly in a container. Simply
312   adding a specific device node to a container's `/dev/` is *not* *enough* to
313   do the job, as `systemd-udevd` and suchlike are not available at all, and no
314   devices will appear available or enumerable, inside the container.
315
3166. Don't mount only a sub-tree of the `cgroupfs` into the container. This will not
317   work as `/proc/$PID/cgroup` lists full paths and cannot be matched up with
318   the actual `cgroupfs` tree visible, then. (You may "prune" some branches
319   though, see above.)
320
3217. Do not make `/sys/` writable in the container. If you do,
322   `systemd-udevd.service` is started to manage your devices — inside the
323   container, but that will cause conflicts and errors given that the Linux
324   device model is not virtualized for containers on Linux and thus the
325   containers and the host would try to manage the same devices, fighting for
326   ownership. Multiple other subsystems of systemd similarly test for `/sys/`
327   being writable to decide whether to use `systemd-udevd` or assume that
328   device management is properly available on the instance. Among them
329   `systemd-networkd` and `systemd-logind`. The conditionalization on the
330   read-only state of `/sys/` enables a nice automatism: as soon as `/sys/` and
331   the Linux device model are changed to be virtualized properly the container
332   payload can make use of that, simply by marking `/sys/` writable. (Note that
333   as special exception, the devices in `/sys/class/net/` are virtualized
334   already, if network namespacing is used. Thus it is OK to mount the relevant
335   sub-directories of `/sys/` writable, but make sure to leave the root of
336   `/sys/` read-only.)
337
3388. Do not pass the `CAP_AUDIT_CONTROL`, `CAP_AUDIT_READ`, `CAP_AUDIT_WRITE`
339   capabilities to the container, in particular not to those making use of user
340   namespaces. The kernel's audit subsystem is still not virtualized for
341   containers, and passing these credentials is pointless hence, given the
342   actual attempt to make use of the audit subsystem will fail. Note that
343   systemd's audit support is partially conditioned on these capabilities, thus
344   by dropping them you ensure that you get an entirely clean boot, as systemd
345   will make no attempt to use it. If you pass the capabilities to the payload
346   systemd will assume that audit is available and works, and some components
347   will subsequently fail in various ways. Note that once the kernel learnt
348   native support for container-virtualized audit, adding the capability to the
349   container description will automatically make the container payload use it.
350
351## Fully Unprivileged Container Payload
352
353First things first, to make this clear: Linux containers are not a security
354technology right now. There are more holes in the model than in swiss cheese.
355
356For example: if you do not use user namespacing, and share root and other users
357between container and host, the `struct user` structures will be shared between
358host and container, and hence `RLIMIT_NPROC` and so of the container users
359affect the host and other containers, and vice versa. This is a major security
360hole, and actually is a real-life problem: since Avahi sets `RLIMIT_NPROC` of
361its user to 2 (to effectively disallow `fork()`ing) you cannot run more than
362one Avahi instance on the entire system...
363
364People have been asking to be able to run systemd without `CAP_SYS_ADMIN` and
365`CAP_SYS_MKNOD` in the container. This is now supported to some level in
366systemd, but we recommend against it (see above). If `CAP_SYS_ADMIN` and
367`CAP_SYS_MKNOD` are missing from the container systemd will now gracefully turn
368off `PrivateTmp=`, `PrivateNetwork=`, `ProtectHome=`, `ProtectSystem=` and
369others, because those capabilities are required to implement these options. The
370services using these settings (which include many of systemd's own) will hence
371run in a different, less secure environment when the capabilities are missing
372than with them around.
373
374With user namespacing in place things get much better. With user namespaces the
375`struct user` issue described above goes away, and containers can keep
376`CAP_SYS_ADMIN` safely for the user namespace, as capabilities are virtualized
377and having capabilities inside a container doesn't mean one also has them
378outside.
379
380## Final Words
381
382If you write software that wants to detect whether it is run in a container,
383please check `/proc/1/environ` and look for the `container=` environment
384variable. Do not assume the environment variable is inherited down the process
385tree. It generally is not. Hence check the environment block of PID 1, not your
386own. Note though that this file is only accessible to root. systemd hence early
387on also copies the value into `/run/systemd/container`, which is readable for
388everybody. However, that's a systemd-specific interface and other init systems
389are unlikely to do the same.
390
391Note that it is our intention to make systemd systems work flawlessly and
392out-of-the-box in containers. In fact we are interested to ensure that the same
393OS image can be booted on a bare system, in a VM and in a container, and behave
394correctly each time. If you notice that some component in systemd does not work
395in a container as it should, even though the container manager implements
396everything documented above, please contact us.
397