1 { config, lib, pkgs, ... }@host:
7 configurationPrefix = optionalString (versionAtLeast config.system.stateVersion "22.05") "nixos-";
8 configurationDirectoryName = "${configurationPrefix}containers";
9 configurationDirectory = "/etc/${configurationDirectoryName}";
10 stateDirectory = "/var/lib/${configurationPrefix}containers";
12 nixos-container = pkgs.nixos-container.override {
13 inherit stateDirectory configurationDirectory;
16 # The container's init script, a small wrapper around the regular
17 # NixOS stage-2 init script.
20 renderExtraVeth = (name: cfg:
22 echo "Bringing ${name} up"
23 ip link set dev ${name} up
24 ${optionalString (cfg.localAddress != null) ''
25 echo "Setting ip for ${name}"
26 ip addr add ${cfg.localAddress} dev ${name}
28 ${optionalString (cfg.localAddress6 != null) ''
29 echo "Setting ip6 for ${name}"
30 ip -6 addr add ${cfg.localAddress6} dev ${name}
32 ${optionalString (cfg.hostAddress != null) ''
33 echo "Setting route to host for ${name}"
34 ip route add ${cfg.hostAddress} dev ${name}
36 ${optionalString (cfg.hostAddress6 != null) ''
37 echo "Setting route6 to host for ${name}"
38 ip -6 route add ${cfg.hostAddress6} dev ${name}
43 pkgs.writeScript "container-init"
45 #! ${pkgs.runtimeShell} -e
47 # Exit early if we're asked to shut down.
48 trap "exit 0" SIGRTMIN+3
50 # Initialise the container side of the veth pair.
51 if [ -n "$HOST_ADDRESS" ] || [ -n "$HOST_ADDRESS6" ] ||
52 [ -n "$LOCAL_ADDRESS" ] || [ -n "$LOCAL_ADDRESS6" ] ||
53 [ -n "$HOST_BRIDGE" ]; then
54 ip link set host0 name eth0
55 ip link set dev eth0 up
57 if [ -n "$LOCAL_ADDRESS" ]; then
58 ip addr add $LOCAL_ADDRESS dev eth0
60 if [ -n "$LOCAL_ADDRESS6" ]; then
61 ip -6 addr add $LOCAL_ADDRESS6 dev eth0
63 if [ -n "$HOST_ADDRESS" ]; then
64 ip route add $HOST_ADDRESS dev eth0
65 ip route add default via $HOST_ADDRESS
67 if [ -n "$HOST_ADDRESS6" ]; then
68 ip -6 route add $HOST_ADDRESS6 dev eth0
69 ip -6 route add default via $HOST_ADDRESS6
73 ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)}
75 # Start the regular stage 2 script.
76 # We source instead of exec to not lose an early stop signal, which is
77 # also the only _reliable_ shutdown signal we have since early stop
78 # does not execute ExecStop* commands.
84 nspawnExtraVethArgs = (name: cfg: "--network-veth-extra=${name}");
88 # Declare root explicitly to avoid shellcheck warnings, it comes from the env
91 mkdir -p "$root/etc" "$root/var/lib"
92 chmod 0755 "$root/etc" "$root/var/lib"
93 mkdir -p "$root/var/lib/private" "$root/root" /run/nixos-containers
94 chmod 0700 "$root/var/lib/private" "$root/root" /run/nixos-containers
95 if ! [ -e "$root/etc/os-release" ]; then
96 touch "$root/etc/os-release"
99 if ! [ -e "$root/etc/machine-id" ]; then
100 touch "$root/etc/machine-id"
104 "/nix/var/nix/profiles/per-container/$INSTANCE" \
105 "/nix/var/nix/gcroots/per-container/$INSTANCE"
107 "/nix/var/nix/profiles/per-container/$INSTANCE" \
108 "/nix/var/nix/gcroots/per-container/$INSTANCE"
110 cp --remove-destination /etc/resolv.conf "$root/etc/resolv.conf"
112 declare -a extraFlags
114 if [ "$PRIVATE_NETWORK" = 1 ]; then
115 extraFlags+=("--private-network")
118 if [ -n "$HOST_ADDRESS" ] || [ -n "$LOCAL_ADDRESS" ] ||
119 [ -n "$HOST_ADDRESS6" ] || [ -n "$LOCAL_ADDRESS6" ]; then
120 extraFlags+=("--network-veth")
123 if [ -n "$HOST_PORT" ]; then
128 extraFlags+=("--port=$i")
133 if [ -n "$HOST_BRIDGE" ]; then
134 extraFlags+=("--network-bridge=$HOST_BRIDGE")
137 if [ -n "$NETWORK_NAMESPACE_PATH" ]; then
138 extraFlags+=("--network-namespace-path=$NETWORK_NAMESPACE_PATH")
141 extraFlags+=(${lib.escapeShellArgs (mapAttrsToList nspawnExtraVethArgs cfg.extraVeths)})
143 for iface in $INTERFACES; do
144 extraFlags+=("--network-interface=$iface")
147 for iface in $MACVLANS; do
148 extraFlags+=("--network-macvlan=$iface")
151 # If the host is 64-bit and the container is 32-bit, add a
152 # --personality flag.
153 ${optionalString (pkgs.stdenv.hostPlatform.system == "x86_64-linux") ''
154 if [ "$(< "''${SYSTEM_PATH:-/nix/var/nix/profiles/per-container/$INSTANCE/system}/system")" = i686-linux ]; then
155 extraFlags+=("--personality=x86")
159 export SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1
161 # Run systemd-nspawn without startup notification (we'll
162 # wait for the container systemd to signal readiness)
163 # Kill signal handling means systemd-nspawn will pass a system-halt signal
164 # to the container systemd when it receives SIGTERM for container shutdown;
165 # containerInit and stage2 have to handle this as well.
166 # TODO: fix shellcheck issue properly
167 # shellcheck disable=SC2086
168 exec ${config.systemd.package}/bin/systemd-nspawn \
170 -M "$INSTANCE" -D "$root" "''${extraFlags[@]}" \
171 $EXTRA_NSPAWN_FLAGS \
173 --kill-signal=SIGRTMIN+3 \
174 --bind-ro=/nix/store \
175 --bind-ro=/nix/var/nix/db \
176 --bind-ro=/nix/var/nix/daemon-socket \
177 --bind="/nix/var/nix/profiles/per-container/$INSTANCE:/nix/var/nix/profiles" \
178 --bind="/nix/var/nix/gcroots/per-container/$INSTANCE:/nix/var/nix/gcroots" \
179 ${optionalString (!cfg.ephemeral) "--link-journal=try-guest"} \
180 --setenv PRIVATE_NETWORK="$PRIVATE_NETWORK" \
181 --setenv HOST_BRIDGE="$HOST_BRIDGE" \
182 --setenv HOST_ADDRESS="$HOST_ADDRESS" \
183 --setenv LOCAL_ADDRESS="$LOCAL_ADDRESS" \
184 --setenv HOST_ADDRESS6="$HOST_ADDRESS6" \
185 --setenv LOCAL_ADDRESS6="$LOCAL_ADDRESS6" \
186 --setenv HOST_PORT="$HOST_PORT" \
187 --setenv PATH="$PATH" \
188 ${optionalString cfg.ephemeral "--ephemeral"} \
189 ${optionalString (cfg.additionalCapabilities != null && cfg.additionalCapabilities != [])
190 ''--capability="${concatStringsSep "," cfg.additionalCapabilities}"''
192 ${optionalString (cfg.tmpfs != null && cfg.tmpfs != [])
193 ''--tmpfs=${concatStringsSep " --tmpfs=" cfg.tmpfs}''
195 ${containerInit cfg} "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/init"
198 preStartScript = cfg:
200 # Clean up existing machined registration and interfaces.
201 machinectl terminate "$INSTANCE" 2> /dev/null || true
203 if [ -n "$HOST_ADDRESS" ] || [ -n "$LOCAL_ADDRESS" ] ||
204 [ -n "$HOST_ADDRESS6" ] || [ -n "$LOCAL_ADDRESS6" ]; then
205 ip link del dev "ve-$INSTANCE" 2> /dev/null || true
206 ip link del dev "vb-$INSTANCE" 2> /dev/null || true
209 ${concatStringsSep "\n" (
210 mapAttrsToList (name: cfg:
211 "ip link del dev ${name} 2> /dev/null || true "
216 postStartScript = (cfg:
218 ipcall = cfg: ipcmd: variable: attribute:
219 if cfg.${attribute} == null then
221 if [ -n "${variable}" ]; then
222 ${ipcmd} add "${variable}" dev "$ifaceHost"
226 ''${ipcmd} add ${cfg.${attribute}} dev "$ifaceHost"'';
227 renderExtraVeth = name: cfg:
228 if cfg.hostBridge != null then
230 # Add ${name} to bridge ${cfg.hostBridge}
231 ip link set dev "${name}" master "${cfg.hostBridge}" up
235 echo "Bring ${name} up"
236 ip link set dev "${name}" up
237 # Set IPs and routes for ${name}
238 ${optionalString (cfg.hostAddress != null) ''
239 ip addr add ${cfg.hostAddress} dev "${name}"
241 ${optionalString (cfg.hostAddress6 != null) ''
242 ip -6 addr add ${cfg.hostAddress6} dev "${name}"
244 ${optionalString (cfg.localAddress != null) ''
245 ip route add ${cfg.localAddress} dev "${name}"
247 ${optionalString (cfg.localAddress6 != null) ''
248 ip -6 route add ${cfg.localAddress6} dev "${name}"
253 if [ -n "$HOST_ADDRESS" ] || [ -n "$LOCAL_ADDRESS" ] ||
254 [ -n "$HOST_ADDRESS6" ] || [ -n "$LOCAL_ADDRESS6" ]; then
255 if [ -z "$HOST_BRIDGE" ]; then
256 ifaceHost=ve-$INSTANCE
257 ip link set dev "$ifaceHost" up
259 ${ipcall cfg "ip addr" "$HOST_ADDRESS" "hostAddress"}
260 ${ipcall cfg "ip -6 addr" "$HOST_ADDRESS6" "hostAddress6"}
261 ${ipcall cfg "ip route" "$LOCAL_ADDRESS" "localAddress"}
262 ${ipcall cfg "ip -6 route" "$LOCAL_ADDRESS6" "localAddress6"}
265 ${concatStringsSep "\n" (mapAttrsToList renderExtraVeth cfg.extraVeths)}
269 serviceDirectives = cfg: {
270 ExecReload = pkgs.writeScript "reload-container"
272 #! ${pkgs.runtimeShell} -e
273 ${nixos-container}/bin/nixos-container run "$INSTANCE" -- \
274 bash --login -c "''${SYSTEM_PATH:-/nix/var/nix/profiles/system}/bin/switch-to-configuration test"
277 SyslogIdentifier = "container %i";
279 EnvironmentFile = "-${configurationDirectory}/%i.conf";
283 RuntimeDirectory = lib.optional cfg.ephemeral "${configurationDirectoryName}/%i";
285 # Note that on reboot, systemd-nspawn returns 133, so this
286 # unit will be restarted. On poweroff, it returns 0, so the
287 # unit won't be restarted.
288 RestartForceExitStatus = "133";
289 SuccessExitStatus = "133";
291 # Some containers take long to start
292 # especially when you automatically start many at once
293 TimeoutStartSec = cfg.timeoutStartSec;
295 Restart = "on-failure";
297 Slice = "machine.slice";
300 # We rely on systemd-nspawn turning a SIGTERM to itself into a shutdown
301 # signal (SIGRTMIN+3) for the inner container.
305 DevicePolicy = "closed";
306 DeviceAllow = map (d: "${d.node} ${d.modifier}") cfg.allowedDevices;
309 kernelVersion = config.boot.kernelPackages.kernel.version;
311 bindMountOpts = { name, ... }: {
314 mountPoint = mkOption {
315 example = "/mnt/usb";
317 description = "Mount point on the container file system.";
319 hostPath = mkOption {
321 example = "/home/alice";
322 type = types.nullOr types.str;
323 description = "Location of the host path to be mounted.";
325 isReadOnly = mkOption {
328 description = "Determine whether the mounted path will be accessed in read-only mode.";
333 mountPoint = mkDefault name;
338 allowedDeviceOpts = { ... }: {
341 example = "/dev/net/tun";
343 description = "Path to device node";
345 modifier = mkOption {
349 Device node access modifier. Takes a combination
350 `r` (read), `w` (write), and
352 `systemd.resource-control(5)` man page for more
359 let flagPrefix = if d.isReadOnly then " --bind-ro=" else " --bind=";
360 mountstr = if d.hostPath != null then "${d.hostPath}:${d.mountPoint}" else "${d.mountPoint}";
361 in flagPrefix + mountstr ;
363 mkBindFlags = bs: concatMapStrings mkBindFlag (lib.attrValues bs);
366 hostBridge = mkOption {
367 type = types.nullOr types.str;
371 Put the host-side of the veth-pair into the named bridge.
372 Only one of hostAddress* or hostBridge can be given.
376 forwardPorts = mkOption {
377 type = types.listOf (types.submodule {
379 protocol = mkOption {
382 description = "The protocol specifier for port forwarding between host and container";
384 hostPort = mkOption {
386 description = "Source port of the external interface on host";
388 containerPort = mkOption {
389 type = types.nullOr types.int;
391 description = "Target port of container";
396 example = [ { protocol = "tcp"; hostPort = 8080; containerPort = 80; } ];
398 List of forwarded ports from host to container. Each forwarded port
399 is specified by protocol, hostPort and containerPort. By default,
400 protocol is tcp and hostPort and containerPort are assumed to be
401 the same if containerPort is not explicitly given.
406 hostAddress = mkOption {
407 type = types.nullOr types.str;
409 example = "10.231.136.1";
411 The IPv4 address assigned to the host interface.
412 (Not used when hostBridge is set.)
416 hostAddress6 = mkOption {
417 type = types.nullOr types.str;
421 The IPv6 address assigned to the host interface.
422 (Not used when hostBridge is set.)
426 localAddress = mkOption {
427 type = types.nullOr types.str;
429 example = "10.231.136.2";
431 The IPv4 address assigned to the interface in the container.
432 If a hostBridge is used, this should be given with netmask to access
433 the whole network. Otherwise the default netmask is /32 and routing is
434 set up from localAddress to hostAddress and back.
438 localAddress6 = mkOption {
439 type = types.nullOr types.str;
443 The IPv6 address assigned to the interface in the container.
444 If a hostBridge is used, this should be given with netmask to access
445 the whole network. Otherwise the default netmask is /128 and routing is
446 set up from localAddress6 to hostAddress6 and back.
455 additionalCapabilities = [];
457 timeoutStartSec = "1min";
462 localAddress6 = null;
471 boot.isContainer = mkOption {
475 Whether this NixOS machine is a lightweight container running
476 in another NixOS system.
480 boot.enableContainers = mkOption {
484 Whether to enable support for NixOS containers. Defaults to true
485 (at no cost if containers are not actually used).
489 containers = mkOption {
490 type = types.attrsOf (types.submodule (
491 { config, options, name, ... }:
496 A specification of the desired configuration of this
497 container, as a NixOS module.
499 type = lib.mkOptionType {
500 name = "Toplevel NixOS config";
501 merge = loc: defs: (import "${toString config.nixpkgs}/nixos/lib/eval-config.nix" {
504 extraConfig = { options, ... }: {
505 _file = "module at ${__curPos.file}:${toString __curPos.line}";
508 if options.nixpkgs?hostPlatform
509 then { inherit (host.pkgs.stdenv) hostPlatform; }
510 else { localSystem = host.pkgs.stdenv.hostPlatform; }
512 boot.isContainer = true;
513 networking.hostName = mkDefault name;
514 networking.useDHCP = false;
518 (builtins.compareVersions kernelVersion "5.8" <= 0)
519 -> config.privateNetwork
520 -> stringLength name <= 11;
522 Container name `${name}` is too long: When `privateNetwork` is enabled, container names can
523 not be longer than 11 characters, because the container's interface name is derived from it.
524 You should either make the container name shorter or upgrade to a more recent kernel that
525 supports interface altnames (i.e. at least Linux 5.8 - please see https://github.com/NixOS/nixpkgs/issues/38509
530 assertion = !lib.strings.hasInfix "_" name;
532 Names containing underscores are not allowed in nixos-containers. Please rename the container '${name}'
538 in [ extraConfig ] ++ (map (x: x.value) defs);
539 prefix = [ "containers" name ];
540 inherit (config) specialArgs;
542 # The system is inherited from the host above.
543 # Set it to null, to remove the "legacy" entrypoint's non-hermetic default.
551 example = "/nix/var/nix/profiles/per-container/webserver";
553 As an alternative to specifying
554 {option}`config`, you can specify the path to
555 the evaluated NixOS system configuration, typically a
556 symlink to a system profile.
560 additionalCapabilities = mkOption {
561 type = types.listOf types.str;
563 example = [ "CAP_NET_ADMIN" "CAP_MKNOD" ];
565 Grant additional capabilities to the container. See the
566 capabilities(7) and systemd-nspawn(1) man pages for more
574 defaultText = literalExpression "pkgs.path";
576 A path to the nixpkgs that provide the modules, pkgs and lib for evaluating the container.
578 To only change the `pkgs` argument used inside the container modules,
579 set the `nixpkgs.*` options in the container {option}`config`.
580 Setting `config.nixpkgs.pkgs = pkgs` speeds up the container evaluation
581 by reusing the system pkgs, but the `nixpkgs.config` option in the
582 container config is ignored in this case.
586 specialArgs = mkOption {
587 type = types.attrsOf types.unspecified;
590 A set of special arguments to be passed to NixOS modules.
591 This will be merged into the `specialArgs` used to evaluate
592 the NixOS configurations.
596 ephemeral = mkOption {
600 Runs container in ephemeral mode with the empty root filesystem at boot.
601 This way container will be bootstrapped from scratch on each boot
602 and will be cleaned up on shutdown leaving no traces behind.
603 Useful for completely stateless, reproducible containers.
605 Note that this option might require to do some adjustments to the container configuration,
606 e.g. you might want to set
607 {var}`systemd.network.networks.$interface.dhcpV4Config.ClientIdentifier` to "mac"
608 if you use {var}`macvlans` option.
609 This way dhcp client identifier will be stable between the container restarts.
611 Note that the container journal will not be linked to the host if this option is enabled.
615 enableTun = mkOption {
619 Allows the container to create and setup tunnel interfaces
620 by granting the `NET_ADMIN` capability and
621 enabling access to `/dev/net/tun`.
625 privateNetwork = mkOption {
629 Whether to give the container its own private virtual
630 Ethernet interface. The interface is called
631 `eth0`, and is hooked up to the interface
632 `ve-«container-name»`
633 on the host. If this option is not set, then the
634 container shares the network interfaces of the host,
635 and can bind to any port on any interface.
639 networkNamespace = mkOption {
640 type = types.nullOr types.path;
643 Takes the path to a file representing a kernel network namespace that the container
644 shall run in. The specified path should refer to a (possibly bind-mounted) network
645 namespace file, as exposed by the kernel below /proc/<PID>/ns/net. This makes the
646 container enter the given network namespace. One of the typical use cases is to give
647 a network namespace under /run/netns created by ip-netns(8).
648 Note that this option cannot be used together with other network-related options,
649 such as --private-network or --network-interface=.
653 interfaces = mkOption {
654 type = types.listOf types.str;
656 example = [ "eth1" "eth2" ];
658 The list of interfaces to be moved into the container.
662 macvlans = mkOption {
663 type = types.listOf types.str;
665 example = [ "eth1" "eth2" ];
667 The list of host interfaces from which macvlans will be
668 created. For each interface specified, a macvlan interface
669 will be created and moved to the container.
673 extraVeths = mkOption {
674 type = with types; attrsOf (submodule { options = networkOptions; });
677 Extra veth-pairs to be created for the container.
681 autoStart = mkOption {
685 Whether the container is automatically started at boot-time.
689 restartIfChanged = mkOption {
693 Whether the container should be restarted during a NixOS
694 configuration switch if its definition has changed.
698 timeoutStartSec = mkOption {
702 Time for the container to start. In case of a timeout,
703 the container processes get killed.
704 See {manpage}`systemd.time(7)`
705 for more information about the format.
709 bindMounts = mkOption {
710 type = with types; attrsOf (submodule bindMountOpts);
712 example = literalExpression ''
713 { "/home" = { hostPath = "/home/alice";
714 isReadOnly = false; };
719 An extra list of directories that is bound to the container.
723 allowedDevices = mkOption {
724 type = with types; listOf (submodule allowedDeviceOpts);
726 example = [ { node = "/dev/net/tun"; modifier = "rwm"; } ];
728 A list of device nodes to which the containers has access to.
733 type = types.listOf types.str;
735 example = [ "/var" ];
737 Mounts a set of tmpfs file systems into the container.
738 Multiple paths can be specified.
739 Valid items must conform to the --tmpfs argument
740 of systemd-nspawn. See systemd-nspawn(1) for details.
744 extraFlags = mkOption {
745 type = types.listOf types.str;
747 example = [ "--drop-capability=CAP_SYS_CHROOT" ];
749 Extra flags passed to the systemd-nspawn command.
750 See systemd-nspawn(1) for details.
754 # Removed option. See `checkAssertion` below for the accompanying error message.
755 pkgs = mkOption { visible = false; };
759 # Throw an error when removed option `pkgs` is used.
760 # Because this is a submodule we cannot use `mkRemovedOptionModule` or option `assertions`.
761 optionPath = "containers.${name}.pkgs";
762 files = showFiles options.pkgs.files;
763 checkAssertion = if options.pkgs.isDefined then throw ''
764 The option definition `${optionPath}' in ${files} no longer has any effect; please remove it.
766 Alternatively, you can use the following options:
767 - containers.${name}.nixpkgs
768 This sets the nixpkgs (and thereby the modules, pkgs and lib) that
769 are used for evaluating the container.
771 - containers.${name}.config.nixpkgs.pkgs
772 This only sets the `pkgs` argument used inside the container modules.
776 path = builtins.seq checkAssertion
777 mkIf options.config.isDefined config.config.system.build.toplevel;
782 example = literalExpression
785 { path = "/nix/var/nix/profiles/webserver";
789 { config, pkgs, ... }:
790 { services.postgresql.enable = true;
791 services.postgresql.package = pkgs.postgresql_14;
793 system.stateVersion = "${lib.trivial.release}";
799 A set of NixOS system configurations to be run as lightweight
800 containers. Each container appears as a service
802 on the host system, allowing it to be started and stopped via
803 {command}`systemctl`.
812 warnings = optional (!config.boot.enableContainers && config.containers != {})
813 "containers.<name> is used, but boot.enableContainers is false. To use containers.<name>, set boot.enableContainers to true.";
816 mapper = name: cfg: optional (cfg.networkNamespace != null && (cfg.privateNetwork || cfg.interfaces != []))
817 "containers.${name}.networkNamespace is mutally exclusive to containers.${name}.privateNetwork and containers.${name}.interfaces.";
818 in mkMerge (mapAttrsToList mapper config.containers);
821 (mkIf (config.boot.enableContainers) (let
823 description = "Container '%i'";
825 unitConfig.RequiresMountsFor = "${stateDirectory}/%i";
827 path = [ pkgs.iproute2 ];
830 root = "${stateDirectory}/%i";
834 preStart = preStartScript dummyConfig;
836 script = startScript dummyConfig;
838 postStart = postStartScript dummyConfig;
840 restartIfChanged = false;
842 serviceConfig = serviceDirectives dummyConfig;
846 (optional (config.virtualisation.containers.enable && versionOlder config.system.stateVersion "22.05") ''
847 Enabling both boot.enableContainers & virtualisation.containers on system.stateVersion < 22.05 is unsupported.
850 systemd.targets.multi-user.wants = [ "machines.target" ];
852 systemd.services = listToAttrs (filter (x: x.value != null) (
853 # The generic container template used by imperative containers
854 [{ name = "container@"; value = unit; }]
855 # declarative containers
856 ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (let
857 containerConfig = cfg // (
858 optionalAttrs cfg.enableTun
860 allowedDevices = cfg.allowedDevices
861 ++ [ { node = "/dev/net/tun"; modifier = "rwm"; } ];
862 additionalCapabilities = cfg.additionalCapabilities
863 ++ [ "CAP_NET_ADMIN" ];
867 recursiveUpdate unit {
868 preStart = preStartScript containerConfig;
869 script = startScript containerConfig;
870 postStart = postStartScript containerConfig;
871 serviceConfig = serviceDirectives containerConfig;
872 unitConfig.RequiresMountsFor = lib.optional (!containerConfig.ephemeral) "${stateDirectory}/%i"
874 (d: if d.hostPath != null then d.hostPath else d.mountPoint)
875 (builtins.attrValues cfg.bindMounts);
876 environment.root = if containerConfig.ephemeral then "/run/nixos-containers/%i" else "${stateDirectory}/%i";
878 optionalAttrs containerConfig.autoStart
880 wantedBy = [ "machines.target" ];
881 wants = [ "network.target" ] ++ (map (i: "sys-subsystem-net-devices-${i}.device") cfg.interfaces);
882 after = [ "network.target" ] ++ (map (i: "sys-subsystem-net-devices-${i}.device") cfg.interfaces);
885 config.environment.etc."${configurationDirectoryName}/${name}.conf".source
887 restartIfChanged = containerConfig.restartIfChanged;
890 )) config.containers)
893 # Generate a configuration file in /etc/nixos-containers for each
894 # container so that container@.target can get the container
897 let mkPortStr = p: p.protocol + ":" + (toString p.hostPort) + ":" + (if p.containerPort == null then toString p.hostPort else toString p.containerPort);
898 in mapAttrs' (name: cfg: nameValuePair "${configurationDirectoryName}/${name}.conf"
901 SYSTEM_PATH=${cfg.path}
902 ${optionalString cfg.privateNetwork ''
904 ${optionalString (cfg.hostBridge != null) ''
905 HOST_BRIDGE=${cfg.hostBridge}
907 ${optionalString (length cfg.forwardPorts > 0) ''
908 HOST_PORT=${concatStringsSep "," (map mkPortStr cfg.forwardPorts)}
910 ${optionalString (cfg.hostAddress != null) ''
911 HOST_ADDRESS=${cfg.hostAddress}
913 ${optionalString (cfg.hostAddress6 != null) ''
914 HOST_ADDRESS6=${cfg.hostAddress6}
916 ${optionalString (cfg.localAddress != null) ''
917 LOCAL_ADDRESS=${cfg.localAddress}
919 ${optionalString (cfg.localAddress6 != null) ''
920 LOCAL_ADDRESS6=${cfg.localAddress6}
923 ${optionalString (cfg.networkNamespace != null) ''
924 NETWORK_NAMESPACE_PATH=${cfg.networkNamespace}
926 INTERFACES="${toString cfg.interfaces}"
927 MACVLANS="${toString cfg.macvlans}"
928 ${optionalString cfg.autoStart ''
931 EXTRA_NSPAWN_FLAGS="${mkBindFlags cfg.bindMounts +
932 optionalString (cfg.extraFlags != [])
933 (" " + concatStringsSep " " cfg.extraFlags)}"
935 }) config.containers;
937 # Generate /etc/hosts entries for the containers.
938 networking.extraHosts = concatStrings (mapAttrsToList (name: cfg: optionalString (cfg.localAddress != null)
940 ${head (splitString "/" cfg.localAddress)} ${name}.containers
941 '') config.containers);
943 networking.dhcpcd.denyInterfaces = [ "ve-*" "vb-*" ];
945 services.udev.extraRules = optionalString config.networking.networkmanager.enable ''
946 # Don't manage interfaces created by nixos-container.
947 ENV{INTERFACE}=="v[eb]-*", ENV{NM_UNMANAGED}="1"
950 environment.systemPackages = [
954 boot.kernelModules = [
963 meta.buildDocsInSandbox = false;