Skip to content

Instantly share code, notes, and snippets.

@dramforever
Forked from whitequark/nixos-bite.sh
Last active October 5, 2025 04:34
Show Gist options
  • Save dramforever/bf339cb721d25892034e052765f931c6 to your computer and use it in GitHub Desktop.
Save dramforever/bf339cb721d25892034e052765f931c6 to your computer and use it in GitHub Desktop.
substrate n. portmanteau of "subdir" and "lustrate" (also a real word that means something relevant, probably)
#! /usr/bin/env bash
set -x -e -o pipefail
# PoC of nixos-substrate
#
# substrate n.
# portmanteau of "subdir" and "lustrate"
# (also a real word that means something relevant, probably)
#
# fanfiction of nixos-bite https://gist.github.com/whitequark/b2ebce6ce77f4b29e37dffc4cbd45873
# fanfiction of fanfiction of https://github.com/elitak/nixos-infect
#
# FIXME: Assumes BIOS boot, Debian, and probably a lot of details
# TODO: Make this just, not be bad, in general
#
# Goal: Replace Debian with a NixOS "installer" in an extremely cursed way
#
# After running this script:
# (rootfsdev)/nix - New nix store
# (rootfsdev)/new-root - "Installer" root directory
# (rootfsdev)/boot/grub/custom.cfg - New boot entry for "installer"
#
# After booting into the "installer", the directories are mounted as
# (rootfsdev)/new-root -> /
# (rootfsdev)/nix -> /nix
# (rootfsdev)/ -> /old-root
#
# Notably, the new ("installer") root and old root only share /new-root and
# /nix. This means that, if something goes wrong for the "installer", as long
# as you haven't broken /old-root, just picking the old boot entry will boot
# back into Debian.
#
# Once inside the "installer", install NixOS as you normally would to /old-root,
# but without reformatting it. Basically, do a "manual lustration".
#
# use via ssh: ssh root@${HOST} bash < nixos-substrate.sh
[[ -z "$NIX_CHANNEL" ]] && NIX_CHANNEL="nixos-25.05"
[[ -z "$NIX_STATE_VERSION" ]] && NIX_STATE_VERSION="$(echo $NIX_CHANNEL | sed -r 's/[a-z-]+([0-9.]+)/\1/')"
if [[ -z "$NIX_STATE_VERSION" ]]; then echo "Provide explicit NIX_STATE_VERSION= for $NIX_CHANNEL" >&2; exit 1; fi
apt-get update -y
apt-get install -y bzip2 xz-utils curl iproute2
rootfsdev=$(mount | grep "on / type" | awk '{ print $1 }')
grubdev=$(echo "$rootfsdev" | sed -r 's|p?[0-9]+$||')
bootldr="boot.loader.grub.device = \"$grubdev\";"
sshkeys=$(awk '/^[^#]*(ssh-[^#]+)$/ { print "\""$0"\"" }' < $HOME/.ssh/authorized_keys)
dns='"2620:fe::fe" "9.9.9.9"'
rm /etc/resolv.conf
echo $dns | sed -r 's|"([^"]+?)"\s*|nameserver \1\n|g' > /etc/resolv.conf
mkdir -p /etc/nixos
cat > /etc/nixos/configuration.nix <<EOF
{ modulesPath, ... }: {
system.stateVersion = "$NIX_STATE_VERSION";
nix.settings.experimental-features = "flakes nix-command";
# Hardware
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
fileSystems."/old-root" = { device = "$rootfsdev"; fsType = "auto"; };
fileSystems."/" = { device = "$rootfsdev"; fsType = "auto"; options = [ "X-mount.subdir=new-root" ]; };
fileSystems."/nix" = { device = "$rootfsdev"; fsType = "auto"; options = [ "X-mount.subdir=nix" ]; };
$bootldr
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "xen_blkfront" ];
boot.initrd.kernelModules = [ "nvme" ];
boot.initrd.supportedFilesystems = [ "ext2" "ext3" "ext4" "vfat" ];
boot.initrd.systemd.enable = true; # Required for X-mount.subdir
boot.tmp.cleanOnBoot = true;
# Networking
networking = {
useNetworkd = true;
usePredictableInterfaceNames = true;
hostName = "$(hostname -s)";
domain = "$(hostname -d)";
};
systemd.network = {
enable = true;
networks."40-wan" = {
matchConfig.Name = "en*";
dns = [ $dns ];
DHCP = "yes";
};
};
# SSH
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [ $sshkeys ];
}
EOF
mkdir -p -m 0755 /nix
curl -L -o nix https://hydra.nixos.org/job/nix/master/buildStatic.nix-cli.x86_64-linux/latest/download/1
chmod +x nix
./nix --extra-experimental-features "nix-command" \
--build-users-group "" \
build \
-I nixpkgs=channel:$NIX_CHANNEL \
-I nixos-config=/etc/nixos/configuration.nix \
-f '<nixpkgs/nixos>' config.system.build.toplevel
mkdir -p /new-root/etc/ssh
( cd / && cp etc/ssh/ssh_host_*_key* /new-root/etc/ssh || true )
cat <<END > /boot/grub/custom.cfg
menuentry "NixOS substrate" --id nixos-substrate {
insmod ext2
search -f /etc/nixos/configuration.nix --set root
linux $(realpath result/kernel) $(cat result/kernel-params) init=$(realpath result/init)
initrd $(realpath result/initrd)
}
set default="nixos-substrate"
END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment