Skip to content

Deploy a machine

Now that you have created a new machine, we will walk through how to install it.

Prerequisites

General Requirements

  • RAM > 2GB
  • Two Computers: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
  • Machine configuration: See our basic adding and configuring machine guide
  • Initialized secrets: See secrets for how to initialize your secrets.

Steps

  1. Create a NixOS installer image and transfer it to a bootable USB drive as described in the installer.

  2. Boot the target machine and connect it to a network that makes it reachable from your setup computer.

  • Any cloud machine if it is reachable via SSH and supports kexec.

NixOS can cause strange issues when booting in certain cloud environments.

If on Linode: Make sure that the system uses Direct Disk boot kernel (found in the configuration pannel)

Setting targetHost

{
    inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
    inputs.nixpkgs.follows = "clan-core/nixpkgs";
    inputs.flake-parts.follows = "clan-core/flake-parts";
    inputs.flake-parts.inputs.nixpkgs-lib.follows = "clan-core/nixpkgs";

    outputs =
        inputs@{ flake-parts, ... }:
        flake-parts.lib.mkFlake { inherit inputs; } {
        systems = [
            "x86_64-linux"
            "aarch64-linux"
            "x86_64-darwin"
            "aarch64-darwin"
        ];
        imports = [ inputs.clan-core.flakeModules.default ];

        clan = {
            inventory.machines = {
                jon = {
                    # targetHost will get picked up by cli commands
                    deploy.targetHost = "root@jon";
                };
            };
        };
    };
}
{
    inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
    inputs.nixpkgs.follows = "clan-core/nixpkgs";

    outputs =
        { self, clan-core, ... }:
        let
            clan = clan-core.lib.clan {
                inherit self;

                inventory.machines = {
                    jon = {
                        # targetHost will get picked up by cli commands
                        deploy.targetHost = "root@jon";
                    };
                };
            };
        in
        {
            inherit (clan.config)
                nixosConfigurations
                nixosModules
                clanInternals
                darwinConfigurations
                darwinModules
                ;
        };
}

Warning

The use of root@ in the target address implies SSH access as the root user. Ensure that the root login is secured and only used when necessary.

Identify the Target Disk

On the setup computer, SSH into the target:

setup computer
ssh root@<IP> lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT

Replace <IP> with the machine's IP or hostname if mDNS (i.e. Avahi) is available.

Which should show something like:

NAME        ID-LINK                                         FSTYPE   SIZE MOUNTPOINT
sda         usb-ST_16GB_AA6271026J1000000509-0:0                    14.9G
β”œβ”€sda1      usb-ST_16GB_AA6271026J1000000509-0:0-part1                 1M
β”œβ”€sda2      usb-ST_16GB_AA6271026J1000000509-0:0-part2      vfat     100M /boot
└─sda3      usb-ST_16GB_AA6271026J1000000509-0:0-part3      ext4     2.9G /
nvme0n1     nvme-eui.e8238fa6bf530001001b448b4aec2929              476.9G
β”œβ”€nvme0n1p1 nvme-eui.e8238fa6bf530001001b448b4aec2929-part1 vfat     512M
β”œβ”€nvme0n1p2 nvme-eui.e8238fa6bf530001001b448b4aec2929-part2 ext4   459.6G
└─nvme0n1p3 nvme-eui.e8238fa6bf530001001b448b4aec2929-part3 swap    16.8G

Look for the top-level disk device (e.g., nvme0n1 or sda) and copy its ID-LINK. Avoid using partition IDs like nvme0n1p1.

In this example we would copy nvme-eui.e8238fa6bf530001001b448b4aec2929

Tip

For advanced partitioning, see Disko templates or Disko examples.

Fill in hardware specific machine configuration

Edit the following fields inside the ./machines/<machine_name>/configuration.nix

./machines/jon/configuration.nix
{
    imports = [
    # contains your disk format and partitioning configuration.
    ../../modules/disko.nix
    # this file is shared among all machines
    ../../modules/shared.nix
    # enables GNOME desktop (optional)
    ../../modules/gnome.nix
    ];

    # Put your username here for login
    users.users.user.name = "__YOUR_USERNAME__";

    # Replace this __CHANGE_ME__ with the copied result of the lsblk command
    disko.devices.disk.main.device = "/dev/disk/by-id/__CHANGE_ME__";

    # IMPORTANT! Add your SSH key here
    # e.g. > cat ~/.ssh/id_ed25519.pub
    users.users.root.openssh.authorizedKeys.keys = [ "__YOUR_SSH_KEY__" ];

    # ...
}

Replace __YOUR_USERNAME__ with the ip of your machine, if you use avahi you can also use your hostname

Replace __CHANGE_ME__ with the appropriate ID-LINK identifier, such as nvme-eui.e8238fa6bf530001001b448b4aec2929

Replace __YOUR_SSH_KEY__ with your personal key, like ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILoMI0NC5eT9pHlQExrvR5ASV3iW9+BXwhfchq0smXUJ jon@jon-desktop

Deploy the machine

Finally deployment time! Use the following command to build and deploy the image via SSH onto your machine.

