Recently I installed NixOS into a systemd-nspawn
container on a Debian host. I found this was quite painful.
I was using Debian stretch (testing), but backported systemd and systemd-container 226 from sid (unstable) due to https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792882 - anyone wanting to install NixOS under systemd-nspawn should make sure systemd-nspawn is working first with some simpler system (e.g. by debootstrap
ing jessie (stable))
Links to not-very-useful documentation:
configure.nix
to disable grub, but all the commands require a working nix hostSteps to reproduce:
/etc/static/os-release
on the host because Nix ships absolute symlinks, which is a bug. Per os-release(5)
, if /etc/os-release
is a symlink, it must be relative. While NixOS doesn't use /usr/lib/os-release
, the same principle applies (and there are multiple symlinks to follow - all need to be relative). This bug is obscured when the host is also NixOS because it also contains a (wrong!) /etc/static/os-release
.systemd-nspawn
to start the contents of the squashfs, and bind-mount the path you want to install the real NixOS system to /mnt. Entering it in any other way will clobber the hosts's UTS and mount namespaces - nixos-install doesn't form its own until far too late.boot.isContainer = true;
nixos-install
. Note that if /mnt/ is not a mount point, nixos-install will bail out for no good reason, there should probably be a --force if people want to mv it later.sbin/init
in the real container as a relative symlink to ../nix/var/nix/profiles/system/init
. If you were fancy, you could do some sort of menu that lets you choose a profile./etc/profile.local
containing if [ "$TERM" = vt102 ]; then TERM=xterm; fi
to fix systemd's braindead agetty settings.systemctl -bD $container_path
(or machinectl start $machine_name
or systemctl start systemd-nspawn@$machine_name
if it's in /var/lib/machines
) and enjoy!Looks useful for the first step: https://hydra.nixos.org/job/nixos/release-14.12/nixos.system_tarball_pc.x86_64-linux
As of this writing, this has gotten pretty simple (although it's still a few steps away from plug-and-play). Many thanks to those who came before me; here's what I learned from them and a little trial-and-error:
Pick your build from this list. Choose the last successful job, copy the URL of its build product, and use it below.
url=https://hydra.nixos.org/build/62686007/download/1/nixos-system-x86_64-linux.tar.xz
name=my-nixos
path=/var/lib/machines/$name
sudo machinectl pull-tar --verify=no $url $name
sudo mkdir $path/etc
sudo touch $path/etc/os-release
sudo mkdir $path/sbin
sudo ln -s /init $path/sbin/init
As with every nspawn container, you can configure it by creating an /etc/systemd/nspawn/$name.nspawn
(see man page). Here's mineāthis is the simplest way to get internet access from inside the container:
[Exec]
PrivateUsers=no
[Files]
Bind=/home
[Network]
Private=no
VirtualEthernet=no
Now you're ready to run:
sudo machinectl start $name
sudo machinectl shell $name
sudo machinectl stop $name
Before you run your first nixos-rebuild
, if your host is running an sshd, you may want to add services.openssh.enable = false;
to your configuration.nix
to prevent a port conflict.
Tested on Arch Linux, but I don't see why it wouldn't work on other systemd distros.
Oh, so not being able to downloading stuff from hydra was fixed, yay! That indeed makes things much easier already.
I tried it with building my own containerTarball, but I only fixed /etc/os-release and couldn't figure out the /init part. Have to test this soon!
I created a PR for the os-release issue at #35364 , @rhendric could you please take a look? I hoped this could take the /etc/os-release
part away from the nspawn setup, but I'm not sure anymore.
The nspawn code searching for the init system, which does not check /init
but /sbin/init
, can be found at https://github.com/systemd/systemd/blob/476f65f98a60b070bfec85842f220f0d37819f7f/src/nspawn/nspawn.c#L2477
We might be able to work around needing to modify the init part of the tarball by adding this to the Exec section:
[Exec]
Boot=no
Parameters=/init
The /init
link itself is created at: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/docker-container.nix#L25
Using the storeContents
attribute, we could either:
a) change the link from /init
to /sbin/init
b) add the /sbin/init
link on top for both docker and lxc, or only the lxc image.
c) if the [Exec]
workaround works, leave it as is, and document the remaining required setup e.g. in the https://nixos.wiki
Is there any way Hydra could automatically push this image to the Docker Hub?
The [Exec]
workaround works! So now if #35364 will be merged, we're down to no modifications required anymore!
Hm, not quite. It seems that using Boot=no
does remove the need for /sbin/init
(it also removes the need for /etc/os-release
, so #35364 isn't even necessary), but it replaces it with a need for a /usr
directory (see nspawn.c), which I had to manually create.
(Sorry for taking so long to get around to this; I tried a few times earlier but my free time always seemed to intersect with Hydra being down.)
That is really strange, it worked for me as described in #35364. But I see from the code it shouldn't have. @rhendric can you elaborate what happened on your system?
We probably should create our own base as an replacement for profile/docker.nix using /sbin/init
instead of /init
, or change it in the docker thing, so that we can boot=yes
and land in this check: https://github.com/systemd/systemd/blob/2e10cc5649193eef5117fa5186869fbb74b0481e/src/basic/stat-util.c#L148
I thought maybe it's because I tried on NixOS which currently ships the somewhat outdated systemd v214, but no, that code is also in there. Why did it work for me, then? :thinking:
Transcript:
$ name=nixos-test-charlie
$ path=/var/lib/machines/$name
$ sudo machinectl pull-tar -q --verify=no https://hydra.nixos.org/build/71761700/download/1/nixos-system-x86_64-linux.tar.xz $name
Content too large.
$ printf %s '[Exec]
> Boot=no
> Parameters=/init
> ' | sudo tee /etc/systemd/nspawn/$name.nspawn
[Exec]
Boot=no
Parameters=/init
$ sudo machinectl start $name
Job for [email protected] failed because the control process exited with error code.
See "systemctl status [email protected]" and "journalctl -xe" for details.
$ systemctl status [email protected] | cat
ā [email protected] - Container nixos-test-charlie
Loaded: loaded (/usr/lib/systemd/system/[email protected]; disabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Fri 2018-03-23 12:48:53 EDT; 1min 20s ago
Docs: man:systemd-nspawn(1)
Process: 22908 ExecStart=/usr/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=nixos-test-charlie (code=exited, status=1/FAILURE)
Main PID: 22908 (code=exited, status=1/FAILURE)
Status: "Terminating..."
Mar 23 12:48:53 adam systemd[1]: Starting Container nixos-test-charlie...
Mar 23 12:48:53 adam systemd-nspawn[22908]: Directory /var/lib/machines/nixos-test-charlie doesn't look like it has an OS tree. Refusing.
Mar 23 12:48:53 adam systemd[1]: [email protected]: Main process exited, code=exited, status=1/FAILURE
Mar 23 12:48:53 adam systemd[1]: [email protected]: Failed with result 'exit-code'.
Mar 23 12:48:53 adam systemd[1]: Failed to start Container nixos-test-charlie.
$
My host OS is Arch Linux on x86_64, in case it matters.
For reference, https://github.com/erikarvstedt/extra-container project removed this pain for me. I was able to launch NixOS container on Ubuntu 17.10. I've documented 2 main problems in https://github.com/erikarvstedt/extra-container/issues/1, of which one is NixOS problem (the check for /etc/static/os-release), and one is extra-container
problem (registers service units in wrong directory, which exists only in NixOS).
Thank you for your contributions.
This has been automatically marked as stale because it has had no activity for 180 days.
If this is still important to you, we ask that you leave a comment below. Your comment can be as simple as "still important to me". This lets people see that at least one person still cares about this. Someone will have to do this at most twice a year if there is no other activity.
Here are suggestions that might help resolve this more quickly:
Most helpful comment
As of this writing, this has gotten pretty simple (although it's still a few steps away from plug-and-play). Many thanks to those who came before me; here's what I learned from them and a little trial-and-error:
Pick your build from this list. Choose the last successful job, copy the URL of its build product, and use it below.
As with every nspawn container, you can configure it by creating an
/etc/systemd/nspawn/$name.nspawn
(see man page). Here's mineāthis is the simplest way to get internet access from inside the container:Now you're ready to run:
Before you run your first
nixos-rebuild
, if your host is running an sshd, you may want to addservices.openssh.enable = false;
to yourconfiguration.nix
to prevent a port conflict.Tested on Arch Linux, but I don't see why it wouldn't work on other systemd distros.