Deployment
Both systems are deployed using nixos-anywhere, which handles partitioning (via disko), installation, and hardware config generation in a single command.
Prerequisites
- The repository cloned on your workstation
- Nix with flakes enabled on your workstation
- Root SSH access to the target machine (booted from a NixOS live USB with SSH enabled)
- The target machine's IP address
git clone https://github.com/Hovirix/nixos-config.git
cd nixos-confignixos-anywhere
Both deployments use the same tool. The --generate-hardware-config flag runs nixos-generate-config on the target and writes the result to the specified path, so you don't need to manually create hardware-configuration.nix.
nix run github:nix-community/nixos-anywhere -- \
--generate-hardware-config nixos-generate-config ./hardware-configuration.nix \
--flake <path to configuration>#<configuration name> \
--target-host root@<ip address>Laptop Deployment
Boot the laptop from a NixOS live USB with SSH enabled, then deploy from your workstation:
nix run github:nix-community/nixos-anywhere -- \
--generate-hardware-config nixos-generate-config ./modules/hardware-configuration.nix \
--flake ./laptop#laptop \
--target-host root@<laptop-ip>This single command:
- Runs disko to partition the disk (1 GB ESP + LUKS-encrypted ext4 root)
- Generates
hardware-configuration.nixfrom the detected hardware - Installs the full NixOS configuration
Disk Layout Created
| Partition | Size | Format | Mount |
|---|---|---|---|
| ESP | 1 GB | FAT32 | /boot |
| Root | Remaining | LUKS → ext4 | / |
The LUKS container is named cryptroot with --allow-discards for SSD TRIM support.
Post-Install: Secure Boot
After the first boot, Lanzaboote handles Secure Boot automatically:
lanzaboote = {
enable = true;
pkiBundle = "/var/lib/sbctl";
autoGenerateKeys.enable = true;
autoEnrollKeys = {
enable = true;
autoReboot = true;
};
};Lanzaboote generates keys, enrols them, and reboots with Secure Boot active. You may need to enable Secure Boot in BIOS/UEFI settings first.
To verify after reboot:
sbctl status
sbctl verifyPost-Install: User Setup
sudo passwd nixosServer Deployment
The server is deployed remotely over SSH from your laptop/workstation. The target machine must be booted from a NixOS live USB with root SSH access enabled.
1. Prepare SSH Host Keys for agenix
agenix needs the server's SSH host key to decrypt secrets. If deploying to new hardware, you must generate the key first and update secrets.nix:
# Generate the key pair
ssh-keygen -t ed25519 -f /tmp/server_host_key -N ""
# Add the public key to server/secrets/secrets.nix
cat /tmp/server_host_key.pub
# Re-encrypt all secrets with the new key
cd server/secrets
agenix -rCritical Step
The Ed25519 public key must match one of the keys in server/secrets/secrets.nix. Without this, agenix cannot decrypt any secrets and all services that depend on secrets will fail to start.
If redeploying to the same hardware with the same SSH host keys, this step is not needed.
2. Deploy with nixos-anywhere
nix run github:nix-community/nixos-anywhere -- \
--generate-hardware-config nixos-generate-config ./modules/hardware-configuration.nix \
--flake ./server#homelab \
--target-host root@192.168.1.20This single command:
- Connects to the target via SSH
- Runs disko to partition both disks (NVMe + SATA)
- Creates BTRFS subvolumes with the impermanence layout
- Generates
hardware-configuration.nixfrom detected hardware - Installs the full NixOS configuration
Disk Layout Created
NVMe drive (WD Black SN850X 2TB):
| Partition | Size | Format | Mount |
|---|---|---|---|
| ESP | 512 MB | FAT32 | /boot |
| Root | Remaining | LUKS → BTRFS | /, /nix, /persist |
SATA drive (512GB SSD):
| Partition | Size | Format | Mount |
|---|---|---|---|
| Backup | 100% | XFS | /backup |
BTRFS subvolumes:
| Subvolume | Mount | Options |
|---|---|---|
root | / | noatime, nodev, nosuid, noexec |
nix | /nix | compress=zstd, noatime |
persist | /persist | compress=zstd, noatime, nodev, nosuid, noexec |
3. Reboot and Remote Unlock
After nixos-anywhere completes and the server reboots, it will wait at the initrd for the LUKS passphrase:
# From your laptop:
ssh -p 22 root@192.168.1.20
# In the initrd shell:
cryptsetup-askpass
# Enter LUKS passphraseThe system will continue booting, wipe the root subvolume (impermanence), and start all services.
4. Verify
ssh nixos@192.168.1.20
# Check impermanence is working
mount | grep btrfs
# Check containers are running
machinectl list
# Check services
systemctl status cloudflared-tunnel
systemctl status restic-backups-backup.timerNetwork Configuration
The server uses a static IP. Ensure your router/DHCP server has 192.168.1.20 reserved or excluded from the DHCP pool:
networking = {
hostName = "homelab";
nameservers = [ "192.168.1.1" ];
defaultGateway = {
interface = "enp1s0";
address = "192.168.1.1";
};
interfaces.enp1s0.ipv4.addresses = [{
address = "192.168.1.20";
prefixLength = 24;
}];
};DNS Configuration
After deployment, point your router's DNS to the server (192.168.1.20) so AdGuard Home handles DNS for the LAN. This enables split-horizon DNS rewrites for local service access (e.g., immich.nemnix.site resolves to 192.168.1.20 on the LAN).