System Requirements: nixos-anywhere
Install NixOS everywhere via ssh
Requirements
Source Machine:
-
Supported Systems:
- Linux or macOS computers with Nix installed.
- NixOS
- Windows systems using WSL2.
Note: Should be able to build nix derivations for the target platform. Otherwise
--build-on-remote
can be used. -
Nix Installation: If Nix is not yet installed on your system, refer to the nix installation page.
Destination Machine:
The machine must be reachable over the public internet or local network. Nixos-anywhere does not support wifi networks. If a VPN is needed, define a custom installer via the --kexec flag which connects to your VPN.
-
Direct Boot Option:
- Must be already running a NixOS installer.
-
Alternative Boot Options: If not booting directly from a NixOS installer image:
- Architecture & Support: Must be operating on:
- x86-64 or aarch64 Linux systems with kexec support. Note: While most x86-64 Linux systems support kexec, if you're using an architecture other than those mentioned, you may need to specify a different kexec image manually.
- Memory Requirements:
- At least 1.5 GB of RAM (excluding swap space).
- Architecture & Support: Must be operating on:
Quickstart Guide: nixos-anywhere
Install NixOS everywhere via ssh
Introduction
This guide documents a simple installation of NixOS using nixos-anywhere on a target machine running x86_64 Linux with kexec support. The example used in this guide installs NixOS on a Hetzner cloud machine. The configuration may be different for some other instances. We will be including further examples in the How To Guide as and when they are available.
You will need:
- A flake that controls the actions to be performed
- A disk configuration containing details of the file system that will be created on the new server.
- A target machine, reachable via SSH, with your SSH public key deployed and and the privilege to either login directly as root or to use password-less sudo.
nixos-anywhere doesn’t need to be installed. You can run it directly from Numtide's repository on Github.
Details of the flake, the disk configuration and the CLI command are discussed below.
Steps required to run nixos-anywhere
-
Enable Flakes:
Ensure that flakes are enabled on your system. To enable flakes, refer to the NixOS Wiki.
-
Initialize a Flake:
The easiest way to start is to copy our example flake.nix into a new directory. This example is tailored for a virtual machine setup similar to one on Hetzner Cloud, so you might need to adapt it for your setup.
Hardware-Specific Configuration: If you're not using a virtual machine, you'll need to generate a custom hardware configuration with
nixos-generate-config
.
-
Get
nixos-generate-config
onto the Target Machine:- Option 1: If NixOS is not installed, boot into an installer without first installing NixOS.
- Option 2: Use the kexec tarball method, as described here.
-
Generate Configuration: Run the following command on the target machine:
nixos-generate-config --no-filesystems --root /mnt
This creates the necessary configuration files under
/mnt/etc/nixos/
, which you can then customize as needed and copy them to your local machine in order to include them in your flake.
-
Find SSH Key Line:
if you cloned our nixos-anywhere-example you will also replace the SSH key like this: In your configuration, locate the line that reads:# change this to your ssh key "CHANGE"
Replace the text
CHANGE
with your own SSH key. This is crucial, as you will not be able to log into the target machine post-installation without it. -
In the same directory, create a file named
disk-config.nix
. This will be used to specify the disk layout to the disko tool, which nixos-anywhere uses to partition, format and mount the disks. Again, for a simple installation you can paste the contents from the example here. This configures a standard GPT (GUID Partition Table) partition compatible with both EFI and BIOS systems, and mounts the disk as/dev/sda
. If this doesn’t meet your requirements, choose an example that suits your disk layout from the disko examples. For more information about this configuration, refer to the disko documentation. -
Run the following command to create the
flake.lock
file:nix flake lock
Optionally, you can commit these files to a repo such as Github, or you can simply reference your local directory when you run nixos-anywhere. This example uses a local directory on the source machine.
-
On the target machine, make sure you have access as root via ssh by adding your SSH key to the file
authorized_keys
in the directory/root/.ssh
Optionally, bootstrapping can also be performed through password login. For example through the
image-installer-*
provided bynix-community/nixos-images
. Assign your password to theSSH_PASS
environment variable and specify--env-password
as an additional command line option. This will providessh-copy-id
with the required password. -
(Optional) Test your nixos and disko configuration:
The following command will automatically test your nixos configuration and run disko inside a virtual machine, where
-
<path to configuration>
is the path to the directory or repository containingflake.nix
anddisk-config.nix
-
<configuration name>
must match the name that immediately follows the textnixosConfigurations.
in the flake, as indicated by the comment in the example).
nix run github:nix-community/nixos-anywhere -- --flake <path to configuration>#<configuration name> --vm-test
-
-
You can now run nixos-anywhere from the command line as shown below, where:
-
<path to configuration>
is the path to the directory or repository containingflake.nix
anddisk-config.nix
-
<configuration name>
must match the name that immediately follows the textnixosConfigurations.
in the flake, as indicated by the comment in the example). -
<ip address>
is the IP address of the target machine.nix run github:nix-community/nixos-anywhere -- --flake <path to configuration>#<configuration name> root@<ip address>
The command would look like this if you had created your files in a directory named
/home/mydir/test
and the IP address of your target machine is37.27.18.135
:nix run github:nix-community/nixos-anywhere -- --flake /home/mydir/test#hetzner-cloud root@37.27.18.135
nixos-anywhere will then run, showing various output messages at each stage. It may take some time to complete, depending on Internet speeds. It should finish by showing the messages below before returning to the command prompt.
Installation finished. No error reported. Warning: Permanently added '<ip-address>' (ED25519) to the list of known hosts
When this happens, the target server will have been overwritten with a new installation of NixOS. Note that the server's public SSH key will have changed.
If you have previously accessed this server using SSH, you may see the following message the next time you try to log in to the target.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the ED25519 key sent by the remote host is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. Please contact your system administrator. Add correct host key in ~/.ssh/known_hosts to get rid of this message. Offending ECDSA key in ~/.ssh/known_hosts:6 remove with: ssh-keygen -f ~/.ssh/known_hosts" -R "<ip address>" Host key for <ip_address> has changed and you have requested strict checking. Host key verification failed.
This is because the
known_hosts
file in the.ssh
directory now contains a mismatch, since the server has been overwritten. To solve this, use a text editor to remove the old entry from theknown_hosts
file. The next connection attempt will then treat this as a new server.The error message line
Offending ECDSA key in ~/.ssh/known_hosts:
gives the line number that needs to be removed from theknown_hosts
file.The new server's configurations are defined in the flake.
nixos-anywhere
does not createetc/nixos/configuration.nix
, since it expects the server to be administered remotely. Any future changes to the configuration should be made to the flake, and you would reference this flake when doing the nixos-rebuild command or a deployment tool of your choice i.e. colmena, nixinate.This example can be run from the machine itself for updating (replace
<URL to your flake>
with your flake i.e..#
if your flake is in the current directory):nixos-rebuild switch --flake <URL to your flake>
You can also run
nixos-rebuild
to update a machine remotely, if you have set up an openssh server and your ssh key for the root user:nixos-rebuild switch --flake <URL to your flake> --target-host "root@<ip address>"
For more information on different use cases of nixos-anywhere please refer to the How to Guide, and for more technical information and explanation of known error messages, refer to the Reference Manual.
-
How To Guide: nixos-anywhere
Install NixOS everywhere via ssh
Contents
Installing on a machine with no operating system
Secrets and full disk encryption
Installing on a machine with no operating system
If your machine doesn't currently have an operating system installed, you can
still run nixos-anywhere
remotely to automate the install. To do this, you
would first need to boot the target machine from the standard NixOS installer.
You can either boot from a USB or use netboot
.
The NixOS installation guide has detailed instructions on how to boot the installer.
When you run nixos-anywhere
, it will determine whether a NixOS installer is
present by checking whether the /etc/os-release
file contains the identifier
VARIANT_ID=installer
. This identifier is available on releases NixOS 23.05 or
later.
If an installer is detected, nixos-anywhere
will not attempt to kexec
into
its own image. This is particularly useful for targets that don't have enough
RAM for kexec
or don't support kexec
.
NixOS starts an SSH server on the installer by default, but you need to set a
password in order to access it. To set a password for the nixos
user, run the
following command in a terminal on the NixOS machine:
passwd
If you don't know the IP address of the installer on your network, you can find it by running the following command:
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
altname enp0s3
altname ens3
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0
valid_lft 86385sec preferred_lft 75585sec
inet6 fec0::5054:ff:fe12:3456/64 scope site dynamic mngtmpaddr noprefixroute
valid_lft 86385sec preferred_lft 14385sec
inet6 fe80::5054:ff:fe12:3456/64 scope link
valid_lft forever preferred_lft forever
This will display the IP addresses assigned to your network interface(s),
including the IP address of the installer. In the example output below, the
installer's IP addresses are 10.0.2.15
, fec0::5054:ff:fe12:3456
, and
fe80::5054:ff:fe12:3456%eth0
:
To test if you can connect and your password works, you can use the following SSH command (replace the IP address with your own):
ssh -v nixos@fec0::5054:ff:fe12:3456
You can then use the IP address to run nixos-anywhere
like this:
nix run github:nix-community/nixos-anywhere -- --flake '.#myconfig' nixos@fec0::5054:ff:fe12:3456
This example assumes a flake in the current directory containing a configuration
named myconfig
.
Using your own kexec image
By default, nixos-anywhere
downloads the kexec image from the
NixOS images repository.
However, you can provide your own kexec
image file if you need to use a
different one. This is particularly useful for architectures other than x86_64
and aarch64
, since they don't have a pre-build image.
To do this, use the --kexec
command line switch followed by the path to your
image file. The image will be uploaded prior to execution.
Here's an example command that demonstrates how to use a custom kexec image with
nixos-anywhere
:
nix run github:nix-community/nixos-anywhere -- \
--kexec "$(nix build --print-out-paths github:nix-community/nixos-images#packages.aarch64-linux.kexec-installer-nixos-unstable-noninteractive)/nixos-kexec-installer-noninteractive-aarch64-linux.tar.gz" \
--flake 'github:your-user/your-repo#your-system' \
root@yourip
Make sure to replace github:your-user/your-repo#your-system
with the
appropriate Flake URL representing your NixOS configuration.
The example above assumes that your local machine can build for aarch64 in one of the following ways:
-
Natively
-
Through a remote builder
-
By emulating the architecture with qemu using the following NixOS configuration:
{
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
}
Secrets and full disk encryption
The nixos-anywhere
utility offers the capability to install secrets onto a
target machine. This feature is particularly beneficial when you want to
bootstrap secrets management tools such as
sops-nix or
agenix, which rely on machine-specific
secrets to decrypt other uploaded secrets.
Example: Decrypting an OpenSSH Host Key with pass
In this example, we demonstrate how to use a script to decrypt an OpenSSH host
key from the pass
password manager and subsequently pass it to
nixos-anywhere
during the installation process:
#!/usr/bin/env bash
# Create a temporary directory
temp=$(mktemp -d)
# Function to cleanup temporary directory on exit
cleanup() {
rm -rf "$temp"
}
trap cleanup EXIT
# Create the directory where sshd expects to find the host keys
install -d -m755 "$temp/etc/ssh"
# Decrypt your private key from the password store and copy it to the temporary directory
pass ssh_host_ed25519_key > "$temp/etc/ssh/ssh_host_ed25519_key"
# Set the correct permissions so sshd will accept the key
chmod 600 "$temp/etc/ssh/ssh_host_ed25519_key"
# Install NixOS to the host system with our secrets
nixos-anywhere --extra-files "$temp" --flake '.#your-host' root@yourip
Example: Uploading Disk Encryption Secrets
In a similar vein, nixos-anywhere
can upload disk encryption secrets, which
are necessary during formatting with disko. Here's an example that demonstrates
how to provide your disk encryption password as a file or via the pass
utility
to nixos-anywhere
:
# Write your disk encryption password to a file
echo "my-super-safe-password" > /tmp/disk-1.key
# Call nixos-anywhere with disk encryption keys
nixos-anywhere \
--disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
--disk-encryption-keys /tmp/disk-2.key <(pass my-disk-encryption-password) \
--flake '.#your-host' \
root@yourip
In the above example, replace "my-super-safe-password"
with your actual
encryption password, and my-disk-encryption-password
with the relevant entry
in your pass password store. Also, ensure to replace '.#your-host'
and
root@yourip
with your actual flake and IP address, respectively.
Example: Using existing SSH host keys
If the system contains existing trusted /etc/ssh/ssh_host_*
SSH host keys and
certificates, nixos-anywhere
can copy them in case they are necessary during
installation and system activation.
nixos-anywhere --copy-host-keys --flake '.#your-host' root@yourip
This would copy /etc/ssh/ssh_host_*
to /mnt
after kexec but before
installation, ignoring files that already exist in destination.
Use without flakes
While nixos-anywhere
is designed to work optimally with Nix Flakes, it also
supports the traditional approach without flakes. This document outlines how to
use nixos-anywhere
without relying on flakes. You will need to
import the disko nixos module
in your NixOS configuration and define disko devices as described in the
examples.
Generate Required Store Paths
Before you can use nixos-anywhere
without flakes, you'll need to manually
generate the paths for the NixOS system toplevel and disk image. The paths are
generated using nix-build
and are necessary for executing nixos-anywhere
.
Generating NixOS System Toplevel:
Execute the following command to generate the store path for the NixOS system toplevel:
nix-build -I nixos-config=/etc/nixos/configuration.nix -E '(import <nixpkgs/nixos> {}).config.system.build.toplevel'
This will output a path in /nix/store
that corresponds to the system toplevel,
which includes all the software and configurations for the system. Make note of
this path for later use.
Generating Disk Image without Dependencies:
To generate the disk image without dependencies, execute:
nix-build -I nixos-config=/etc/nixos/configuration.nix -E '(import <nixpkgs/nixos> {}).config.system.build.diskoNoDeps'
This will also output a script path in /nix/store
that will format your disk.
Keep this path handy as well.
Running NixOS-Anywhere
With both paths in hand, you can execute nixos-anywhere
as follows:
nixos-anywhere --store-paths /nix/store/[your-toplevel-path] /nix/store/[your-disk-image-path]
Replace [your-toplevel-path]
and [your-disk-image-path]
with the
corresponding store paths you generated earlier.
Terraform
The nixos-anywhere terraform modules allow you to use Terraform for installing and updating NixOS. It simplifies the deployment process by integrating nixos-anywhere functionality.
Our terraform module requires the null and external provider.
You can get these by from nixpkgs like this:
nix-shell -p '(pkgs.terraform.withPlugins (p: [ p.null p.external ]))'
You can add this expression the packages
list in your devshell in flake.nix or
in shell.nix.
Checkout out the module reference for examples and module parameter on how to use the modules.
Nix-channels / NIX_PATH
nixos-anywhere does not install channels onto the new system by default to save time and disk space. This for example results in errors like:
(stack trace truncated; use '--show-trace' to show the full trace)
error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I)
at «none»:0: (source not available)
when using tools like nix-shell/nix-env that rely on NIX_PATH
beeing set.
Solution 1: Set the NIX_PATH
via nixos configuration (recommended)
Instead of stateful channels, one can also populate the NIX_PATH
using nixos
configuration instead:
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# ... other inputs
outputs = { nixpkgs, ... }:
{
nixosConfigurations.yoursystem = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; # adapt to your actual system
modules = [
# This line will populate NIX_PATH
{ nix.nixPath = [ "nixpkgs=${inputs.nixpkgs}" ]; }
# ... other modules and your configuration.nix
];
};
};
}
Advantage: This solution will be automatically kept up-to-date everytime the flake is updated.
In your shell you will see something in your $NIX_PATH
:
$ echo $NIX_PATH
/root/.nix-defexpr/channels:nixpkgs=/nix/store/8b61j28rpy11dg8hanbs2x710d8w3v0d-source
Solution 2: Manually add the channel
On the installed machine, run:
$ nix-channel --add https://nixos.org/channels/nixos-unstable nixos
$ nix-channel --update
NixOS-anywhere on IPv6-only targets
As GitHub engineers still haven't enabled the IPv6 switch, the kexec image hosted on GitHub, cannot be used unfortunately on IPv6-only hosts. However it is possible to use an IPv6 proxy for GitHub content like that:
nixos-anywhere \
--kexec https://gh-v6.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-kexec-installer-noninteractive-x86_64-linux.tar.gz \
...
This proxy is hosted by numtide. It also works for IPv4.
Alternatively it is also possible to reference a local file:
nixos-anywhere \
--kexec ./nixos-kexec-installer-noninteractive-x86_64-linux.tar.gz \
...
This tarball will be then uploaded via sftp to the target.
Reference Manual: nixos-anywhere
Install NixOS everywhere via ssh
TODO: Populate this guide properly
Contents
Explanation of known error messages
Command Line Usage
Usage: nixos-anywhere [options] <ssh-host>
Options:
* -f, --flake <flake_uri>
set the flake to install the system from.
* -i <identity_file>
selects which SSH private key file to use.
* -p, --ssh-port <ssh_port>
set the ssh port to connect with
* --ssh-option <ssh_option>
set an ssh option
* -L, --print-build-logs
print full build logs
* -s, --store-paths <disko-script> <nixos-system>
set the store paths to the disko-script and nixos-system directly
if this is give, flake is not needed
* --no-reboot
do not reboot after installation, allowing further customization of the target installation.
* --kexec <path>
use another kexec tarball to bootstrap NixOS
* --post-kexec-ssh-port <ssh_port>
after kexec is executed, use a custom ssh port to connect. Defaults to 22
* --copy-host-keys
copy over existing /etc/ssh/ssh_host_* host keys to the installation
* --stop-after-disko
exit after disko formatting, you can then proceed to install manually or some other way
* --extra-files <file...>
files to copy into the new nixos installation
* --disk-encryption-keys <remote_path> <local_path>
copy the contents of the file or pipe in local_path to remote_path in the installer environment,
after kexec but before installation. Can be repeated.
* --no-substitute-on-destination
disable passing --substitute-on-destination to nix-copy
* --debug
enable debug output
* --option <key> <value>
nix option to pass to every nix related command
* --from <store-uri>
URL of the source Nix store to copy the nixos and disko closure from
* --build-on-remote
build the closure on the remote machine instead of locally and copy-closuring it
* --vm-test
build the system and test the disk configuration inside a VM without installing it to the target.
Explanation of known error messages
TODO: Add additional error messages and meanings. Fill in missing explanations
This section lists known error messages and their explanations. Some explanations may refer to the following CLI syntax:
nix run github:nix-community/nixos-anywhere -- --flake <path to configuration>#<configuration name> root@<ip address>
This list is not comprehensive. It's possible you may encounter errors that originate from the underlying operating system. These should be documented in the relevant operating system manual.
Id | Message | Explanation |
---|---|---|
1 | Failure unpacking initrd | You don't have enough RAM to hold kexec |
2 | Flake <flake_url> does not provide attirbute | The configuration name you specified in your flake URI is not defined as a NixOS configuration in your flake eg if your URI was mydir#myconfig, then myconfig should be included in the flake as nixosConfigurations.myconfig |
3 | Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri. | As for error #2 |
For example, to use the output nixosConfigurations.foo from the flake.nix, append "#foo" to the flake-uri | ||
4 | Retrieving host facts via ssh failed. Check with --debug for the root cause, unless you have done so already | TODO: Explain |
5 | ssh-host must be set | <ip_address> has not been supplied |
6 | <disko_script> and <nixos_system> must be existing store-paths | This occurs if the -s switch has been used to specify the disko script and store path correctly, and the scripts cannot be found at the given URI |
7 | flake must be set | This occurs if both the -flake option (use a flake) and the -s option (specify paths directly) have been omitted. Either one or the other must be specified. |
8 | no tar command found, but required to unpack kexec tarball | The destination machine does not have a tar command available. This is needed to unpack the kexec . |
9 | no setsid command found, but required to run the kexec script under a new session | The destination machine does not have the setsid command available |
10 | This script requires Linux as the operating system, but got | The destination machine is not running Linux |
11 | The default kexec image only support x86_64 cpus. Checkout https://github.com/nix-community/nixos-anywhere/#using-your-own-kexec-image for more information. | By default, nixos-anywhere uses its own kexec image, which will only run on x86_64 CPUs. For other CPU types, you can use your own kexec image instead. Refer to the How To Guide for instructions. |
12 | Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri. | This is a disko error. As for Error #2 |
For example, to use the output diskoConfigurations.foo from the flake.nix, append "#foo" to the flake-uri. | ||
13 | mode must be either create, mount or zap_create_mount | This is a disko error. The disko switches have not been used correctly. This could happen if you supplied your own disko script using the -s option |
14 | disko config must be an existing file or flake must be set | This is a disko error. This will happen if the disko.devices entry in your flake doesn't match the name of a file in the same location as your flake. |