Skip to content

Instantly share code, notes, and snippets.

@Thesola10
Last active October 21, 2023 09:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Thesola10/999b4e6d1eca84b9ce1d380ced2934dd to your computer and use it in GitHub Desktop.
Save Thesola10/999b4e6d1eca84b9ce1d380ced2934dd to your computer and use it in GitHub Desktop.
Build and spin up a NixOS configuration or flake as a systemd-nspawn container on non-NixOS systems.
#!/bin/sh
# nixos-spawn (c) Karim Vergnes <me@thesola.io>
# This script launches systemd-nspawn in an empty root, with an overlay of the
# host's Nix Store. This takes advantage of the NixOS stage 2 init's ability to
# populate an empty system automatically.
# By default, no changes are persisted anywhere -- use an additional bind mount
# to add permanent storage.
: ${NIXOS_CHANNEL:=nixpkgs}
if [[ $UID != 0 ]]
then
>&2 echo "This script is meant to be run as root. Try 'sudo $0'."
exit 1
elif [[ $# < 1 ]]
then
>&2 echo "Usage: $0 <nixos configuration file or flake ref> [systemd-nspawn options...]"
exit 1
fi
if [[ ! -d /run/nixos-container/nix/var/nix/db ]]
then
# Setting up Nix Store overlay area
mkdir -p /run/nixos-container/nix/.rw-{store,db}
mkdir -p /run/nixos-container/nix/{store,var/nix/db}
# Telling systemd-nspawn to shut up
mkdir -p /run/nixos-container/usr/bin
fi
if [[ -e $1 ]]
then
# Systematically enable the `boot.isContainer' option. This bypasses a bunch of
# errors related to bootloader config, as well as stage 2 mounts.
# We also enable systemd-networkd, a hard requirement for working network setup
# in nspawn containers. (oh and systemd-resolved too for convenience)
nix-build -E "(import <$NIXOS_CHANNEL/nixos> { configuration = builtins.toFile ''config''
''
{lib, ...}:
{ imports = [ $(realpath $1) ];
boot.isContainer = lib.mkForce true;
systemd.network.enable = lib.mkForce true;
services.resolved.enable = lib.mkForce true;
networking.useHostResolvConf = lib.mkForce false;
networking.firewall.enable = lib.mkDefault false;
}
''; }).system" -o system \
|| { >&2 echo "NixOS system failed to build. Check output above for more information."; exit 1; }
else
if nix eval --raw "$1".config.system.build.toplevel >&/dev/null 2>&1
then
[[ $(nix eval "$1".config.boot.isContainer) == "false" ]] && \
>&2 echo "WARNING: This NixOS configuration is not a container, and may fail to boot."
nix build "$1".config.system.build.toplevel -o system \
|| { >&2 echo "NixOS system failed to build. Check output above for more information."; exit 1; }
elif nix eval --raw "$1".nixosLabel >&/dev/null 2>&1
then
>&2 echo "WARNING: NixOS toplevel package passed. This is not recommended, since"
>&2 echo "I can't determine whether it is a container or not."
>&2 echo "Prefer using a \`nixosConfiguration' output."
nix build "$1" -o system \
|| { >&2 echo "NixOS system failed to build. Check output above for more information."; exit 1; }
else
>&2 echo "Flake reference \`$1' does not resolve to a NixOS configuration or toplevel package."
exit 1
fi
fi
shift 1
systemd-nspawn -xD/run/nixos-container \
--overlay=/nix/store:+/nix/.rw-store:/nix/store \
--overlay=/nix/var/nix/db:+/nix/.rw-db:/nix/var/nix/db \
"$@" $(readlink ./system)/init
@Thesola10
Copy link
Author

Thesola10 commented Apr 30, 2022

If you haven't installed Nix on your host system, and still want to use this script to invoke a NixOS container, then you may want to use the nix-build command provided by the Nix Portable project, then apply the following changes to the script:

-                --overlay=/nix/store:+/nix/.rw-store:/nix/store \
-                --overlay=/nix/var/nix/db:+/nix/.rw-db:/nix/var/nix/db \
+                --overlay=$HOME/.nix-portable/store:+/nix/.rw-store:/nix/store \
+                --overlay=$HOME/.nix-portable/var/nix/db:+/nix/.rw-db:/nix/var/nix/db \

If all you need is a dev environment, consider using Nixie

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment