Declarative ephemeral NixOS nspawn containers
This is a work in progress proof of concept for a simple alternative to NixOS containers. It contains NixOS modules for a host machine and a container to run declarative NixOS nspawn containers.
Imperative containers are not in scope of this project since it is the author's opinion that those are the main issue holding back the upstream NixOS container migration to proper systemd-nspawn support. Imperative containers need a separate state outside of the NixOS module system and therefore a tool to manage that state. The author suggests importing the official container tarball and using the regular imperative NixOS deployment options instead.
Highlights
- first-class integration into
machinectl
-M
flag forsystemctl
andloginctl
works as intended- uses systemd's
systemd-nspawn@.service
unit
- automatic network configuration using
systemd-networkd
- user namespaces with dynamic UID/GID allocation
- ephemeral execution so no state is being kept across restarts
- if state is needed, bind mounts can be defined in the nspawn configuration
TODO
- NixOS option interface is currently very simple
- needs more options like custom network config and bind mounts
- the whole host nix store is being bind mounted into the container
- explore if only needed store paths could be bind mounted instead
- maybe create an option to make a separate nix daemon instance available in the container
- explore how to pass credentials into the container and provide an interface
How to use this
You can consume this flake and use the provided NixOS modules. See the simple-container
check
in checks.nix
for an example. If you are not using flakes, the NixOS modules are located in
host.nix
and container.nix
.
virtualisation.nixos-nspawn-ephemeral.containers
Attribute set of containers that are configured by this module.
Type: attribute set of (submodule)
Default:
{ }
Example:
{
webserver = {
config = {
networking.firewall.allowedTCPPorts = [ 80 ];
services.nginx.enable = true;
};
};
}
virtualisation.nixos-nspawn-ephemeral.containers.<name>.config
A specification of the desired configuration of this container, as a NixOS module.
Type: Toplevel NixOS config
Example:
{ pkgs, ... }: {
networking.hostName = "foobar";
services.openssh.enable = true;
environment.systemPackages = [ pkgs.htop ];
}
virtualisation.nixos-nspawn-ephemeral.containers.<name>.network.veth.enable
Enable default veth link between host and container.
Type: boolean
Default:
true
Example:
false
virtualisation.nixos-nspawn-ephemeral.containers.<name>.network.veth.config.container
Networkd network config merged with the systemd.network.networks unit on the container side. Interface match config is already prepopulated.
Type: null or (attribute set)
Default:
null
Example:
{
networkConfig = {
Address = [
"fd42::2/64"
"10.23.42.2/28"
];
};
}
virtualisation.nixos-nspawn-ephemeral.containers.<name>.network.veth.config.host
Networkd network config merged with the systemd.network.networks unit on the host side. Interface match config is already prepopulated.
Type: null or (attribute set)
Default:
null
Example:
{
networkConfig = {
Address = [
"fd42::1/64"
"10.23.42.1/28"
];
};
}
virtualisation.nixos-nspawn-ephemeral.containers.<name>.path
As an alternative to specifying
config
, you can specify the path to
the evaluated NixOS system configuration, typically a
symlink to a system profile.
Type: path
Example:
"/nix/var/nix/profiles/my-container"
virtualisation.nixos-nspawn-ephemeral.imports
List of NixOS modules to be imported in every system evaluation when
containers.*.config
is being used.
Type: list of module
Default:
[ ]
Example:
[
{ services.getty.helpLine = "Hello world! I'm a nspawn container!"; }
inputs.lix-module.nixosModules.default
]