Keystone SystemsKS Systems

Installing NixOS with nixos-anywhere and disko

nixos-anywhere enables remote NixOS installation over SSH. Combined with disko for declarative disk partitioning, you can fully automate system provisioning.

Overview

nixos-anywhere: Installs NixOS on any Linux system accessible via SSH. Boot a live environment, run one command, get a configured NixOS system.

disko: Declarative disk partitioning. Define your partition layout in Nix, apply it consistently.

Together, they enable fully reproducible infrastructure deployment.

Prerequisites

On Your Local Machine

  • Nix installed with flakes enabled
  • SSH access to the target machine
  • Target machine booted into any Linux with SSH (rescue mode, live ISO, etc.)

On the Target Machine

  • Root SSH access
  • Network connectivity
  • At least 1GB RAM (2GB+ recommended)

Disko Configuration

Create a disko configuration defining your disk layout:

Basic ext4 Setup

# disko-config.nix
{
  disko.devices = {
    disk = {
      main = {
        device = "/dev/sda";
        type = "disk";
        content = {
          type = "gpt";
          partitions = {
            ESP = {
              type = "EF00";
              size = "512M";
              content = {
                type = "filesystem";
                format = "vfat";
                mountpoint = "/boot";
              };
            };
            root = {
              size = "100%";
              content = {
                type = "filesystem";
                format = "ext4";
                mountpoint = "/";
              };
            };
          };
        };
      };
    };
  };
}

ZFS Setup

# disko-config.nix
{
  disko.devices = {
    disk = {
      main = {
        device = "/dev/nvme0n1";
        type = "disk";
        content = {
          type = "gpt";
          partitions = {
            ESP = {
              type = "EF00";
              size = "1G";
              content = {
                type = "filesystem";
                format = "vfat";
                mountpoint = "/boot";
              };
            };
            zfs = {
              size = "100%";
              content = {
                type = "zfs";
                pool = "rpool";
              };
            };
          };
        };
      };
    };
    zpool = {
      rpool = {
        type = "zpool";
        rootFsOptions = {
          compression = "lz4";
          "com.sun:auto-snapshot" = "false";
        };
        datasets = {
          root = {
            type = "zfs_fs";
            mountpoint = "/";
          };
          nix = {
            type = "zfs_fs";
            mountpoint = "/nix";
          };
          home = {
            type = "zfs_fs";
            mountpoint = "/home";
          };
        };
      };
    };
  };
}

Flake Setup

Structure your flake for nixos-anywhere:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    disko.url = "github:nix-community/disko";
    disko.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = { self, nixpkgs, disko, ... }: {
    nixosConfigurations = {
      my-server = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          disko.nixosModules.disko
          ./disko-config.nix
          ./configuration.nix
        ];
      };
    };
  };
}

System Configuration

# configuration.nix
{ config, pkgs, ... }: {
  # Boot loader
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # For ZFS
  boot.supportedFilesystems = [ "zfs" ];
  networking.hostId = "abcd1234";  # Required for ZFS

  # Network
  networking.hostName = "my-server";
  networking.networkmanager.enable = true;

  # SSH access (important for remote management!)
  services.openssh.enable = true;
  users.users.root.openssh.authorizedKeys.keys = [
    "ssh-ed25519 AAAA... your-key"
  ];

  # Create a regular user
  users.users.admin = {
    isNormalUser = true;
    extraGroups = [ "wheel" ];
    openssh.authorizedKeys.keys = [
      "ssh-ed25519 AAAA... your-key"
    ];
  };

  system.stateVersion = "24.05";
}

Running the Installation

Basic Installation

nix run github:nix-community/nixos-anywhere -- \
  --flake .#my-server \
  root@target-ip

With SSH Key

nix run github:nix-community/nixos-anywhere -- \
  --flake .#my-server \
  -i ~/.ssh/id_ed25519 \
  root@target-ip

Test Mode (Dry Run)

nix run github:nix-community/nixos-anywhere -- \
  --flake .#my-server \
  --vm-test

This boots the configuration in a VM for testing.

What Happens

  1. nixos-anywhere connects via SSH
  2. Builds a minimal kexec image on your local machine
  3. Uploads and boots the target into this image
  4. Runs disko to partition disks
  5. Installs NixOS
  6. Reboots into the new system

The entire process takes 5-15 minutes depending on network speed and system specs.

Advanced Usage

Multiple Disks (Mirror)

{
  disko.devices = {
    disk = {
      disk1 = {
        device = "/dev/sda";
        type = "disk";
        content = {
          type = "gpt";
          partitions = {
            ESP = { /* ... */ };
            zfs = {
              size = "100%";
              content = {
                type = "zfs";
                pool = "rpool";
              };
            };
          };
        };
      };
      disk2 = {
        device = "/dev/sdb";
        type = "disk";
        content = {
          type = "gpt";
          partitions = {
            ESP = { /* ... */ };
            zfs = {
              size = "100%";
              content = {
                type = "zfs";
                pool = "rpool";
              };
            };
          };
        };
      };
    };
    zpool = {
      rpool = {
        type = "zpool";
        mode = "mirror";
        # ...
      };
    };
  };
}

Secrets During Installation

nix run github:nix-community/nixos-anywhere -- \
  --flake .#my-server \
  --extra-files /path/to/secrets \
  root@target-ip

Files in /path/to/secrets are copied to the target system.

Troubleshooting

SSH Connection Fails

Ensure root login is enabled on the target:

# On target in rescue/live mode
passwd root
sed -i 's/PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
systemctl restart sshd

Disk Not Found

Verify disk path on target:

lsblk
ls -la /dev/disk/by-id/

Use /dev/disk/by-id/ paths for reliability.

Out of Memory

The kexec phase needs RAM. Ensure at least 1.5GB available.

Next Steps