NixOS for all my system - part 2
Posted in linux on January 11, 2026 by Adrian Wyssmann ‐ 5 min read
I my last post I showed you how to configure a remote host. In this post I want to reduce the manual interaction with the remote host - remember I had to do the manual setup on the target system.
Overall approach
The 3 first steps are basically the same, I have to get the target system ready
- Download latest iso image
- Burn image to an usb stick
- Boot notebook from usb stick
- Install the remote system
Now the next steps is crucial, running the target system with an usb stick comes with ssh running. Unfortunately neither root nor default user nixos have a password set. If you use the image downloaded from the website you still have to interact with the remote system briefly by running sudo passwd nixos or sudo passwd root.
Avoid setting password on remote machine manually
Instead of using the standard NixOS ISO, you can build a custom ISO that already contains your SSH public key. When the notebook boots from this USB, it will automatically start the SSH daemon and trust your key.
You can do this using nixos-generators. Add this to your local machine (where you run your Nix commands):
nix run github:nix-community/nixos-generators -- \
--format iso \
--configuration '{ ... }: {
users.users.nixos.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAA..." ];
}'I did not test that yet.
Create the configuration
So assuming we have now a running remote system and we have an admin password. Before we can start, we need some configuration. Besides the configuration of the system (bootloader, …) as shown in my last post we also need the disc configuration. For that we will use disko - a tool that allows us to describe our disk layout (partitions, LVM, LUKS, file systems) in a .nix file.
disko.devices = {
disk = {
main = {
device = "/dev/sdb";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
end = "-1G";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
plainSwap = {
size = "100%";
content = {
type = "swap";
discardPolicy = "both";
resumeDevice = true; # resume from hiberation from this device
};
};
};
};
};
};
};
}On the github repository of disko you can find a lot of different examples, if you have a more complicated setup. For my homelab it’s simple, I only need a boot partition, a swap partition and the root partition.
The second tool we need is nixos-anywhere - a tool which allows to install NixOS on any machine over SSH. It only requires the target machine to be booted into a Linux installer (like a NixOS ISO) with SSH access.
I update my flake.nix accordingly to install the new host. For simplification I will just add a simplified version
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, disko, ... }: {
nixosConfigurations.lenovo = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./hosts/lenovo
./profiles/server
];
};
};
}hosts/lenovo directory contains host specific configuration including the disko config, So the default.nix
{ pkgs, ... }: {
imports = [
./hardware.nix
./disko.nix
];
}Whereas the hardware.nix looks like that
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
}Under profiles/server I have common network config etc. which are common for all “servers”.
Do the installation
With all the configuration in place we can trigger the installation. As the host received a ip with DHCP you need to check the ip. Then you can run
nix run github:nix-community/nixos-anywhere -- \
--flake .#lenovo \
root@<target-ip-address>So now this happens:
- SSH-Connection: nixos-anywhere connects to the target.
- Partitioning: It uses Disko to wipe the disk and create partitions exactly as defined.
- Installation: It builds your Flake locally and copies the system to the target.
- Reboot: Once finished, the machine reboots into your fresh, perfectly configured NixOS
Now we have a fully working remote system.
Aftermath
There are some things you might want to consider:
If you are using nix-sops, it’s also important to add the host key to your
.sops.yamlas described in my last postEnsure your ssh does not allow password authentication
# Enable the OpenSSH server. services.sshd.enable = true; services.openssh.settings = { PasswordAuthentication = false; PermitRootLogin = "no"; };Ensure you lock down the default user accounts
rootandnixosFor
nixosI set a password and do add my ssh-keysusers.users.nixos = { isNormalUser = true; extraGroups = [ "wheel" "networkmanager" "video" ]; hashedPasswordFile = config.sops.secrets.default_password.path; # Kill the empty string fallback initialHashedPassword = lib.mkForce null; openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOrOn3Kj/+ztMtQAaq4pVvXgTsIs1ZOqQDbsA+nJMuRM nixos@homelab from clawfinger" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFTCwPNpVjW6R9vqpKgNSWgGS5hZMZcHwexAMl7E/OI2 nixos@homelab from clawfinger" ]; };rootis disabled for login entierly and also password is setusers.users.root = { openssh.authorizedKeys.keys = lib.mkForce [ ]; hashedPasswordFile = config.sops.secrets.default_root_password.path; };
Conclusion and next
This workflow is a game-changer for my homelab. I can now reinstall my servers from scratch without ever touching a keyboard attached to the device.
As a next step, I will use a Raspberry PI as a remote host.