The installer will generate a password and local addresses on boot, then run ssh with these preconfigured. The installer shows it's deployment relevant information in two formats, a text form, as well as a QR code.

Sample boot screen shows:

  • Root password
  • IP address
  • Optional Tor and mDNS details
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                       β”‚
β”‚ β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚ # This is the QR Code (1)                             β”‚
β”‚ β”‚β–ˆβ–ˆ β–„β–„β–„β–„β–„ β–ˆβ–€β–„β–ˆβ–€β–ˆβ–€β–„β–ˆ β–„β–„β–„β–„β–„ β–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆ β–ˆ   β–ˆ β–ˆβ–€β–„β–„β–„β–ˆ β–€β–ˆ β–ˆ   β–ˆ β–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆ β–ˆβ–„β–„β–„β–ˆ β–ˆβ–€β–„ β–€β–„β–„β–„β–ˆ β–ˆβ–„β–„β–„β–ˆ β–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆβ–„β–„β–„β–„β–„β–„β–„β–ˆβ–„β–€ β–€β–„β–€β–„β–ˆβ–„β–„β–„β–„β–„β–„β–„β–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆβ–ˆβ–€β–€β–€ β–ˆβ–„β–„β–ˆ β–€β–„   β–„β–€β–„β–ˆ   β–ˆβ–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆβ–„β–ˆβ–ˆβ–„β–„β–ˆβ–„β–„β–€β–€β–ˆβ–ˆβ–„β–€ β–„β–„β–„ β–„β–€β–ˆβ–€β–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆ β–„β–„β–„β–„β–„ β–ˆβ–„β–„β–„β–„ β–ˆ β–ˆβ–„β–ˆ β–ˆβ–€ β–ˆβ–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆ β–ˆ   β–ˆ β–ˆ β–ˆ  β–ˆ β–„β–„β–„  β–„β–€β–€ β–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆ β–ˆβ–„β–„β–„β–ˆ β–ˆ β–„ β–„    β–„ β–€β–ˆ β–„β–ˆβ–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆβ–„β–„β–„β–„β–„β–„β–„β–ˆβ–„β–„β–„β–„β–„β–„β–ˆβ–„β–„β–„β–„β–„β–ˆβ–„β–ˆβ–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β”‚β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚                                                       β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                       β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚Root password: cheesy-capital-unwell  # password (2)                             β”‚ β”‚
β”‚ β”‚Local network addresses:                                                         β”‚ β”‚
β”‚ β”‚enp1s0           UP    192.168.178.169/24 metric 1024 fe80::21e:6ff:fe45:3c92/64 β”‚ β”‚
β”‚ β”‚enp2s0           DOWN                                                            β”‚ β”‚
β”‚ β”‚wlan0            DOWN # connect to wlan (3)                                      β”‚ β”‚
β”‚ β”‚Onion address: 6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion    β”‚ β”‚
β”‚ β”‚Multicast DNS: nixos-installer.local                                             β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ Press 'Ctrl-C' for console access                                                   β”‚
β”‚                                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. This is not an actual QR code, because it is displayed rather poorly on text sites. This would be the actual content of this specific QR code prettified:

    {
        "pass": "cheesy-capital-unwell",
        "tor": "6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion",
        "addrs": [
        "2001:9e8:347:ca00:21e:6ff:fe45:3c92"
        ]
    }
    

    To generate the actual QR code, that would be displayed use:

    echo '{"pass":"cheesy-capital-unwell","tor":"6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion","addrs":["2001:9e8:347:ca00:21e:6ff:fe45:3c92"]}' | nix run nixpkgs#qrencode -- -s 2 -m 2 -t utf8
    

  2. The root password for the installer medium. This password is autogenerated and meant to be easily typeable.

  3. See how to connect to wlan.

Tip

Use KDE Connect for easyily sharing QR codes from phone to desktop

Just run the command Option B: Cloud VM below

Deployment Commands

Using password auth

clan machines install [MACHINE] --target-host <IP> --update-hardware-config nixos-facter

Using QR JSON

clan machines install [MACHINE] --json "[JSON]" --update-hardware-config nixos-facter

Using QR image file

clan machines install [MACHINE] --png [PATH] --update-hardware-config nixos-facter

Option B: Cloud VM

clan machines install [MACHINE] --target-host <IP> --update-hardware-config nixos-facter

Success

Your machine is all set up. πŸŽ‰ πŸš€

Post-Deployment: Updating Machines

Updating

Update a single machine:

clan machines update jon

Update all machines:

clan machines update

Build Host Configuration

If a machine is too resource-limited, use another host.

If the machine does not have enough resources to run the NixOS evaluation or build itself, it is also possible to specify a build host.

During an update, the CLI will SSH into the build host and run nixos-rebuild from there.

clan {
    # ...
    machines = {
        "jon" = {
            clan.core.networking.buildHost = "root@<host_or_ip>";
        };
    };
};

Excluding from Automatic Updates

To exclude machines from being updated when running clan machines update without any machines specified, one can set the clan.deployment.requireExplicitUpdate option to true:

clan {
    # ...
    machines = {
        "jon" = {
            clan.deployment.requireExplicitUpdate = true;
        };
    };
};

This is useful for machines that are not always online or are not part of the regular update cycle.