Skip to content

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
bash
git clone https://github.com/Hovirix/nixos-config.git
cd nixos-config

nixos-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.

bash
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:

bash
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:

  1. Runs disko to partition the disk (1 GB ESP + LUKS-encrypted ext4 root)
  2. Generates hardware-configuration.nix from the detected hardware
  3. Installs the full NixOS configuration

Disk Layout Created

PartitionSizeFormatMount
ESP1 GBFAT32/boot
RootRemainingLUKS → 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:

nix
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:

bash
sbctl status
sbctl verify

Post-Install: User Setup

bash
sudo passwd nixos

Server 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:

bash
# 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 -r

Critical 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

bash
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.20

This single command:

  1. Connects to the target via SSH
  2. Runs disko to partition both disks (NVMe + SATA)
  3. Creates BTRFS subvolumes with the impermanence layout
  4. Generates hardware-configuration.nix from detected hardware
  5. Installs the full NixOS configuration

Disk Layout Created

NVMe drive (WD Black SN850X 2TB):

PartitionSizeFormatMount
ESP512 MBFAT32/boot
RootRemainingLUKS → BTRFS/, /nix, /persist

SATA drive (512GB SSD):

PartitionSizeFormatMount
Backup100%XFS/backup

BTRFS subvolumes:

SubvolumeMountOptions
root/noatime, nodev, nosuid, noexec
nix/nixcompress=zstd, noatime
persist/persistcompress=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:

bash
# From your laptop:
ssh -p 22 root@192.168.1.20

# In the initrd shell:
cryptsetup-askpass
# Enter LUKS passphrase

The system will continue booting, wipe the root subvolume (impermanence), and start all services.

4. Verify

bash
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.timer

Network Configuration

The server uses a static IP. Ensure your router/DHCP server has 192.168.1.20 reserved or excluded from the DHCP pool:

nix
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).

Built with VitePress