1
0
Fork 0

properly pass repo dir for prod, be it with hard-coded TF init

This commit is contained in:
kiara Grouwstra 2025-03-26 09:57:02 +01:00
parent ecc41a7dfd
commit 2365d9a044
Signed by: kiara
SSH key fingerprint: SHA256:COspvLoLJ5WC5rFb9ZDe5urVCkK4LJZOsjfF4duRJFU
137 changed files with 8682 additions and 29 deletions

View file

@ -43,7 +43,10 @@
pre-commit.settings.hooks =
let
## Add a directory here if pre-commit hooks shouldn't apply to it.
optout = [ "npins" ];
optout = [
"npins"
"launch/.terraform"
];
excludes = map (dir: "^${dir}/") optout;
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
in

2
launch/.gitignore vendored
View file

@ -1,3 +1,3 @@
.terraform/
# .terraform/
.terraform.tfstate.lock.info
terraform.tfstate*

View file

@ -0,0 +1 @@
/nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source

View file

@ -0,0 +1 @@
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"mastodon","Source":"./vm","Dir":"vm"},{"Key":"mastodon.deploy","Source":"file:///nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source//terraform/all-in-one","Dir":".terraform/modules/mastodon.deploy/terraform/all-in-one"},{"Key":"mastodon.deploy.install","Source":"../install","Dir":"/nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source/terraform/install"},{"Key":"mastodon.deploy.nixos-rebuild","Source":"../nixos-rebuild","Dir":"/nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source/terraform/nixos-rebuild"},{"Key":"mastodon.deploy.partitioner-build","Source":"../nix-build","Dir":"/nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source/terraform/nix-build"},{"Key":"mastodon.deploy.system-build","Source":"../nix-build","Dir":"/nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source/terraform/nix-build"},{"Key":"peertube","Source":"./vm","Dir":"vm"},{"Key":"peertube.deploy","Source":"file:///nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source//terraform/all-in-one","Dir":".terraform/modules/peertube.deploy/terraform/all-in-one"},{"Key":"peertube.deploy.install","Source":"../install","Dir":".terraform/modules/peertube.deploy/terraform/install"},{"Key":"peertube.deploy.nixos-rebuild","Source":"../nixos-rebuild","Dir":".terraform/modules/peertube.deploy/terraform/nixos-rebuild"},{"Key":"peertube.deploy.partitioner-build","Source":"../nix-build","Dir":".terraform/modules/peertube.deploy/terraform/nix-build"},{"Key":"peertube.deploy.system-build","Source":"../nix-build","Dir":".terraform/modules/peertube.deploy/terraform/nix-build"},{"Key":"pixelfed","Source":"./vm","Dir":"vm"},{"Key":"pixelfed.deploy","Source":"file:///nix/store/y5ripxky2azz90fzpsj6383f1vljrxkg-source//terraform/all-in-one","Dir":".terraform/modules/pixelfed.deploy/terraform/all-in-one"},{"Key":"pixelfed.deploy.install","Source":"../install","Dir":".terraform/modules/pixelfed.deploy/terraform/install"},{"Key":"pixelfed.deploy.nixos-rebuild","Source":"../nixos-rebuild","Dir":".terraform/modules/pixelfed.deploy/terraform/nixos-rebuild"},{"Key":"pixelfed.deploy.partitioner-build","Source":"../nix-build","Dir":".terraform/modules/pixelfed.deploy/terraform/nix-build"},{"Key":"pixelfed.deploy.system-build","Source":"../nix-build","Dir":".terraform/modules/pixelfed.deploy/terraform/nix-build"}]}

View file

@ -0,0 +1,23 @@
To run `nixos-anywhere` from the repo:
```console
nix run . -- --help
```
To format the code:
```console
nix fmt
```
To run all tests:
```console
nix flake check -vL
```
To run an individual test:
```
nix build .#checks.x86_64-linux.from-nixos -vL
```

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Numtide
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,123 @@
# nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" width="150" height="150">
[Documentation Index](docs/INDEX.md)
## README
Setting up a new machine is time-consuming, and becomes complicated when it
needs to be done remotely. If you're installing NixOS, the **nixos-anywhere**
tool allows you to pre-configure the whole process including:
- Disk partitioning and formatting
- Configuring and installing NixOS
- Installing additional files and software
You can then initiate an unattended installation with a single CLI command.
Since **nixos-anywhere** can access the new machine using SSH, it's ideal for
remote installations.
Once you have initiated the command, there is no need to 'babysit' the
installation. It all happens automatically.
You can use the stored configuration to repeat the same installation if you need
to.
## Overview
If you have machines on a mix of platforms, you'll need a common installation
solution that works anywhere. **nixos-anywhere** is ideal in this situation.
**nixos-anywhere** can be used equally well for cloud servers, bare metal
servers such as Hetzner, and local servers accessible via a LAN. You can create
standard configurations, and use the same configuration to create identical
servers anywhere.
You first create Nix configurations to specify partitioning, formatting and
NixOS configurations. Further options can be controlled by a flake and by
run-time switches.
Once the configuration has been created, a single command will:
- Connect to the remote server via SSH
- Detect whether a NixOS installer is present; if not, it will use the Linux
`kexec` tool to boot into a Nixos installer.
- Use the [disko](https://github.com/nix-community/disko) tool to partition and
format the hard drive
- Install NixOS
- Optionally install any Nix packages and other software required.
- Optionally copy additional files to the new machine
It's also possible to use **nixos-anywhere** to simplify the installation on a
machine that has no current operating system, first booting from a NixOS
installer image. This feature is described in the
[how-to guide](./docs/howtos/no-os.md#installing-on-a-machine-with-no-operating-system).
It's useful because you can pre-configure your required software and
preferences, and build the new machine with a single command.
**Important Note:** Never use a production server as the target. It will be
completely overwritten and all data lost. This tool should only be used for
commissioning a new computer or repurposing an old machine once all important
data has been migrated.
## Prerequisites
- Source Machine:
- Can be any machine with Nix installed, e.g. a NixOS machine.
- Target Machine:
- Unless you're using the option to boot from a NixOS installer image, or
providing your own `kexec` image, it must be running x86-64 Linux with kexec
support. Most `x86_64` Linux systems do have kexec support. By providing
your own [image](./docs/howtos/custom-kexec.md#using-your-own-kexec-image)
you can also perform kexec for other architectures eg aarch64
- 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.
- When `kexec` is used the target must have at least 1 GB of RAM, excluding
swap.
## How to use nixos-anywhere
The [Quickstart Guide](./docs/quickstart.md) gives more information on how to
run **nixos-anywhere** in its simplest form. For more specific instructions to
suit individual requirements, see the [How To Guide](./docs/howtos/INDEX.md).
## Related Tools
**nixos-anywhere** makes use of the
[disko](https://github.com/nix-community/disko) tool to handle the partitioning
and formatting of the disks.
## Contact
For questions, come join us in the
[nixos-anywhere](https://matrix.to/#/#nixos-anywhere:nixos.org) matrix room.
## Licensing and Contribution details
This software is provided free under the
[MIT License](https://opensource.org/licenses/MIT).
---
This project is supported by [Numtide](https://numtide.com/).
![Untitledpng](https://codahosted.io/docs/6FCIMTRM0p/blobs/bl-sgSunaXYWX/077f3f9d7d76d6a228a937afa0658292584dedb5b852a8ca370b6c61dabb7872b7f617e603f1793928dc5410c74b3e77af21a89e435fa71a681a868d21fd1f599dd10a647dd855e14043979f1df7956f67c3260c0442e24b34662307204b83ea34de929d)
We are a team of independent freelancers that love open source.  We help our
customers make their project lifecycles more efficient by:
- Providing and supporting useful tools such as this one
- Building and deploying infrastructure, and offering dedicated DevOps support
- Building their in-house Nix skills, and integrating Nix with their workflows
- Developing additional features and tools
- Carrying out custom research and development.
[Contact us](https://numtide.com/contact) if you have a project in mind, or if
you need help with any of our supported tools, including this one. We'd love to
hear from you.

View file

@ -0,0 +1,11 @@
# Table of Content: - nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="149">
- [README](../README.md)
- [Quickstart](./quickstart.md)
- [System Requirements](./requirements.md)
- [How to Guide](./howtos/INDEX.md)
- [Reference](./reference.md)

View file

@ -0,0 +1,26 @@
# Summary: - nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="149">
The **nixos-anywhere** tool allows you to pre-configure the whole process of
installing NixOS, and run the install remotely with a single CLI command.
Refer to the following documentation for more information.
[System Requirements](./requirements.md): CPU and memory requirements
[Quickstart](./quickstart.md): Instructions for a typical installation
[How to Guide](./howtos/INDEX.md): Instructions for non-typical use cases
- [Installing on a machine with no operating system](./howtos/no-os.md)
- [Using your own kexec image](./howtos/custom-kexec.md)
- [Secrets and full disk encryption](./howtos/secrets.md)
- [Use without flakes](./howtos/use-without-flakes.md)
- [Terraform](./howtos/terraform.md)
- [Nix-channels / `NIX_PATH`](./howtos/nix-path.md)
- [IPv6-only targets](./howtos/ipv6.md)
[Reference](./reference.md): Reference Guide

View file

@ -0,0 +1,6 @@
[book]
authors = [ ]
language = "en"
multilingual = false
src = "."
title = "nixos-anywhere - install NixOS everywhere"

View file

@ -0,0 +1,63 @@
# CLI
```
Usage: nixos-anywhere [options] [<ssh-host>]
Options:
* -f, --flake <flake_uri>
set the flake to install the system from.
* --target-host <ssh-host>
specified the SSH target host to deploy onto.
* -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
* --env-password
set a password used by ssh-copy-id, the password should be set by
the environment variable SSHPASS
* -s, --store-paths <disko-script> <nixos-system>
set the store paths to the disko-script and nixos-system directly
if this is given, 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
* --kexec-extra-flags
extra flags to add into the call to kexec, e.g. "--no-sync"
* --ssh-store-setting <key> <value>
ssh store settings appended to the store URI, e.g. "compress true". <value> needs to be URI encoded.
* --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 <path>
contents of local <path> are recursively copied to the root (/) of the new NixOS installation. Existing files are overwritten
Copied files will be owned by root. See documentation for details.
* --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.
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
```

View file

@ -0,0 +1,21 @@
{
perSystem =
{ pkgs, lib, ... }:
{
packages.docs =
pkgs.runCommand "nixos-anywhere-docs"
{
passthru.serve = pkgs.writeShellScriptBin "serve" ''
set -euo pipefail
cd docs
workdir=$(${pkgs.coreutils}/bin/mktemp -d)
trap 'rm -rf "$workdir"' EXIT
${pkgs.mdbook}/bin/mdbook serve --dest-dir "$workdir"
'';
}
''
cp -r ${lib.cleanSource ./.}/* .
${pkgs.mdbook}/bin/mdbook build --dest-dir "$out"
'';
};
}

View file

@ -0,0 +1 @@
# How to Guide

View file

@ -0,0 +1,29 @@
# How To Guide: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="129">
[Documentation Index](./INDEX.md)
## Contents
[Installing on a machine with no operating system](./no-os.md)
[Kexec on systems with limited RAM](./limited-ram.md)
[Copying files to the new installation](./extra-files.md)
[Using your own kexec image](./custom-kexec.md)
[Repair installations without wiping data](./disko-modes.md)
[Secrets and full disk encryption](./secrets.md)
[Use without flakes](./use-without-flakes.md)
[Terraform](./terraform.md)
[Nix-channels / `NIX_PATH`](./nix-path.md)
[IPv6-only targets](./ipv6.md)

View file

@ -0,0 +1,40 @@
# Using your own kexec image
By default, `nixos-anywhere` downloads the kexec image from the
[NixOS images repository](https://github.com/nix-community/nixos-images#kexec-tarballs).
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:
```nix
{
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
}
```

View file

@ -0,0 +1,19 @@
# Repair installations without wiping data
By default, nixos-anywhere will reformat all configured disks before running the
installation. However it is also possible to mount the filesystems of an
existing installation and run `nixos-install`. This is useful to recover from a
misconfigured NixOS installation by first booting into a NixOS installer or
recovery system.
To only mount existing filesystems, add `--disko-mode mount` to
`nixos-anywhere`:
```
nix run github:nix-community/nixos-anywhere -- --disko-mode mount --flake <path to configuration>#<configuration name> --target-host root@<ip address>
```
1. This will first boot into a nixos-installer
2. Mounts disks with disko
3. Runs nixos-install based on the provided flake
4. Reboots the machine.

View file

@ -0,0 +1,114 @@
# Copying files to the new installation
The `--extra-files <path>` option allows copying files to the target host after
installation.
The contents of the `<path>` is recursively copied and overwrites the targets
root (/). The contents _must_ be in a structure and permissioned as it should be
on the target.
In this way, there is no need to repeatedly pass arguments (eg: a fictional
argument: `--copy <source> <dest>`) to `nixos-anywhere` to complete the intended
outcome.
The path and directory structure passed to `--extra-files` should be prepared
beforehand.
This allows a simple programmatic invocation of `nixos-anywhere` for multiple
hosts.
## Simple Example
You want `/etc/ssh/ssh_host_*` and `/persist` from the local system on the
target. The `<path>` contents will look like this:
```console
$ cd /tmp
$ root=$(mktemp -d)
$ sudo cp --verbose --archive --parents /etc/ssh/ssh_host_* ${root}
$ cp --verbose --archive --link /persist ${root}
```
The directory structure would look like this:
```console
drwx------ myuser1 users 20 tmp.d6nx5QUwPN
drwxr-xr-x root root 6 ├── etc
drwx------ myuser1 users 160 │ └── ssh
.rw------- root root 399 │ ├── ssh_host_ed25519_key
.rw-r--r-- root root 91 │ ├── ssh_host_ed25519_key.pub
drwxr-xr-x myuser1 users 22 └── persist
drwxr-xr-x myuser1 users 14 ├── all
drwxr-xr-x myuser1 users 22 │ ├── my
.rw-r--r-- myuser1 users 6 │ │ ├── test3
drwxr-xr-x myuser1 users 10 │ │ └── things
.rw-r--r-- myuser1 users 6 │ │ └── test4
.rw-r--r-- myuser1 users 6 │ └── test2
drwxr-xr-x myuser1 users 0 ├── blah
.rw-r--r-- myuser1 users 6 └── test
```
**NOTE**: Permissions will be copied, but ownership on the target will be root.
Then pass $root like:
> nixos-anywhere --flake ".#" --extra-files $root --target-host root@newhost
## Programmatic Example
```sh
for host in host1 host2 host3; do
root="target/${host}"
install -d -m755 ${root}/etc/ssh
ssh-keygen -A -C root@${host} -f ${root}
nixos-anywhere --extra-files "${root}" --flake ".#${host}" --target-host "root@${host}"
done
```
## Considerations
### Ownership
The new system may have differing UNIX user and group id's for users created
during installation.
When the files are extracted on the remote the copied data will be owned by
root.
If you wish to change the ownership after the files are copied onto the system,
you can use the `--chown` option.
For example, if you did `--chown /home/myuser/.ssh 1000:100`, this would equate
to running `chown -R /home/myuser/.ssh 1000:100` where the uid is 1000 and the
gid is 100. **Only do this when you can _guarantee_ what the uid and gid will
be.**
### Symbolic Links
Do not create symbolic links to reference data to copy.
GNU `tar` is used to do the copy over ssh. It is an archival tool used to
re/store directory structures as is. Thus `tar` copies symbolic links created
with `ln -s` by default. It does not follow them to copy the underlying file.
### Hard links
**NOTE**: hard links can only be created on the same filesystem.
If you have larger persistent data to copy to the target. GNU `tar` will copy
data referenced by hard links created with `ln`. A hard link does not create
another copy the data.
To copy a directory tree to the new target you can use the `cp` command with the
`--link` option which creates hard links.
#### Example
```sh
cd /tmp
root=$(mktemp -d)
cp --verbose --archive --link --parents /persist/home/myuser ${root}
```
`--parents` will create the directory structure of the source at the
destination.

View file

@ -0,0 +1,23 @@
# 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](https://numtide.com/). 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.

View file

@ -0,0 +1,29 @@
# Kexec on Systems with Limited RAM
When working with nixos-anywhere on systems with limited RAM (around 1GB), you
can use the `--no-disko-deps` option to reduce memory usage during installation.
## How it works
The `--no-disko-deps` option uploads only the disko partitioning script without
including its dependencies. This significantly reduces memory usage because:
1. The installer normally stores all dependencies in memory
2. Partitioning tools can be quite large when bundled with their dependencies
## Usage example
```bash
nix run github:nix-community/nixos-anywhere -- --no-disko-deps --flake <path to configuration>#<configuration name> --target-host root@<ip address>
```
## Trade-off
While this approach saves memory, it means the partitioning tools will be
whatever versions are available on the target system, rather than the specific
versions defined in your NixOS configuration. This could potentially lead to
version inconsistencies between the partitioning tools and the NixOS system
being installed.
This trade-off is usually acceptable for memory-constrained environments where
installation would otherwise fail due to insufficient RAM.

View file

@ -0,0 +1,57 @@
# 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` being 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:
```nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# ... other inputs
outputs = inputs@{ 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 every time the
flake is updated.
In your shell you will see something in your `$NIX_PATH`:
```shellSession
$ echo $NIX_PATH
/root/.nix-defexpr/channels:nixpkgs=/nix/store/8b61j28rpy11dg8hanbs2x710d8w3v0d-source
```
# Solution 2: Manually add the channel
On the installed machine, run:
```shellSession
$ nix-channel --add https://nixos.org/channels/nixos-unstable nixos
$ nix-channel --update
```

View file

@ -0,0 +1,71 @@
# 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](https://nixos.org/manual/nixos/stable/index.html#sec-booting-from-usb)
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' --target-host nixos@fec0::5054:ff:fe12:3456
```
This example assumes a flake in the current directory containing a configuration
named `myconfig`.

View file

@ -0,0 +1,76 @@
# 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](https://github.com/Mic92/sops-nix) or
[agenix](https://github.com/ryantm/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:
```bash
#!/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' --target-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`:
```bash
# 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.

View file

@ -0,0 +1,23 @@
# 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](https://registry.terraform.io/providers/hashicorp/null/latest) and
[external](https://registry.terraform.io/providers/hashicorp/external/latest)
provider.
You can get these by from nixpkgs like this:
```nix
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](https://github.com/nix-community/nixos-anywhere/tree/main/terraform)
for examples and module parameter on how to use the modules.

View file

@ -0,0 +1,82 @@
# Use without flakes
First,
[import the disko NixOS module](https://github.com/nix-community/disko/blob/master/docs/HowTo.md#installing-nixos-module)
in your NixOS configuration and define disko devices as described in the
[examples](https://github.com/nix-community/disko/tree/master/example).
Let's assume that your NixOS configuration lives in `configuration.nix` and your
target machine is called `machine`:
## 1. Download your favourite disk layout:
See https://github.com/nix-community/disko-templates/ for more examples:
The example below will work with both UEFI and BIOS-based systems.
```bash
curl https://raw.githubusercontent.com/nix-community/disko-templates/main/single-disk-ext4/disko-config.nix > ./disko-config.nix
```
## 2. Get a hardware-configuration.nix from on 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](https://github.com/nix-community/nixos-images#kexec-tarballs).
- **Generate Configuration**: Run the following command on the target machine:
```bash
nixos-generate-config --no-filesystems --dir /tmp/config
```
This creates the necessary configuration files under `/tmp/config/`. Copy
`/tmp/config/nixos/hardware-configuration.nix` to your local machine into the
same directory as `disko-config.nix`.
## 3. Set NixOS version to use
```nix
# default.nix
let
# replace nixos-24.11 with your preferred nixos version or revision from here: https://status.nixos.org/
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/refs/heads/nixos-24.11.tar.gz";
in
import (nixpkgs + "/nixos/lib/eval-config.nix") {
modules = [ ./configuration.nix ];
}
```
## 4. Write a NixOS configuration
```nix
# configuration.nix
{
imports = [
"${fetchTarball "https://github.com/nix-community/disko/tarball/master"}/module.nix"
./disko-config.nix
./hardware-configuration.nix
];
# Replace this with the system of the installation target you want to install!!!
disko.devices.disk.main.device = "/dev/sda";
# Set this to the NixOS version that you have set in the previous step.
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "24.11";
}
```
## 5. Build and deploy with nixos-anywhere
Your current directory now should contain the following files from the previous
step:
- `configuration.nix`, `default.nix`, `disko-config.nix` and
`hardware-configuration.nix`
Run `nixos-anywhere` as follows:
```bash
nixos-anywhere --store-paths $(nix-build -A config.system.build.formatScript -A config.system.build.toplevel --no-out-link) root@machine
```

Binary file not shown.

After

(image error) Size: 29 KiB

View file

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
width="512"
height="512"
id="svg322"
sodipodi:docname="logo.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
inkscape:export-filename="logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#">
<defs
id="defs326">
<marker
markerWidth="512"
markerHeight="512"
refX="256"
refY="256"
orient="auto"
id="marker11007">
<path
d="M 0,0 H 512 V 512 H 0 Z"
fill="#fbfdfc"
id="path246" />
</marker>
</defs>
<sodipodi:namedview
id="namedview324"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="2.2304247"
inkscape:cx="32.280848"
inkscape:cy="129.79591"
inkscape:window-width="3840"
inkscape:window-height="2090"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg322" />
<path
d="M0,0 L4,2 L18,10 L27,18 L30,26 L30,34 L27,46 L23,60 L17,73 L8,94 L3,105 L3,116 L7,119 L13,121 L39,126 L55,131 L64,137 L68,142 L70,146 L70,154 L66,162 L57,170 L45,176 L37,179 L20,182 L11,183 L-17,184 L-24,192 L-34,206 L-47,221 L-59,232 L-73,241 L-85,246 L-93,248 L-113,248 L-127,245 L-132,243 L-132,241 L-123,235 L-113,226 L-105,218 L-96,205 L-88,189 L-82,168 L-80,136 L-72,125 L-54,98 L-38,71 L-23,44 L-13,25 L-5,9 Z "
fill="#517ED0"
transform="translate(435,104)"
id="path248" />
<path
d="M0,0 L10,1 L19,6 L31,17 L40,26 L49,37 L57,48 L67,62 L75,74 L91,75 L107,78 L127,85 L141,93 L153,105 L160,115 L167,130 L170,139 L171,149 L162,146 L155,143 L138,139 L110,139 L93,143 L87,146 L80,146 L71,144 L57,142 L22,140 L-23,140 L-45,141 L-65,143 L-74,143 L-81,135 L-83,129 L-83,117 L-79,109 L-73,102 L-63,95 L-46,87 L-28,82 L-11,79 L14,79 L11,71 L-2,48 L-10,34 L-14,23 L-14,13 L-11,7 L-5,2 Z "
fill="#517DD0"
transform="translate(132,13)"
id="path250" />
<path
d="M0,0 L11,2 L20,5 L29,7 L55,7 L72,4 L80,2 L104,7 L126,9 L142,10 L234,10 L240,14 L245,20 L246,24 L246,33 L241,43 L234,50 L221,58 L205,65 L183,70 L162,73 L151,73 L159,87 L174,112 L179,122 L180,125 L180,136 L175,145 L167,150 L153,150 L143,143 L130,130 L117,113 L107,99 L95,80 L90,76 L71,74 L50,69 L34,62 L23,54 L15,46 L8,34 L2,17 L0,7 Z "
fill="#4DB0CE"
transform="translate(213,350)"
id="path252" />
<path
d="M0,0 L2,0 L7,17 L12,30 L21,45 L31,57 L39,64 L46,71 L47,73 L49,89 L54,107 L60,122 L72,147 L83,167 L99,191 L113,212 L117,220 L117,234 L114,240 L111,243 L80,243 L63,231 L50,219 L38,207 L31,199 L30,196 L27,196 L25,200 L11,215 L2,224 L-9,230 L-13,231 L-21,231 L-27,227 L-32,221 L-36,210 L-36,203 L-28,186 L-22,174 L-17,159 L-13,141 L-12,131 L-12,99 L-16,64 L-18,49 L-18,34 L-15,24 L-8,11 Z "
fill="#517ED0"
transform="translate(158,269)"
id="path256" />
<path
d="M0,0 L12,1 L28,8 L40,16 L50,24 L65,37 L75,48 L77,51 L79,51 L89,37 L98,27 L107,18 L117,14 L126,14 L134,19 L139,25 L142,32 L142,40 L137,54 L128,74 L123,88 L118,109 L117,117 L117,147 L121,169 L124,181 L124,200 L120,211 L111,225 L106,229 L100,214 L92,199 L80,183 L70,173 L64,168 L62,153 L57,139 L49,123 L34,97 L21,75 L6,53 L-6,36 L-12,26 L-13,23 L-13,11 L-9,5 L-4,1 Z "
fill="#4EB0CF"
transform="translate(246,0)"
id="path258" />
<path
d="M0,0 L6,0 L6,2 L11,1 L22,1 L32,6 L41,8 L53,8 L60,16 L62,17 L61,22 L56,28 L54,33 L54,43 L58,45 L58,48 L52,47 L46,42 L46,40 L30,45 L24,53 L24,61 L25,62 L31,62 L35,58 L39,59 L41,62 L43,71 L44,72 L50,72 L53,80 L53,82 L61,80 L65,78 L70,78 L72,80 L82,79 L92,84 L103,95 L106,98 L108,99 L117,100 L123,105 L123,114 L118,128 L110,142 L99,156 L88,165 L77,173 L60,180 L55,180 L56,176 L65,170 L67,166 L67,151 L72,144 L73,141 L73,134 L69,129 L65,127 L55,126 L47,116 L45,112 L45,102 L49,97 L55,94 L55,87 L49,83 L44,78 L34,72 L18,64 L11,58 L4,42 L0,35 L1,26 L2,19 L-2,16 L-8,13 L-9,12 L-9,6 L-6,2 Z "
fill="#4D99CF"
transform="translate(223,166)"
id="path260" />
<path
d="M0,0 L9,0 L20,8 L36,14 L44,17 L46,20 L46,27 L42,33 L42,44 L47,49 L46,52 L39,52 L25,46 L15,37 L6,27 L0,17 L-3,10 L-2,2 Z "
fill="#4FABD1"
transform="translate(176,284)"
id="path262"
style="fill:#4d99cf;fill-opacity:1" />
<path
d="M0,0 L14,0 L23,1 L29,5 L30,6 L30,11 L25,10 L24,9 L24,4 L-1,4 Z "
fill="#438ED7"
transform="translate(247,157)"
id="path266" />
<path
id="path268"
transform="translate(203,179)"
d="m 0,0 3,1 1,8 h 8 v 4 l -2,1 H 2 L -2,9 V 2 Z"
style="fill:#4d99cf;fill-opacity:1" />
<path
id="path288"
transform="translate(278,222)"
d="M 0,0 6,1 7,4 9,5 H 2 L -1,3 Z m 0,0 6,1 6,2 8,1 V 7 H 7 L 0,4 Z"
style="fill:#4d99cf;fill-opacity:1" />
<path
d="M0,0 L3,1 L-7,8 L-9,8 L-9,6 L-5,5 L-4,2 Z "
fill="#6184C5"
transform="translate(371,336)"
id="path304"
style="fill:#517ed0;fill-opacity:1" />
<path
id="path314"
style="fill:#4eb0cf;fill-opacity:1"
transform="translate(113,201)"
d="M 0,0 H 2 L 0,4 -2.8602037,5.4301018 -4,6 V 8 L -6,7 Z m 48,-40 h 25 l 14,3 4,2 -1,3 -10,8 -10,11 -9,14 -7,14 -5,17 -2,11 -1,15 -8,10 -12,21 -11,19 -6,10 -11,19 -12,22 -11,21 -9,20 -5,1 -10,-5 -8,-4 -4,-6 -1,-3 v -18 l 6,-26 5,-13 5,-12 6,-13 2,-5 v -6 l -5,-3 -29,-4 -18,-6 -10,-6 -5,-5 -2,-4 V 50 l 7,-8 10,-7 18,-6 10,-2 10,-1 18.016033,-0.600534 L -22,25 l 5,-5 8,-9 7,-9 8,-10 9,-10 9,-9 10,-7 8,-4 z" />
<metadata
id="metadata148">
<rdf:RDF>
<cc:Work
rdf:about="">
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>

After

(image error) Size: 6.4 KiB

View file

@ -0,0 +1,334 @@
# Quickstart Guide: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" width="150" height="150">
[Documentation Index](./INDEX.md)
## Introduction
This guide documents a simple installation of NixOS using **nixos-anywhere** on
a target machine running x86_64 Linux with
[kexec](https://man7.org/linux/man-pages/man8/kexec.8.html) 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](./howtos/INDEX.md) as and when they are available.
You will need:
- A [flake](https://wiki.nixos.org/wiki/Flakes) 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 that is reachable via SSH, either using keys or a password,
and the privilege to either log in directly as root or a user with
password-less sudo.
**nixos-anywhere** doesnt need to be installed. You can run it directly from
[the Github repository.](https://github.com/nix-community/nixos-anywhere)
Details of the flake, the disk configuration and the CLI command are discussed
below.
## Steps required to run nixos-anywhere
### 1. Enable Flakes
Check if your nix has flakes enabled by running `nix flake`. It will tell you if
it's not. To enable flakes, refer to the
[NixOS Wiki](https://wiki.nixos.org/wiki/Flakes#enable-flakes).
### 2. Initialize a Flake
The easiest way to start is to copy our
[example flake.nix](https://github.com/nix-community/nixos-anywhere-examples/blob/main/flake.nix)
into a new directory. This example is tailored for a virtual machine setup
similar to one on [Hetzner Cloud](https://www.hetzner.com/cloud), so you might
need to adapt it for your setup.
If you already have a flake, you can use it by adding
[disko configuration](https://github.com/nix-community/disko?tab=readme-ov-file#how-to-use-disko)
to it.
### 3. Configure your SSH key
If you cloned
[our nixos-anywhere-example](https://github.com/nix-community/nixos-anywhere-examples/blob/main/configuration.nix)
you will also replace the SSH key like this: In your configuration, locate the
line that reads:
```bash
# 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. If you
have a .pem file you can run
```bash
ssh-keygen -y -f /path/to/your/key.pem
```
then paste the result in between the quotes like "ssh-rsa AAA..."
### 4. Configure Storage
In the same directory, create a file called `disk-config.nix`. This file will
define the disk layout for the
[disko](https://github.com/nix-community/disko/blob/master/docs/INDEX.md) tool,
which is used by nixos-anywhere to partition, format, and mount the disks.
For a basic installation, you can copy the contents from the example provided
[here](https://github.com/nix-community/nixos-anywhere-examples/blob/main/disk-config.nix).
This configuration sets up a standard GPT (GUID Partition Table) that is
compatible with both EFI and BIOS systems and mounts the disk as `/dev/sda`. You
may need to adjust `/dev/sda` to match the correct disk on your machine. To
identify the disk, run the `lsblk` command and replace `sda` with the actual
disk name.
For example, on this machine, we would select `/dev/nvme0n1` as the disk:
```
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 1.8T 0 disk
```
If this setup does not match your requirements, you can choose an example that
better suits your disk layout from the
[disko examples](https://github.com/nix-community/disko/tree/master/example).
For more detailed information, refer to the
[disko documentation](https://github.com/nix-community/disko).
### 5. Lock your Flake
```
nix flake lock
```
This will download your flake dependencies and make a `flake.lock` file that
describes how to reproducibly build your system.
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.
### 6. Connectivity to the Target Machine
**nixos-anywhere** will create a temporary SSH key to use for the installation.
If your SSH key is not found, you will be asked for your password. If you are
using a non-root user, you must have access to sudo without a password. To avoid
SSH password prompts, set the `SSHPASS` environment variable to your password
and add `--env-password` to the `nixos-anywhere` command. If providing a
specific SSH key through `-i` (identity_file), this key will then be used for
the installation and no temporary SSH key will be created.
### 7. (Optional) Test your NixOS and Disko configuration
Skip this step and continue with Step 8, if you don't have a hardware
configuration (hardware-configuration.nix or facter.json) generated yet or make
sure you don't import non-existing hardware-configuration.nix or facter.json
during running the vm test.
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
containing `flake.nix` and `disk-config.nix`
- `<configuration name>` must match the name that immediately follows the text
`nixosConfigurations.` in the flake, as indicated by the comment in the
[example](https://github.com/nix-community/nixos-anywhere-examples/blob/main/flake.nix).
```
nix run github:nix-community/nixos-anywhere -- --flake <path to configuration>#<configuration name> --vm-test
```
### 8. Prepare Hardware Configuration
If you're not using a virtual machine, it's recommended to allow
`nixos-anywhere` to generate a hardware configuration during installation. This
ensures that essential drivers, such as those required for disk detection, are
properly configured.
To enable `nixos-anywhere` to integrate its generated configuration into your
NixOS setup, you need to include an import for the hardware configuration
beforehand.
Heres an example:
```diff
nixosConfigurations.generic = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./configuration.nix
+ ./hardware-configuration.nix
];
};
```
When running `nixos-anywhere`, this file is automatically generated by including
the following flags in your command:
`--generate-hardware-config nixos-generate-config ./hardware-configuration.nix`.
The second flag, `./hardware-configuration.nix`, specifies where
`nixos-generate-config` will store the configuration. Adjust this path to
reflect the location where you want the `hardware-configuration.nix` for this
machine to be saved.
#### 8.1 nixos-facter
As an alternative to `nixos-generate-config`, you can use the experimental
[nixos-facter](https://github.com/numtide/nixos-facter) command, which offers
more comprehensive hardware reports and advanced configuration options.
To use `nixos-facter`, add the following to your flake inputs:
```diff
{
+ inputs.nixos-facter-modules.url = "github:numtide/nixos-facter-modules";
}
```
Next, import the module into your configuration and specify `facter.json` as the
path where the hardware report will be saved:
```diff
nixosConfigurations.generic-nixos-facter = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./configuration.nix
+ nixos-facter-modules.nixosModules.facter
+ { config.facter.reportPath = ./facter.json }
];
};
```
To generate the configuration for `nixos-facter` with `nixos-anywhere`, use the
following flags: `--generate-hardware-config nixos-facter ./facter.json`. The
second flag, `./facter.json`, specifies where `nixos-generate-config` will store
the hardware report. Adjust this path to suit the location where you want the
`facter.json` to be saved.
### 9. Run it
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
containing `flake.nix` and `disk-config.nix`
- `<configuration name>` must match the name that immediately follows the text
`nixosConfigurations.` in the flake, as indicated by the comment in the
[example](https://github.com/nix-community/nixos-anywhere-examples/blob/main/flake.nix).
- `<ip address>` is the IP address of the target machine.
```
nix run github:nix-community/nixos-anywhere -- --flake <path to configuration>#<configuration name> --target-host 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 is
`37.27.18.135`:
```
nix run github:nix-community/nixos-anywhere -- --flake /home/mydir/test#hetzner-cloud --target-host root@37.27.18.135
```
If you also need to generate hardware configuration amend flags for
nixos-generate-config:
```
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>
```
Or these flags if you are using nixos-facter instead:
```
nix run github:nix-community/nixos-anywhere -- --generate-hardware-config nixos-facter ./facter.json --flake <path to configuration>#<configuration name> --target-host root@<ip address>
```
Adjust the location of `./hardware-configuration.nix` and `./facter.json`
accordingly.
**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 the `known_hosts` file (or use the command
`ssh-keygen -R <ip_address>`). The next connection attempt will then treat this
as a new server.
The error message line `Offending ECDSA key in ~/.ssh/known_hosts:6` gives the
line number that needs to be removed from the `known_hosts` file (line 6 in this
example).
# Finished!
**nixos-anywhere**'s job is now done, as it is a tool to install NixOS onto the
target machine.
Any future changes to the configuration should be made to your flake. You would
reference this flake when using the NixOS `nixos-rebuild` command or a separate
3rd party deployment tool of your choice i.e.
[deploy-rs](https://github.com/serokell/deploy-rs),
[colmena](https://github.com/zhaofengli/colmena),
[nixinate](https://github.com/MatthewCroughan/nixinate),
[clan](https://clan.lol/) (author's choice).
To update on the machine locally (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>
```
To update remotely you will need to have configured an
[ssh server](https://search.nixos.org/options?show=services.sshd.enable) and
your ssh key for the
[root user](https://search.nixos.org/options?show=users.users.%3Cname%3E.openssh.authorizedKeys.keys):
```
nixos-rebuild switch --flake <URL to your flake> --target-host "root@<ip address>"
```
See the Nix documentation for use of the flake
[URL-like syntax](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#url-like-syntax).
For more information on different use cases of **nixos-anywhere** please refer
to the [How to Guide](./howtos/INDEX.md), and for more technical information and
explanation of known error messages, refer to the
[Reference Manual](./reference.md).

View file

@ -0,0 +1,137 @@
# Reference Manual: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="141">
[Documentation Index](./INDEX.md)
TODO: Populate this guide properly
## Contents
[Command Line Usage](#command-line-usage)
[Explanation of known error messages](#explanation-of-known-error-messages)
## Command Line Usage
<!-- `$ bash ./src/nixos-anywhere.sh --help` -->
```
Usage: nixos-anywhere [options] [<ssh-host>]
Options:
* -f, --flake <flake_uri>
set the flake to install the system from. i.e.
nixos-anywhere --flake .#mymachine
Also supports variants:
nixos-anywhere --flake .#nixosConfigurations.mymachine.config.virtualisation.vmVariant
* --target-host <ssh-host>
set the SSH target host to deploy onto.
* -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
* --env-password
set a password used by ssh-copy-id, the password should be set by
the environment variable SSHPASS
* -s, --store-paths <disko-script> <nixos-system>
set the store paths to the disko-script and nixos-system directly
if this is given, flake is not needed
* --kexec <path>
use another kexec tarball to bootstrap NixOS
* --kexec-extra-flags
extra flags to add into the call to kexec, e.g. "--no-sync"
* --ssh-store-setting <key> <value>
ssh store settings appended to the store URI, e.g. "compress true". <value> needs to be URI encoded.
* --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
* --extra-files <path>
contents of local <path> are recursively copied to the root (/) of the new NixOS installation. Existing files are overwritten
Copied files will be owned by root unless specified by --chown option. See documentation for details.
* --chown <path> <ownership>
change ownership of <path> recursively. Recommended to use uid:gid as opposed to username:groupname for ownership.
Option can be specified more than once.
* --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
* --show-trace
show nix build traces
* --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.
* --generate-hardware-config nixos-facter|nixos-generate-config <path>
generate a hardware-configuration.nix file using the specified backend and write it to the specified path.
The backend can be either 'nixos-facter' or 'nixos-generate-config'.
* --phases
comma separated list of phases to run. Default is: kexec,disko,install,reboot
kexec: kexec into the nixos installer
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
install: install the system
reboot: unmount the filesystems, export any ZFS pools and reboot the machine
* --disko-mode disko|mount|format
set the disko mode to format, mount or destroy. Default is disko.
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
* --no-disko-deps
This will only upload the disko script and not the partitioning tools dependencies.
Installers usually have dependencies available.
Use this option if your target machine has not enough RAM to store the dependencies in memory.
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
```
## 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 attribute | 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 <operating system> | 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](./howtos#using-your-own-kexec-image) 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. |
| | | |
| | | |
| | | |
| | | |

View file

@ -0,0 +1,39 @@
# System Requirements: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" width="150" height="150">
[Documentation Index](./INDEX.md)
## Requirements
### Source Machine
1. **Supported Systems:**
- Linux or macOS computers with Nix installed.
- NixOS
- Windows systems using WSL2.
2. **Nix Installation:** If Nix is not yet installed on your system, refer to
the [nix installation page](https://nixos.org/download#download-nix).
### 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.
1. **Direct Boot Option:**
- Must be already running a NixOS installer.
2. **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](./howtos/INDEX.md#using-your-own-kexec-image)
manually.
- **Memory Requirements:**
- At least 1 GB of RAM (excluding swap space).

View file

@ -0,0 +1,132 @@
{
"nodes": {
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1741786315,
"narHash": "sha256-VT65AE2syHVj6v/DGB496bqBnu1PXrrzwlw07/Zpllc=",
"owner": "nix-community",
"repo": "disko",
"rev": "0d8c6ad4a43906d14abd5c60e0ffe7b587b213de",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "master",
"repo": "disko",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1741352980,
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixos-images": {
"inputs": {
"nixos-stable": [
"nixos-stable"
],
"nixos-unstable": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1741866599,
"narHash": "sha256-Re/T1Cjmiis0tdphj/Wjqt+c2RlMw/il7LBWzvwQPz0=",
"owner": "nix-community",
"repo": "nixos-images",
"rev": "63285ff93fc1daa2caac9f86e2302ae4edc5e84f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-images",
"type": "github"
}
},
"nixos-stable": {
"locked": {
"lastModified": 1741862977,
"narHash": "sha256-prZ0M8vE/ghRGGZcflvxCu40ObKaB+ikn74/xQoNrGQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cdd2ef009676ac92b715ff26630164bb88fec4e0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1742051767,
"narHash": "sha256-JpyjnalnIqJ7cvP8HzaoJN9/i2bDx83dToodHHjGuNg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ec886d10b507760c90ed01e2eac7f0679d0a47ae",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable-small",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"flake-parts": "flake-parts",
"nixos-images": "nixos-images",
"nixos-stable": "nixos-stable",
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1739829690,
"narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -0,0 +1,55 @@
{
description = "A universal nixos installer, just needs ssh access to the target system";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable-small";
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
# used for testing
disko = {
url = "github:nix-community/disko/master";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-stable.url = "github:NixOS/nixpkgs/nixos-24.11";
nixos-images.url = "github:nix-community/nixos-images";
nixos-images.inputs.nixos-unstable.follows = "nixpkgs";
nixos-images.inputs.nixos-stable.follows = "nixos-stable";
# used for development
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs:
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-linux"
"aarch64-darwin"
];
imports = [
./src/flake-module.nix
./tests/flake-module.nix
./docs/flake-module.nix
# allow to disable treefmt in downstream flakes
] ++ inputs.nixpkgs.lib.optional (inputs.treefmt-nix ? flakeModule) ./treefmt/flake-module.nix;
perSystem =
{ self', lib, ... }:
{
checks =
let
packages = lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages;
devShells = lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells;
in
packages // devShells;
};
};
}

View file

@ -0,0 +1,38 @@
#!/usr/bin/env nix
#! nix shell nixpkgs#bash nixpkgs#gnused --command bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd "$SCRIPT_DIR/.."
version=${1:-}
if [[ -z $version ]]; then
echo "USAGE: $0 version" >&2
exit 1
fi
if [[ "$(git symbolic-ref --short HEAD)" != "main" ]]; then
echo "must be on main branch" >&2
exit 1
fi
# ensure we are up-to-date
uncommitted_changes=$(git diff --compact-summary)
if [[ -n $uncommitted_changes ]]; then
echo -e "There are uncommitted changes, exiting:\n${uncommitted_changes}" >&2
exit 1
fi
git pull git@github.com:nix-community/nixos-anywhere main
unpushed_commits=$(git log --format=oneline origin/main..main)
if [[ $unpushed_commits != "" ]]; then
echo -e "\nThere are unpushed changes, exiting:\n$unpushed_commits" >&2
exit 1
fi
sed -i -e "s!version = \".*\";!version = \"${version}\";!" src/default.nix
git add src/default.nix
nix-shell -p nix-fast-build --command "nix-fast-build --eval-workers 2"
git commit -m "bump version ${version}"
git tag "${version}"
echo "now run 'git push --tags origin main'"

View file

@ -0,0 +1,63 @@
{
stdenv,
openssh,
gitMinimal,
nixVersions,
nix,
coreutils,
curl,
gnugrep,
gnutar,
gawk,
findutils,
gnused,
sshpass,
terraform-docs,
lib,
makeWrapper,
mkShellNoCC,
}:
let
runtimeDeps = [
gitMinimal # for git flakes
# pinned because nix-copy-closure hangs if ControlPath provided for SSH: https://github.com/NixOS/nix/issues/8480
(if lib.versionAtLeast nix.version "2.16" then nix else nixVersions.nix_2_16)
coreutils
curl # when uploading tarballs
gnugrep
gawk
findutils
gnused # needed by ssh-copy-id
sshpass # used to provide password for ssh-copy-id
gnutar # used to upload extra-files
];
in
stdenv.mkDerivation {
pname = "nixos-anywhere";
version = "1.8.0";
src = ./..;
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
install -D --target-directory=$out/libexec/nixos-anywhere/ -m 0755 src/*.sh
# We prefer the system's openssh over our own, since it might come with features not present in ours:
# https://github.com/nix-community/nixos-anywhere/issues/62
makeShellWrapper $out/libexec/nixos-anywhere/nixos-anywhere.sh $out/bin/nixos-anywhere \
--prefix PATH : ${lib.makeBinPath runtimeDeps} --suffix PATH : ${lib.makeBinPath [ openssh ]}
'';
# Dependencies for our devshell
passthru.devShell = mkShellNoCC {
packages = runtimeDeps ++ [
openssh
terraform-docs
];
};
meta = with lib; {
description = "Install nixos everywhere via ssh";
homepage = "https://github.com/nix-community/nixos-anywhere";
license = licenses.mit;
platforms = platforms.all;
};
}

View file

@ -0,0 +1,11 @@
{
perSystem =
{ config, pkgs, ... }:
{
packages = {
nixos-anywhere = pkgs.callPackage ./. { };
default = config.packages.nixos-anywhere;
};
devShells.default = config.packages.nixos-anywhere.devShell;
};
}

View file

@ -0,0 +1,23 @@
#!/bin/sh
set -efu "${enableDebug:-}"
has() {
command -v "$1" >/dev/null && echo "y" || echo "n"
}
isNixos=$(if test -f /etc/os-release && grep -Eq 'ID(_LIKE)?="?nixos"?' /etc/os-release; then echo "y"; else echo "n"; fi)
cat <<FACTS
isOs=$(uname)
isArch=$(uname -m)
isKexec=$(if test -f /etc/is_kexec; then echo "y"; else echo "n"; fi)
isNixos=$isNixos
isInstaller=$(if [ "$isNixos" = "y" ] && grep -Eq 'VARIANT_ID="?installer"?' /etc/os-release; then echo "y"; else echo "n"; fi)
isContainer=$(if [ "$(has systemd-detect-virt)" = "y" ]; then systemd-detect-virt --container; else echo "none"; fi)
hasIpv6Only=$(if [ "$(has ip)" = "n" ] || ip r g 1 >/dev/null 2>/dev/null || ! ip -6 r g :: >/dev/null 2>/dev/null; then echo "n"; else echo "y"; fi)
hasTar=$(has tar)
hasCpio=$(has cpio)
hasSudo=$(has sudo)
hasDoas=$(has doas)
hasWget=$(has wget)
hasCurl=$(has curl)
hasSetsid=$(has setsid)
hasNixOSFacter=$(command -v nixos-facter >/dev/null && echo "y" || echo "n")
FACTS

View file

@ -0,0 +1,880 @@
#!/usr/bin/env bash
set -euo pipefail
here=$(dirname "${BASH_SOURCE[0]}")
flake=""
flakeAttr=""
kexecUrl=""
kexecExtraFlags=""
sshStoreSettings=""
enableDebug=""
nixBuildFlags=()
diskoAttr=""
diskoScript=""
diskoMode="disko"
diskoDeps=y
nixosSystem=""
extraFiles=""
vmTest="n"
nixOptions=(
--extra-experimental-features 'nix-command flakes'
"--no-write-lock-file"
)
SSH_PRIVATE_KEY=${SSH_PRIVATE_KEY-}
declare -A phases
phases[kexec]=1
phases[disko]=1
phases[install]=1
phases[reboot]=1
hardwareConfigBackend=none
hardwareConfigPath=
sshPrivateKeyFile=
if [ -t 0 ]; then # stdin is a tty, we allow interactive input to ssh i.e. passwords
sshTtyParam="-t"
else
sshTtyParam="-T"
fi
sshConnection=
postKexecSshPort=22
buildOnRemote=n
buildOn=auto
envPassword=n
# Facts set by get-facts.sh
isOs=
isArch=
isKexec=
isInstaller=
isContainer=
hasIpv6Only=
hasTar=
hasCpio=
hasSudo=
hasDoas=
hasWget=
hasCurl=
hasSetsid=
hasNixOSFacter=
sshKeyDir=$(mktemp -d)
trap 'rm -rf "$sshKeyDir"' EXIT
mkdir -p "$sshKeyDir"
declare -A diskEncryptionKeys=()
declare -A extraFilesOwnership=()
declare -a nixCopyOptions=()
declare -a sshArgs=()
showUsage() {
cat <<USAGE
Usage: nixos-anywhere [options] [<ssh-host>]
Options:
* -f, --flake <flake_uri>
set the flake to install the system from. i.e.
nixos-anywhere --flake .#mymachine
Also supports variants:
nixos-anywhere --flake .#nixosConfigurations.mymachine.config.virtualisation.vmVariant
* --target-host <ssh-host>
set the SSH target host to deploy onto.
* -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
* --env-password
set a password used by ssh-copy-id, the password should be set by
the environment variable SSHPASS
* -s, --store-paths <disko-script> <nixos-system>
set the store paths to the disko-script and nixos-system directly
if this is given, flake is not needed
* --kexec <path>
use another kexec tarball to bootstrap NixOS
* --kexec-extra-flags
extra flags to add into the call to kexec, e.g. "--no-sync"
* --ssh-store-setting <key> <value>
ssh store settings appended to the store URI, e.g. "compress true". <value> needs to be URI encoded.
* --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
* --extra-files <path>
contents of local <path> are recursively copied to the root (/) of the new NixOS installation. Existing files are overwritten
Copied files will be owned by root unless specified by --chown option. See documentation for details.
* --chown <path> <ownership>
change ownership of <path> recursively. Recommended to use uid:gid as opposed to username:groupname for ownership.
Option can be specified more than once.
* --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
* --show-trace
show nix build traces
* --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.
* --generate-hardware-config nixos-facter|nixos-generate-config <path>
generate a hardware-configuration.nix file using the specified backend and write it to the specified path.
The backend can be either 'nixos-facter' or 'nixos-generate-config'.
* --phases
comma separated list of phases to run. Default is: kexec,disko,install,reboot
kexec: kexec into the nixos installer
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
install: install the system
reboot: unmount the filesystems, export any ZFS pools and reboot the machine
* --disko-mode disko|mount|format
set the disko mode to format, mount or destroy. Default is disko.
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
* --no-disko-deps
This will only upload the disko script and not the partitioning tools dependencies.
Installers usually have dependencies available.
Use this option if your target machine has not enough RAM to store the dependencies in memory.
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
USAGE
}
abort() {
echo "aborted: $*" >&2
exit 1
}
step() {
echo "### $* ###"
}
parseArgs() {
local substituteOnDestination=y
local printBuildLogs=n
local buildOnRemote=n
while [[ $# -gt 0 ]]; do
case "$1" in
-f | --flake)
flake=$2
shift
;;
--target-host)
sshConnection=$2
shift
;;
-i)
sshPrivateKeyFile=$2
shift
;;
-p | --ssh-port)
sshArgs+=("-p" "$2")
shift
;;
--ssh-option)
sshArgs+=("-o" "$2")
shift
;;
-L | --print-build-logs)
printBuildLogs=y
;;
-s | --store-paths)
diskoScript=$(readlink -f "$2")
nixosSystem=$(readlink -f "$3")
shift
shift
;;
--generate-hardware-config)
if [[ $# -lt 3 ]]; then
abort "Missing arguments for --generate-hardware-config <backend> <path>"
fi
case "$2" in
nixos-facter | nixos-generate-config)
hardwareConfigBackend=$2
;;
*)
abort "Unknown hardware config backend: $2"
;;
esac
hardwareConfigPath=$3
shift
shift
;;
-t | --tty)
echo "the '$1' flag is deprecated, a tty is now detected automatically" >&2
;;
--help)
showUsage
exit 0
;;
--kexec)
kexecUrl=$2
shift
;;
--kexec-extra-flags)
kexecExtraFlags=$2
shift
;;
--ssh-store-setting)
key=$2
shift
value=$2
shift
sshStoreSettings+="$sshStoreSettings$key=$value&"
shift
;;
--post-kexec-ssh-port)
postKexecSshPort=$2
shift
;;
--copy-host-keys)
copyHostKeys=y
;;
--show-trace)
nixBuildFlags+=("--show-trace")
;;
--debug)
enableDebug="-x"
printBuildLogs=y
set -x
;;
--disko-mode)
case "$2" in
format | mount | disko)
diskoMode=$2
;;
*)
abort "Supported values for --disko-mode are disko, mount and format. Unknown mode : $2"
;;
esac
shift
;;
--no-disko-deps)
diskoDeps=n
;;
--build-on)
case "$2" in
auto | local | remote)
buildOn=$2
;;
*)
abort "Supported values for --build-on are auto, local and remote. Unknown mode : $2"
;;
esac
shift
;;
--extra-files)
extraFiles=$2
shift
;;
--chown)
extraFilesOwnership["$2"]="$3"
shift
shift
;;
--disk-encryption-keys)
diskEncryptionKeys["$2"]="$3"
shift
shift
;;
--phases)
phases[kexec]=0
phases[disko]=0
phases[install]=0
phases[reboot]=0
IFS=, read -r -a phaseList <<<"$2"
for phase in "${phaseList[@]}"; do
if [[ ${phases[$phase]:-unset} == unset ]]; then
abort "Unknown phase: $phase"
fi
phases[$phase]=1
done
shift
;;
--stop-after-disko)
echo "WARNING: --stop-after-disko is deprecated, use --phases kexec,disko instead" 2>&1
phases[kexec]=1
phases[disko]=1
phases[install]=0
phases[reboot]=0
;;
--no-reboot)
echo "WARNING: --no-reboot is deprecated, use --phases kexec,disko,install instead" 2>&1
phases[kexec]=1
phases[disko]=1
phases[install]=1
phases[reboot]=0
;;
--from)
nixCopyOptions+=("--from" "$2")
shift
;;
--option)
key=$2
shift
value=$2
shift
nixOptions+=("--option" "$key" "$value")
;;
--no-substitute-on-destination)
substituteOnDestination=n
;;
--build-on-remote)
echo "WARNING: --build-on-remote is deprecated, use --build-on remote instead" 2>&1
buildOnRemote=y
buildOn="remote"
;;
--env-password)
envPassword=y
;;
--vm-test)
vmTest=y
;;
*)
if [[ -z ${sshConnection} ]]; then
sshConnection="$1"
else
showUsage
exit 1
fi
;;
esac
shift
done
diskoAttr="${diskoMode}Script"
if [[ ${diskoDeps} == "n" ]]; then
diskoAttr="${diskoAttr}NoDeps"
fi
if [[ ${printBuildLogs} == "y" ]]; then
nixOptions+=("-L")
fi
if [[ $substituteOnDestination == "y" ]]; then
nixCopyOptions+=("--substitute-on-destination")
fi
if [[ $vmTest == "n" ]] && [[ -z ${sshConnection} ]]; then
abort "ssh-host must be set"
fi
if [[ $buildOn == "local" ]] && [[ $buildOnRemote == "y" ]]; then
abort "Conflicting flags: --build-on local and --build-on-remote used."
fi
if [[ -n ${flake} ]]; then
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
flake="${BASH_REMATCH[1]}"
flakeAttr="${BASH_REMATCH[2]}"
fi
if [[ -z ${flakeAttr} ]]; then
echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri." >&2
echo 'For example, to use the output nixosConfigurations.foo from the flake.nix, append "#foo" to the flake-uri.' >&2
exit 1
fi
# Support .#foo shorthand
if [[ $flakeAttr != nixosConfigurations.* ]]; then
flakeAttr="nixosConfigurations.\"$flakeAttr\".config"
fi
fi
}
# ssh wrapper
runSshNoTty() {
ssh -i "$sshKeyDir"/nixos-anywhere -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${sshArgs[@]}" "$sshConnection" "$@"
}
runSshTimeout() {
timeout 10 ssh -i "$sshKeyDir"/nixos-anywhere -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${sshArgs[@]}" "$sshConnection" "$@"
}
runSsh() {
ssh "$sshTtyParam" -i "$sshKeyDir"/nixos-anywhere -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${sshArgs[@]}" "$sshConnection" "$@"
}
nixCopy() {
NIX_SSHOPTS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $sshKeyDir/nixos-anywhere ${sshArgs[*]}" nix copy \
"${nixOptions[@]}" \
"${nixCopyOptions[@]}" \
"$@"
}
nixBuild() {
NIX_SSHOPTS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $sshKeyDir/nixos-anywhere ${sshArgs[*]}" nix build \
--print-out-paths \
--no-link \
"${nixBuildFlags[@]}" \
"${nixOptions[@]}" \
"$@"
}
runVmTest() {
if [[ -z ${flakeAttr} ]]; then
echo "--vm-test is not supported with --store-paths" >&2
echo "Please use --flake instead or build config.system.build.installTest of your nixos configuration manually" >&2
exit 1
fi
if [[ ${buildOn} == "remote" ]]; then
echo "--vm-test is not supported with --build-on-remote" >&2
exit 1
fi
if [[ -n ${extraFiles} ]]; then
echo "--vm-test is not supported with --extra-files" >&2
exit 1
fi
if [ ${#diskEncryptionKeys[@]} -gt 0 ]; then
echo "--vm-test is not supported with --disk-encryption-keys" >&2
exit 1
fi
nix build \
--print-out-paths \
--no-link \
-L \
"${nixBuildFlags[@]}" \
"${nixOptions[@]}" \
"${flake}#${flakeAttr}.system.build.installTest"
}
uploadSshKey() {
# ssh-copy-id requires this directory
mkdir -p "$HOME/.ssh/"
if [[ -n ${sshPrivateKeyFile} ]]; then
cp "$sshPrivateKeyFile" "$sshKeyDir/nixos-anywhere"
ssh-keygen -y -f "$sshKeyDir/nixos-anywhere" >"$sshKeyDir/nixos-anywhere.pub"
else
# we generate a temporary ssh keypair that we can use during nixos-anywhere
ssh-keygen -t ed25519 -f "$sshKeyDir"/nixos-anywhere -P "" -C "nixos-anywhere" >/dev/null
fi
declare -a sshCopyIdArgs
if [[ -n ${sshPrivateKeyFile} ]]; then
unset SSH_AUTH_SOCK # don't use system agent if key was supplied
sshCopyIdArgs+=(-o "IdentityFile=${sshPrivateKeyFile}" -f)
fi
step Uploading install SSH keys
until
if [[ ${envPassword} == y ]]; then
sshpass -e \
ssh-copy-id \
-i "$sshKeyDir"/nixos-anywhere.pub \
-o ConnectTimeout=10 \
-o UserKnownHostsFile=/dev/null \
-o IdentitiesOnly=yes \
-o StrictHostKeyChecking=no \
"${sshCopyIdArgs[@]}" \
"${sshArgs[@]}" \
"$sshConnection"
else
ssh-copy-id \
-i "$sshKeyDir"/nixos-anywhere.pub \
-o ConnectTimeout=10 \
-o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \
"${sshCopyIdArgs[@]}" \
"${sshArgs[@]}" \
"$sshConnection"
fi
do
sleep 3
done
}
importFacts() {
step Gathering machine facts
local facts filteredFacts
if ! facts=$(runSsh -o ConnectTimeout=10 enableDebug=$enableDebug sh -- <"$here"/get-facts.sh); then
exit 1
fi
filteredFacts=$(echo "$facts" | grep -E '^(has|is)[A-Za-z0-9_]+=\S+')
if [[ -z $filteredFacts ]]; then
abort "Retrieving host facts via ssh failed. Check with --debug for the root cause, unless you have done so already"
fi
# make facts available in script
# shellcheck disable=SC2046
export $(echo "$filteredFacts" | xargs)
for var in isOs isArch isKexec isInstaller isContainer hasIpv6Only hasTar hasCpio hasSudo hasDoas hasWget hasCurl hasSetsid; do
if [[ -z ${!var} ]]; then
abort "Failed to retrieve fact $var from host"
fi
done
}
checkBuildLocally() {
local system extraPlatforms machineSystem
system="$(nix --extra-experimental-features 'nix-command flakes' config show system)"
extraPlatforms="$(nix --extra-experimental-features 'nix-command flakes' config show extra-platforms)"
if [[ $# -gt 0 ]]; then
machineSystem=$1
elif [[ -n ${nixosSystem} ]]; then
machineSystem="$(cat "${nixosSystem}"/system)"
else
machineSystem="$(nix --extra-experimental-features 'nix-command flakes' eval --raw "${flake}"#"${flakeAttr}".pkgs.system 2>/dev/null || echo "unknown")"
if [[ ${machineSystem} == "unknown" ]]; then
buildOn=auto
return
fi
fi
if [[ ${system} == "${machineSystem}" ]]; then
buildOn=local
return
fi
if [[ ${extraPlatforms} == "*${machineSystem}*" ]]; then
buildOn=local
return
fi
local entropy
entropy="$(date +'%Y%m%d%H%M%S')"
if nix build \
-L \
"${nixOptions[@]}" \
--expr \
"derivation { system = \"$system\"; name = \"env-$entropy\"; builder = \"/bin/sh\"; args = [ \"-c\" \"echo > \$out\" ]; }"; then
# The local build failed
buildOn=local
fi
buildOn=remote
}
generateHardwareConfig() {
local maybeSudo="$maybeSudo"
mkdir -p "$(dirname "$hardwareConfigPath")"
case "$hardwareConfigBackend" in
nixos-facter)
if [[ ${isInstaller} == "y" ]]; then
if [[ ${hasNixOSFacter} == "n" ]]; then
abort "nixos-facter is not available in booted installer, use nixos-generate-config. For nixos-facter, you may want to boot an installer image from here instead: https://github.com/nix-community/nixos-images"
fi
else
maybeSudo=""
fi
step "Generating hardware-configuration.nix using nixos-facter"
runSshNoTty -o ConnectTimeout=10 ${maybeSudo} "nixos-facter" >"$hardwareConfigPath"
;;
nixos-generate-config)
step "Generating hardware-configuration.nix using nixos-generate-config"
runSshNoTty -o ConnectTimeout=10 nixos-generate-config --show-hardware-config --no-filesystems >"$hardwareConfigPath"
;;
*)
abort "Unknown hardware config backend: $hardwareConfigBackend"
;;
esac
# to make sure nix knows about the new file
if command -v git >/dev/null; then
# handle relative paths
hardwareConfigPath="$(realpath "$hardwareConfigPath")"
pushd "$(dirname "$hardwareConfigPath")"
if git rev-parse --is-inside-work-tree >/dev/null; then
git add --intent-to-add --force -- "$hardwareConfigPath"
fi
popd
fi
}
runKexec() {
if [[ ${isKexec} == "y" ]] || [[ ${isInstaller} == "y" ]]; then
return
fi
if [[ ${isContainer} != "none" ]]; then
echo "WARNING: This script does not support running from a '${isContainer}' container. kexec will likely not work" >&2
fi
if [[ $kexecUrl == "" ]]; then
case "${isArch}" in
x86_64 | aarch64)
kexecUrl="https://github.com/nix-community/nixos-images/releases/download/nixos-24.11/nixos-kexec-installer-noninteractive-${isArch}-linux.tar.gz"
;;
*)
abort "Unsupported architecture: ${isArch}. Our default kexec images only support x86_64 and aarch64 cpus. Checkout https://github.com/nix-community/nixos-anywhere/#using-your-own-kexec-image for more information."
;;
esac
fi
step Switching system into kexec
runSsh sh <<SSH
set -efu ${enableDebug}
$maybeSudo rm -rf /root/kexec
$maybeSudo mkdir -p /root/kexec
SSH
# no way to reach global ipv4 destinations, use gh-v6.com automatically if github url
if [[ ${hasIpv6Only} == "y" ]] && [[ $kexecUrl == "https://github.com/"* ]]; then
kexecUrl=${kexecUrl/"github.com"/"gh-v6.com"}
fi
if [[ -f $kexecUrl ]]; then
runSsh "${maybeSudo} tar -C /root/kexec -xvzf-" <"$kexecUrl"
elif [[ ${hasCurl} == "y" ]]; then
runSsh "curl --fail -Ss -L '${kexecUrl}' | ${maybeSudo} tar -C /root/kexec -xvzf-"
elif [[ ${hasWget} == "y" ]]; then
runSsh "wget '${kexecUrl}' -O- | ${maybeSudo} tar -C /root/kexec -xvzf-"
else
curl --fail -Ss -L "${kexecUrl}" | runSsh "${maybeSudo} tar -C /root/kexec -xvzf-"
fi
runSsh <<SSH
TMPDIR=/root/kexec setsid ${maybeSudo} /root/kexec/kexec/run --kexec-extra-flags "${kexecExtraFlags}"
SSH
# use the default SSH port to connect at this point
for i in "${!sshArgs[@]}"; do
if [[ ${sshArgs[i]} == "-p" ]]; then
sshArgs[i + 1]=$postKexecSshPort
break
fi
done
# wait for machine to become unreachable.
while runSshTimeout -- exit 0; do sleep 1; done
# After kexec we explicitly set the user to root@
sshConnection="root@${sshHost}"
# waiting for machine to become available again
until runSsh -o ConnectTimeout=10 -- exit 0; do sleep 5; done
}
runDisko() {
local diskoScript=$1
for path in "${!diskEncryptionKeys[@]}"; do
step "Uploading ${diskEncryptionKeys[$path]} to $path"
runSsh "umask 077; mkdir -p \"$(dirname "$path")\"; cat > $path" <"${diskEncryptionKeys[$path]}"
done
if [[ -n ${diskoScript} ]]; then
nixCopy --to "ssh://$sshConnection?$sshStoreSettings" "$diskoScript"
elif [[ ${buildOn} == "remote" ]]; then
step Building disko script
# We need to do a nix copy first because nix build doesn't have --no-check-sigs
# Use ssh:// here to avoid https://github.com/NixOS/nix/issues/7359
nixCopy --to "ssh://$sshConnection?$sshStoreSettings" "${flake}#${flakeAttr}.system.build.${diskoMode}Script" \
--derivation --no-check-sigs
# If we don't use ssh-ng here, we get `error: operation 'getFSAccessor' is not supported by store`
diskoScript=$(
nixBuild "${flake}#${flakeAttr}.system.build.${diskoAttr}" \
--eval-store auto --store "ssh-ng://$sshConnection?ssh-key=$sshKeyDir%2Fnixos-anywhere&$sshStoreSettings"
)
fi
step Formatting hard drive with disko
runSsh "$diskoScript"
}
nixosInstall() {
local nixosSystem=$1
if [[ -n ${nixosSystem} ]]; then
step Uploading the system closure
nixCopy --to "ssh://$sshConnection?remote-store=local%3Froot=%2Fmnt&$sshStoreSettings" "$nixosSystem"
elif [[ ${buildOn} == "remote" ]]; then
step Building the system closure
# We need to do a nix copy first because nix build doesn't have --no-check-sigs
# Use ssh:// here to avoid https://github.com/NixOS/nix/issues/7359
nixCopy --to "ssh://$sshConnection?remote-store=local%3Froot=%2Fmnt&$sshStoreSettings" "${flake}#${flakeAttr}.system.build.toplevel" \
--derivation --no-check-sigs
# If we don't use ssh-ng here, we get `error: operation 'getFSAccessor' is not supported by store`
nixosSystem=$(
nixBuild "${flake}#${flakeAttr}.system.build.toplevel" \
--eval-store auto --store "ssh-ng://$sshConnection?ssh-key=$sshKeyDir%2Fnixos-anywhere&remote-store=local%3Froot=%2Fmnt&$sshStoreSettings"
)
fi
if [[ -n ${extraFiles} ]]; then
step Copying extra files
tar -C "$extraFiles" -cpf- . | runSsh "tar -C /mnt -xf- --no-same-owner"
runSsh "chmod 755 /mnt" # tar also changes permissions of /mnt
fi
if [[ ${#extraFilesOwnership[@]} -gt 0 ]]; then
# shellcheck disable=SC2016
printf "%s\n" "${!extraFilesOwnership[@]}" "${extraFilesOwnership[@]}" | pr -2t | runSsh 'while read file ownership; do chown -R "$ownership" "/mnt/$file"; done'
fi
step Installing NixOS
runSsh sh <<SSH
set -eu ${enableDebug}
# when running not in nixos we might miss this directory, but it's needed in the nixos chroot during installation
export PATH="\$PATH:/run/current-system/sw/bin"
# needed for installation if initrd-secrets are used
mkdir -p /mnt/tmp
chmod 777 /mnt/tmp
if [ ${copyHostKeys-n} = "y" ]; then
# NB we copy host keys that are in turn copied by kexec installer.
mkdir -m 755 -p /mnt/etc/ssh
for p in /etc/ssh/ssh_host_*; do
# Skip if the source file does not exist (i.e. glob did not match any files)
# or the destination already exists (e.g. copied with --extra-files).
if [ ! -e "\$p" ] || [ -e "/mnt/\$p" ]; then
continue
fi
cp -a "\$p" "/mnt/\$p"
done
fi
# https://stackoverflow.com/a/13864829
if [ ! -z ${NIXOS_NO_CHECK+0} ]; then
export NIXOS_NO_CHECK
fi
nixos-install --no-root-passwd --no-channel-copy --system "$nixosSystem"
if [[ ${phases[reboot]} == 1 ]]; then
if command -v zpool >/dev/null && [ "\$(zpool list)" != "no pools available" ]; then
# we always want to export the zfs pools so people can boot from it without force import
umount -Rv /mnt/
swapoff -a
zpool export -a || true
fi
nohup sh -c 'sleep 6 && reboot' >/dev/null &
fi
SSH
}
main() {
parseArgs "$@"
if [[ ${vmTest} == y ]]; then
if [[ ${hardwareConfigBackend} != "none" ]]; then
abort "--vm-test is not supported with --generate-hardware-config. You need to generate the hardware configuration before you can run the VM test." >&2
fi
runVmTest
exit 0
fi
if [[ ${buildOn} == "auto" ]]; then
checkBuildLocally
fi
# parse flake nixos-install style syntax, get the system attr
if [[ -n ${flake} ]]; then
if [[ ${buildOn} == "local" ]] && [[ ${hardwareConfigBackend} == "none" ]]; then
if [[ ${phases[disko]} == 1 ]]; then
diskoScript=$(nixBuild "${flake}#${flakeAttr}.system.build.${diskoAttr}")
fi
if [[ ${phases[install]} == 1 ]]; then
nixosSystem=$(nixBuild "${flake}#${flakeAttr}.system.build.toplevel")
fi
fi
elif [[ -n ${diskoScript} ]] && [[ -n ${nixosSystem} ]]; then
if [[ ! -e ${diskoScript} ]] || [[ ! -e ${nixosSystem} ]]; then
abort "${diskoScript} and ${nixosSystem} must be existing store-paths"
fi
else
abort "--flake or --store-paths must be set"
fi
if [[ -n ${SSH_PRIVATE_KEY} ]] && [[ -z ${sshPrivateKeyFile} ]]; then
# $sshKeyDir is getting deleted on trap EXIT
sshPrivateKeyFile="$sshKeyDir/from-env"
(
umask 077
printf '%s\n' "$SSH_PRIVATE_KEY" >"$sshPrivateKeyFile"
)
fi
sshSettings=$(ssh "${sshArgs[@]}" -G "${sshConnection}")
sshUser=$(echo "$sshSettings" | awk '/^user / { print $2 }')
sshHost=$(echo "$sshSettings" | awk '/^hostname / { print $2 }')
uploadSshKey
importFacts
if [[ ${hasTar-n} == "n" ]]; then
abort "no tar command found, but required to unpack kexec tarball"
fi
if [[ ${hasCpio-n} == "n" ]]; then
abort "no cpio command found, but required to build the new initrd"
fi
if [[ ${hasSetsid-n} == "n" ]]; then
abort "no setsid command found, but required to run the kexec script under a new session"
fi
maybeSudo=""
if [[ ${hasSudo-n} == "y" ]]; then
maybeSudo="sudo"
elif [[ ${hasDoas-n} == "y" ]]; then
maybeSudo="doas"
fi
if [[ ${isOs} != "Linux" ]]; then
abort "This script requires Linux as the operating system, but got $isOs"
fi
if [[ ${phases[kexec]} == 1 ]]; then
runKexec
fi
if [[ ${hardwareConfigBackend} != "none" ]]; then
generateHardwareConfig
fi
# Before we do not have a valid hardware configuration we don't know the machine system
if [[ ${buildOn} == "auto" ]]; then
local remoteSystem
remoteSystem=$(runSshNoTty -o ConnectTimeout=10 nix --extra-experimental-features nix-command config show system)
checkBuildLocally "${remoteSystem}"
# if we cannot figure it out at this point, we will build on the remote host
if [[ ${buildOn} == "auto" ]]; then
buildOn=remote
fi
fi
if [[ ${buildOn} != "remote" ]] && [[ -n ${flake} ]] && [[ -z ${diskoScript} ]]; then
if [[ ${phases[disko]} == 1 ]]; then
diskoScript=$(nixBuild "${flake}#${flakeAttr}.system.build.${diskoAttr}")
fi
if [[ ${phases[install]} == 1 ]]; then
nixosSystem=$(nixBuild "${flake}#${flakeAttr}.system.build.toplevel")
fi
fi
# Installation will fail if non-root user is used for installer.
# Switch to root user by copying authorized_keys.
if [[ ${isInstaller} == "y" ]] && [[ ${sshUser} != "root" ]]; then
# Allow copy to fail if authorized_keys does not exist, like if using /etc/ssh/authorized_keys.d/
runSsh "${maybeSudo} mkdir -p /root/.ssh; ${maybeSudo} cp ~/.ssh/authorized_keys /root/.ssh || true"
sshConnection="root@${sshHost}"
fi
if [[ ${phases[disko]} == 1 ]]; then
runDisko "$diskoScript"
fi
if [[ ${phases[install]} == 1 ]]; then
nixosInstall "$nixosSystem"
fi
if [[ ${phases[reboot]} == 1 ]]; then
step Waiting for the machine to become unreachable due to reboot
while runSshTimeout -- exit 0; do sleep 1; done
fi
step "Done!"
}
main "$@"

View file

@ -0,0 +1,21 @@
# NixOS-Anywhere Terraform Modules Overview
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.
Here's a brief overview of each module:
- **[All-in-One](all-in-one.md)**: This is a consolidated module that first
installs NixOS using nixos-anywhere and then keeps it updated with
nixos-rebuild. If you choose this, you won't need additional deployment tools
like colmena.
- **[Install](install.md)**: This module focuses solely on installing NixOS via
nixos-anywhere.
- **[NixOS-Rebuild](nixos-rebuild.md)**: Use this module to remotely update an
existing NixOS machine using nixos-rebuild.
- **[Nix-Build](nix-build.md)**: This is a handy helper module designed to build
a flake attribute or an attribute from a nix file.
For detailed information and usage examples, click on the respective module
links above.

View file

@ -0,0 +1,235 @@
# All-in-one
Combines the install and nixos-rebuild module in one interface to install NixOS
with nixos-anywhere and then keep it up-to-date with nixos-rebuild.
## Example
```hcl
locals {
ipv4 = "192.0.2.1"
}
module "deploy" {
source = "github.com/nix-community/nixos-anywhere//terraform/all-in-one"
# with flakes
nixos_system_attr = ".#nixosConfigurations.mymachine.config.system.build.toplevel"
nixos_partitioner_attr = ".#nixosConfigurations.mymachine.config.system.build.diskoScript"
# without flakes
# file can use (pkgs.nixos []) function from nixpkgs
#file = "${path.module}/../.."
#nixos_system_attr = "config.system.build.toplevel"
#nixos_partitioner_attr = "config.system.build.diskoScript"
target_host = local.ipv4
# when instance id changes, it will trigger a reinstall
instance_id = local.ipv4
# useful if something goes wrong
# debug_logging = true
# build the closure on the remote machine instead of locally
# build_on_remote = true
# script is below
extra_files_script = "${path.module}/decrypt-ssh-secrets.sh"
disk_encryption_key_scripts = [{
path = "/tmp/secret.key"
# script is below
script = "${path.module}/decrypt-zfs-key.sh"
}]
# Optional, arguments passed to special_args here will be available from a NixOS module in this example the `terraform` argument:
# { terraform, ... }: {
# networking.interfaces.enp0s3.ipv4.addresses = [{ address = terraform.ip; prefixLength = 24; }];
# }
# Note that this will means that your NixOS configuration will always depend on terraform!
# Skip to `Pass data persistently to the NixOS` for an alternative approach
#special_args = {
# terraform = {
# ip = "192.0.2.0"
# }
#}
}
```
_Note:_ You need to mark scripts as executable (`chmod +x`)
### ./decrypt-ssh-secrets.sh
```bash
#!/usr/bin/env bash
mkdir -p etc/ssh var/lib/secrets
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
umask 0177
sops --extract '["initrd_ssh_key"]' --decrypt "$SCRIPT_DIR/secrets.yaml" >./var/lib/secrets/initrd_ssh_key
# restore umask
umask 0022
for keyname in ssh_host_rsa_key ssh_host_rsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub; do
if [[ $keyname == *.pub ]]; then
umask 0133
else
umask 0177
fi
sops --extract '["'$keyname'"]' --decrypt "$SCRIPT_DIR/secrets.yaml" >"./etc/ssh/$keyname"
done
```
### ./decrypt-zfs-key.sh
```bash
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd "$SCRIPT_DIR"
sops --extract '["zfs-key"]' --decrypt "$SCRIPT_DIR/secrets.yaml"
```
## See also
- [nixos-wiki setup](https://github.com/NixOS/nixos-wiki-infra/blob/main/terraform/nixos-wiki/main.tf)
for hetzner-cloud
## Pass data persistently to the NixOS
This guide outlines how to pass data from Terraform to NixOS by generating a
file during Terraform execution and including it in your NixOS configuration.
This approach works well if your Terraform and NixOS configurations are stored
in the same Git repository.
### Why Use This Method?
This method provides a straightforward way to transfer values from Terraform to
NixOS without relying on special_args.
- **Advantages**:
- You can continue to use nix build or nixos-rebuild to evaluate your
configuration without interruption. Simplifies configuration management by
centralizing state in a single repository.
- **Disadvantages**:
- Deploying new machines requires tracking additional state. Every time
Terraform updates the JSON file, you'll need to commit these changes to your
repository.
### Implementation
Add the following snippet to your Terraform configuration to create and manage a
JSON file containing the necessary variables for NixOS. This file will be
automatically added to your Git repository, ensuring the data persists.
Assuming you have your terraform and nixos configuration in the same git
repository. You can use the following snippet to `git add` a file generated by
`terraform` during execution to pass data from terraform to NixOS. These changes
should be committed afterwards. This is an alternative over using
`special_args`. Advantage: you can still use nix build or nixos-rebuild on your
flake to evaluate your configuration. Disadvantage: Deploying new machines also
means you need to track additional state and make additional commits whenever
terraform updates the json file.
```hcl
locals {
nixos_vars_file = "nixos-vars.json" # Path to the JSON file containing NixOS variables
nixos_vars = {
ip = "192.0.2.0" # Replace with actual variables
}
}
resource "local_file" "nixos_vars" {
content = jsonencode(local.nixos_vars) # Converts variables to JSON
filename = local.nixos_vars_file # Specifies the output file path
file_permission = "600"
# Automatically adds the generated file to Git
provisioner "local-exec" {
interpreter = ["bash", "-c"]
command = "git add -f '${local.nixos_vars_file}'"
}
}
```
After applying the Terraform changes, ensure you commit the updated
`nixos-vars.json` file to your Git repository:
```bash
git commit -m "Update NixOS variables from Terraform"
```
You can import this json file into your configuration like this:
```nix
let
nixosVars = builtins.fromJSON (builtins.readFile ./nixos-vars.json);
in
{
# Example usage of imported variables
networking.hostName = "example-machine";
networking.interfaces.eth0.ipv4.addresses = [
{
address = nixosVars.ip; # Use the IP from nixos-vars.json
prefixLength = 24;
}
];
}
```
<!-- BEGIN_TF_DOCS -->
## Requirements
No requirements.
## Providers
No providers.
## Modules
| Name | Source | Version |
| -------------------------------------------------------------------------------------- | ---------------- | ------- |
| <a name="module_install"></a> [install](#module_install) | ../install | n/a |
| <a name="module_nixos-rebuild"></a> [nixos-rebuild](#module_nixos-rebuild) | ../nixos-rebuild | n/a |
| <a name="module_partitioner-build"></a> [partitioner-build](#module_partitioner-build) | ../nix-build | n/a |
| <a name="module_system-build"></a> [system-build](#module_system-build) | ../nix-build | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------- | :------: |
| <a name="input_debug_logging"></a> [debug\_logging](#input_debug_logging) | Enable debug logging | `bool` | `false` | no |
| <a name="input_deployment_ssh_key"></a> [deployment\_ssh\_key](#input_deployment_ssh_key) | Content of private key used to deploy to the target\_host after initial installation. To ensure maximum security, it is advisable to connect to your host using ssh-agent instead of relying on this variable | `string` | `null` | no |
| <a name="input_disk_encryption_key_scripts"></a> [disk\_encryption\_key\_scripts](#input_disk_encryption_key_scripts) | Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system | <pre>list(object({<br> path = string<br> script = string<br> }))</pre> | `[]` | no |
| <a name="input_extra_environment"></a> [extra\_environment](#input_extra_environment) | Extra environment variables to be set during installation. This can be useful to set extra variables for the extra\_files\_script or disk\_encryption\_key\_scripts | `map(string)` | `{}` | no |
| <a name="input_extra_files_script"></a> [extra\_files\_script](#input_extra_files_script) | A script that should place files in the current directory that will be copied to the targets / directory | `string` | `null` | no |
| <a name="input_file"></a> [file](#input_file) | Nix file containing the nixos\_system\_attr and nixos\_partitioner\_attr. Use this if you are not using flake | `string` | `null` | no |
| <a name="input_install_port"></a> [install\_port](#input_install_port) | SSH port used to connect to the target\_host, before installing NixOS. If null than the value of `target_port` is used | `string` | `null` | no |
| <a name="input_install_ssh_key"></a> [install\_ssh\_key](#input_install_ssh_key) | Content of private key used to connect to the target\_host during initial installation | `string` | `null` | no |
| <a name="input_install_user"></a> [install\_user](#input_install_user) | SSH user used to connect to the target\_host, before installing NixOS. If null than the value of `target_host` is used | `string` | `null` | no |
| <a name="input_instance_id"></a> [instance\_id](#input_instance_id) | The instance id of the target\_host, used to track when to reinstall the machine | `string` | `null` | no |
| <a name="input_kexec_tarball_url"></a> [kexec\_tarball\_url](#input_kexec_tarball_url) | NixOS kexec installer tarball url | `string` | `null` | no |
| <a name="input_nix_options"></a> [nix\_options](#input_nix_options) | the options of nix | `map(string)` | `{}` | no |
| <a name="input_nixos_facter_path"></a> [nixos\_facter\_path](#input_nixos_facter_path) | Path to which to write a `facter.json` generated by `nixos-facter`. | `string` | `""` | no |
| <a name="input_nixos_generate_config_path"></a> [nixos\_generate\_config\_path](#input_nixos_generate_config_path) | Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`. | `string` | `""` | no |
| <a name="input_nixos_partitioner_attr"></a> [nixos\_partitioner\_attr](#input_nixos_partitioner_attr) | Nixos partitioner and mount script i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.diskoNoDeps or just your-evaluated.config.system.build.diskNoDeps. `config.system.build.diskNoDeps` is provided by the disko nixos module | `string` | n/a | yes |
| <a name="input_nixos_system_attr"></a> [nixos\_system\_attr](#input_nixos_system_attr) | The nixos system to deploy i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.toplevel or just your-evaluated-nixos.config.system.build.toplevel if you are not using flakes | `string` | n/a | yes |
| <a name="input_no_reboot"></a> [no\_reboot](#input_no_reboot) | DEPRECATED: Use `phases` instead. Do not reboot after installation | `bool` | `false` | no |
| <a name="input_phases"></a> [phases](#input_phases) | Phases to run. See `nixos-anywhere --help` for more information | `set(string)` | <pre>[<br> "kexec",<br> "disko",<br> "install",<br> "reboot"<br>]</pre> | no |
| <a name="input_special_args"></a> [special\_args](#input_special_args) | A map exposed as NixOS's `specialArgs` thru a file. | `any` | `{}` | no |
| <a name="input_build_on_remote"></a> [build\_on\_remote](#input_build_on_remote) | Build the closure on the remote machine instead of building it locally and copying it over | `bool` | `false` | no |
| <a name="input_stop_after_disko"></a> [stop\_after\_disko](#input_stop_after_disko) | DEPRECATED: Use `phases` instead. Exit after disko formatting | `bool` | `false` | no |
| <a name="input_target_host"></a> [target\_host](#input_target_host) | DNS host to deploy to | `string` | n/a | yes |
| <a name="input_target_port"></a> [target\_port](#input_target_port) | SSH port used to connect to the target\_host after installing NixOS. If install\_port is not set than this port is also used before installing. | `number` | `22` | no |
| <a name="input_target_user"></a> [target\_user](#input_target_user) | SSH user used to connect to the target\_host after installing NixOS. If install\_user is not set than this user is also used before installing. | `string` | `"root"` | no |
## Outputs
| Name | Description |
| ----------------------------------------------------- | ----------- |
| <a name="output_result"></a> [result](#output_result) | n/a |
<!-- END_TF_DOCS -->

View file

@ -0,0 +1,64 @@
module "system-build" {
source = "../nix-build"
attribute = var.nixos_system_attr
file = var.file
nix_options = var.nix_options
special_args = var.special_args
}
module "partitioner-build" {
source = "../nix-build"
attribute = var.nixos_partitioner_attr
file = var.file
nix_options = var.nix_options
special_args = var.special_args
}
locals {
install_user = var.install_user == null ? var.target_user : var.install_user
install_port = var.install_port == null ? var.target_port : var.install_port
}
module "install" {
source = "../install"
kexec_tarball_url = var.kexec_tarball_url
target_user = local.install_user
target_host = var.target_host
target_port = local.install_port
nixos_partitioner = module.partitioner-build.result.out
nixos_system = module.system-build.result.out
ssh_private_key = var.install_ssh_key
debug_logging = var.debug_logging
extra_files_script = var.extra_files_script
disk_encryption_key_scripts = var.disk_encryption_key_scripts
extra_environment = var.extra_environment
instance_id = var.instance_id
phases = var.phases
nixos_generate_config_path = var.nixos_generate_config_path
nixos_facter_path = var.nixos_facter_path
build_on_remote = var.build_on_remote
# deprecated attributes
stop_after_disko = var.stop_after_disko
no_reboot = var.no_reboot
}
module "nixos-rebuild" {
depends_on = [
module.install
]
# Do not execute this step if var.stop_after_disko == true
count = var.stop_after_disko ? 0 : 1
source = "../nixos-rebuild"
nixos_system = module.system-build.result.out
ssh_private_key = var.deployment_ssh_key
target_host = var.target_host
target_user = var.target_user
target_port = var.target_port
install_bootloader = var.install_bootloader
}
output "result" {
value = module.system-build.result
}

View file

@ -0,0 +1,151 @@
variable "kexec_tarball_url" {
type = string
description = "NixOS kexec installer tarball url"
default = null
}
# To make this re-usable we maybe should accept a store path here?
variable "nixos_partitioner_attr" {
type = string
description = "Nixos partitioner and mount script i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.diskoNoDeps or just your-evaluated.config.system.build.diskNoDeps. `config.system.build.diskNoDeps` is provided by the disko nixos module"
}
# To make this re-usable we maybe should accept a store path here?
variable "nixos_system_attr" {
type = string
description = "The nixos system to deploy i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.toplevel or just your-evaluated-nixos.config.system.build.toplevel if you are not using flakes"
}
variable "file" {
type = string
description = "Nix file containing the nixos_system_attr and nixos_partitioner_attr. Use this if you are not using flake"
default = null
}
variable "target_host" {
type = string
description = "DNS host to deploy to"
}
variable "install_user" {
type = string
description = "SSH user used to connect to the target_host, before installing NixOS. If null than the value of `target_host` is used"
default = null
}
variable "install_port" {
type = string
description = "SSH port used to connect to the target_host, before installing NixOS. If null than the value of `target_port` is used"
default = null
}
variable "target_user" {
type = string
description = "SSH user used to connect to the target_host after installing NixOS. If install_user is not set than this user is also used before installing."
default = "root"
}
variable "target_port" {
type = number
description = "SSH port used to connect to the target_host after installing NixOS. If install_port is not set than this port is also used before installing."
default = 22
}
variable "instance_id" {
type = string
description = "The instance id of the target_host, used to track when to reinstall the machine"
default = null
}
variable "install_ssh_key" {
type = string
description = "Content of private key used to connect to the target_host during initial installation"
default = null
}
variable "deployment_ssh_key" {
type = string
description = "Content of private key used to deploy to the target_host after initial installation. To ensure maximum security, it is advisable to connect to your host using ssh-agent instead of relying on this variable"
default = null
}
variable "debug_logging" {
type = bool
description = "Enable debug logging"
default = false
}
variable "stop_after_disko" {
type = bool
description = "DEPRECATED: Use `phases` instead. Exit after disko formatting"
default = false
}
variable "no_reboot" {
type = bool
description = "DEPRECATED: Use `phases` instead. Do not reboot after installation"
default = false
}
variable "phases" {
type = set(string)
description = "Phases to run. See `nixos-anywhere --help` for more information"
default = ["kexec", "disko", "install", "reboot"]
}
variable "extra_files_script" {
type = string
description = "A script that should place files in the current directory that will be copied to the targets / directory"
default = null
}
variable "disk_encryption_key_scripts" {
type = list(object({
path = string
script = string
}))
description = "Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system"
default = []
}
variable "extra_environment" {
type = map(string)
description = "Extra environment variables to be set during installation. This can be useful to set extra variables for the extra_files_script or disk_encryption_key_scripts"
default = {}
}
variable "nix_options" {
type = map(string)
description = "the options of nix"
default = {}
}
variable "nixos_generate_config_path" {
type = string
description = "Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`."
default = ""
}
variable "nixos_facter_path" {
type = string
description = "Path to which to write a `facter.json` generated by `nixos-facter`."
default = ""
}
variable "special_args" {
type = any
default = {}
description = "A map exposed as NixOS's `specialArgs` thru a file."
}
variable "build_on_remote" {
type = bool
description = "Build the closure on the remote machine instead of building it locally and copying it over"
default = false
}
variable "install_bootloader" {
type = bool
description = "Install/re-install the bootloader"
default = false
}

View file

@ -0,0 +1,91 @@
# Install
Install NixOS with nixos-anywhere
## Example
```hcl
locals {
ipv4 = "192.0.2.1"
}
module "system-build" {
source = "github.com/nix-community/nixos-anywhere//terraform/nix-build"
# with flakes
attribute = ".#nixosConfigurations.mymachine.config.system.build.toplevel"
# without flakes
# file can use (pkgs.nixos []) function from nixpkgs
#file = "${path.module}/../.."
#attribute = "config.system.build.toplevel"
}
module "disko" {
source = "github.com/nix-community/nixos-anywhere//terraform/nix-build"
# with flakes
attribute = ".#nixosConfigurations.mymachine.config.system.build.diskoScript"
# without flakes
# file can use (pkgs.nixos []) function from nixpkgs
#file = "${path.module}/../.."
#attribute = "config.system.build.diskoScript"
}
module "install" {
source = "github.com/nix-community/nixos-anywhere//terraform/install"
nixos_system = module.system-build.result.out
nixos_partitioner = module.disko.result.out
target_host = local.ipv4
}
```
<!-- BEGIN_TF_DOCS -->
## Requirements
No requirements.
## Providers
| Name | Version |
| --------------------------------------------------- | ------- |
| <a name="provider_null"></a> [null](#provider_null) | n/a |
## Modules
No modules.
## Resources
| Name | Type |
| ------------------------------------------------------------------------------------------------------------------- | -------- |
| [null_resource.nixos-remote](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
## Inputs
| Name | Description | Type | Default | Required |
| --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------- | :------: |
| <a name="input_build_on_remote"></a> [build\_on\_remote](#input_build_on_remote) | Build the closure on the remote machine instead of building it locally and copying it over | `bool` | `false` | no |
| <a name="input_debug_logging"></a> [debug\_logging](#input_debug_logging) | Enable debug logging | `bool` | `false` | no |
| <a name="input_disk_encryption_key_scripts"></a> [disk\_encryption\_key\_scripts](#input_disk_encryption_key_scripts) | Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system | <pre>list(object({<br> path = string<br> script = string<br> }))</pre> | `[]` | no |
| <a name="input_extra_environment"></a> [extra\_environment](#input_extra_environment) | Extra environment variables to be set during installation. This can be useful to set extra variables for the extra\_files\_script or disk\_encryption\_key\_scripts | `map(string)` | `{}` | no |
| <a name="input_extra_files_script"></a> [extra\_files\_script](#input_extra_files_script) | A script that should place files in the current directory that will be copied to the targets / directory | `string` | `null` | no |
| <a name="input_flake"></a> [flake](#input_flake) | The flake to install the system from | `string` | `""` | no |
| <a name="input_instance_id"></a> [instance\_id](#input_instance_id) | The instance id of the target\_host, used to track when to reinstall the machine | `string` | `null` | no |
| <a name="input_kexec_tarball_url"></a> [kexec\_tarball\_url](#input_kexec_tarball_url) | NixOS kexec installer tarball url | `string` | `null` | no |
| <a name="input_nixos_facter_path"></a> [nixos\_facter\_path](#input_nixos_facter_path) | Path to which to write a `facter.json` generated by `nixos-facter`. This option cannot be set at the same time as `nixos_generate_config_path`. | `string` | `""` | no |
| <a name="input_nixos_generate_config_path"></a> [nixos\_generate\_config\_path](#input_nixos_generate_config_path) | Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`. This option cannot be set at the same time as `nixos_facter_path`. | `string` | `""` | no |
| <a name="input_nixos_partitioner"></a> [nixos\_partitioner](#input_nixos_partitioner) | nixos partitioner and mount script | `string` | `""` | no |
| <a name="input_nixos_system"></a> [nixos\_system](#input_nixos_system) | The nixos system to deploy | `string` | `""` | no |
| <a name="input_no_reboot"></a> [no\_reboot](#input_no_reboot) | DEPRECATED: Use `phases` instead. Do not reboot after installation | `bool` | `false` | no |
| <a name="input_phases"></a> [phases](#input_phases) | Phases to run. See `nixos-anywhere --help` for more information | `list(string)` | <pre>[<br> "kexec",<br> "disko",<br> "install",<br> "reboot"<br>]</pre> | no |
| <a name="input_ssh_private_key"></a> [ssh\_private\_key](#input_ssh_private_key) | Content of private key used to connect to the target\_host | `string` | `""` | no |
| <a name="input_stop_after_disko"></a> [stop\_after\_disko](#input_stop_after_disko) | DEPRECATED: Use `phases` instead. Exit after disko formatting | `bool` | `false` | no |
| <a name="input_target_host"></a> [target\_host](#input_target_host) | DNS host to deploy to | `string` | n/a | yes |
| <a name="input_target_pass"></a> [target\_pass](#input_target_pass) | Password used to connect to the target\_host | `string` | `null` | no |
| <a name="input_target_port"></a> [target\_port](#input_target_port) | SSH port used to connect to the target\_host | `number` | `22` | no |
| <a name="input_target_user"></a> [target\_user](#input_target_user) | SSH user used to connect to the target\_host | `string` | `"root"` | no |
## Outputs
No outputs.
<!-- END_TF_DOCS -->

View file

@ -0,0 +1,35 @@
locals {
disk_encryption_key_scripts = [for k in var.disk_encryption_key_scripts : "\"${k.path}\" \"${k.script}\""]
removed_phases = setunion(var.stop_after_disko ? ["install"] : [], (var.no_reboot ? ["reboot"] : []))
phases = setsubtract(var.phases, local.removed_phases)
arguments = jsonencode({
ssh_private_key = var.ssh_private_key
debug_logging = var.debug_logging
kexec_tarball_url = var.kexec_tarball_url
nixos_partitioner = var.nixos_partitioner
nixos_system = var.nixos_system
target_user = var.target_user
target_host = var.target_host
target_port = var.target_port
target_pass = var.target_pass
extra_files_script = var.extra_files_script
build_on_remote = var.build_on_remote
flake = var.flake
phases = join(",", local.phases)
nixos_generate_config_path = var.nixos_generate_config_path
nixos_facter_path = var.nixos_facter_path
})
}
resource "null_resource" "nixos-remote" {
triggers = {
instance_id = var.instance_id
}
provisioner "local-exec" {
environment = merge({
ARGUMENTS = local.arguments
}, var.extra_environment)
command = "${path.module}/run-nixos-anywhere.sh ${join(" ", local.disk_encryption_key_scripts)}"
quiet = var.debug_logging
}
}

View file

@ -0,0 +1,5 @@
terraform {
required_providers {
null = { source = "hashicorp/null" }
}
}

View file

@ -0,0 +1,92 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
declare -A input
while IFS= read -r -d '' key && IFS= read -r -d '' value; do
input[$key]=$value
done < <(jq -j 'to_entries[] | (.key, "\u0000", .value, "\u0000")' <<<"${ARGUMENTS}")
args=()
if [[ ${input[debug_logging]} == "true" ]]; then
set -x
declare -p input
args+=("--debug")
fi
if [[ ${input[kexec_tarball_url]} != "null" ]]; then
args+=("--kexec" "${input[kexec_tarball_url]}")
fi
if [[ ${input[build_on_remote]} == "true" ]]; then
args+=("--build-on-remote")
fi
if [[ -n ${input[flake]} ]]; then
args+=("--flake" "${input[flake]}")
else
args+=("--store-paths" "${input[nixos_partitioner]}" "${input[nixos_system]}")
fi
if [[ -n ${input[nixos_generate_config_path]} ]]; then
if [[ -n ${input[nixos_facter_path]} ]]; then
echo "cannot set both variables 'nixos_generate_config_path' and 'nixos_facter_path'!" >&2
exit 1
fi
args+=("--generate-hardware-config" "nixos-generate-config" "${input[nixos_generate_config_path]}")
elif [[ -n ${input[nixos_facter_path]} ]]; then
args+=("--generate-hardware-config" "nixos-facter" "${input[nixos_facter_path]}")
fi
args+=(--phases "${input[phases]}")
if [[ ${input[ssh_private_key]} != null ]]; then
export SSH_PRIVATE_KEY="${input[ssh_private_key]}"
fi
if [[ ${input[target_pass]} != null ]]; then
export SSHPASS=${input[target_pass]}
args+=("--env-password")
fi
tmpdir=$(mktemp -d)
cleanup() {
rm -rf "${tmpdir}"
}
trap cleanup EXIT
if [[ ${input[extra_files_script]} != "null" ]]; then
if [[ ! -f ${input[extra_files_script]} ]]; then
echo "extra_files_script '${input[extra_files_script]}' does not exist"
exit 1
fi
if [[ ! -x ${input[extra_files_script]} ]]; then
echo "extra_files_script '${input[extra_files_script]}' is not executable"
exit 1
fi
extra_files_script=$(realpath "${input[extra_files_script]}")
mkdir "${tmpdir}/extra-files"
pushd "${tmpdir}/extra-files"
$extra_files_script
popd
args+=("--extra-files" "${tmpdir}/extra-files")
fi
args+=("-p" "${input[target_port]}")
args+=("${input[target_user]}@${input[target_host]}")
keyIdx=0
while [[ $# -gt 0 ]]; do
if [[ ! -f $2 ]]; then
echo "Script file '$2' does not exist"
exit 1
fi
if [[ ! -x $2 ]]; then
echo "Script file '$2' is not executable"
exit 1
fi
mkdir -p "${tmpdir}/keys"
"$2" >"${tmpdir}/keys/$keyIdx"
args+=("--disk-encryption-keys" "$1" "${tmpdir}/keys/$keyIdx")
shift
shift
keyIdx=$((keyIdx + 1))
done
nix run --extra-experimental-features 'nix-command flakes' "path:${SCRIPT_DIR}/../..#nixos-anywhere" -- "${args[@]}"

View file

@ -0,0 +1,123 @@
variable "kexec_tarball_url" {
type = string
description = "NixOS kexec installer tarball url"
default = null
}
# To make this re-usable we maybe should accept a store path here?
variable "nixos_partitioner" {
type = string
description = "nixos partitioner and mount script"
default = ""
}
# To make this re-usable we maybe should accept a store path here?
variable "nixos_system" {
type = string
description = "The nixos system to deploy"
default = ""
}
variable "target_host" {
type = string
description = "DNS host to deploy to"
}
variable "target_user" {
type = string
description = "SSH user used to connect to the target_host"
default = "root"
}
variable "target_port" {
type = number
description = "SSH port used to connect to the target_host"
default = 22
}
variable "target_pass" {
type = string
description = "Password used to connect to the target_host"
default = null
}
variable "ssh_private_key" {
type = string
description = "Content of private key used to connect to the target_host"
default = ""
}
variable "instance_id" {
type = string
description = "The instance id of the target_host, used to track when to reinstall the machine"
default = null
}
variable "debug_logging" {
type = bool
description = "Enable debug logging"
default = false
}
variable "extra_files_script" {
type = string
description = "A script that should place files in the current directory that will be copied to the targets / directory"
default = null
}
variable "disk_encryption_key_scripts" {
type = list(object({
path = string
script = string
}))
description = "Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system"
default = []
}
variable "extra_environment" {
type = map(string)
description = "Extra environment variables to be set during installation. This can be useful to set extra variables for the extra_files_script or disk_encryption_key_scripts"
default = {}
}
variable "stop_after_disko" {
type = bool
description = "DEPRECATED: Use `phases` instead. Exit after disko formatting"
default = false
}
variable "no_reboot" {
type = bool
description = "DEPRECATED: Use `phases` instead. Do not reboot after installation"
default = false
}
variable "phases" {
type = list(string)
description = "Phases to run. See `nixos-anywhere --help` for more information"
default = ["kexec", "disko", "install", "reboot"]
}
variable "build_on_remote" {
type = bool
description = "Build the closure on the remote machine instead of building it locally and copying it over"
default = false
}
variable "flake" {
type = string
description = "The flake to install the system from"
default = ""
}
variable "nixos_generate_config_path" {
type = string
description = "Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`. This option cannot be set at the same time as `nixos_facter_path`."
default = ""
}
variable "nixos_facter_path" {
type = string
description = "Path to which to write a `facter.json` generated by `nixos-facter`. This option cannot be set at the same time as `nixos_generate_config_path`."
default = ""
}

View file

@ -0,0 +1,47 @@
# Nix-build
Small helper module to run do build a flake attribute or attribute from a nix
file.
## Example
- See [install](install.md) or [nixos-rebuild](nixos-rebuild.md)
<!-- BEGIN_TF_DOCS -->
## Requirements
No requirements.
## Providers
| Name | Version |
| --------------------------------------------------------------- | ------- |
| <a name="provider_external"></a> [external](#provider_external) | n/a |
## Modules
No modules.
## Resources
| Name | Type |
| --------------------------------------------------------------------------------------------------------------------------- | ----------- |
| [external_external.nix-build](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |
## Inputs
| Name | Description | Type | Default | Required |
| ---------------------------------------------------------------------- | --------------------------------------------------- | ------------- | ------- | :------: |
| <a name="input_attribute"></a> [attribute](#input_attribute) | the attribute to build, can also be a flake | `string` | n/a | yes |
| <a name="input_file"></a> [file](#input_file) | the nix file to evaluate, if not run in flake mode | `string` | `null` | no |
| <a name="input_nix_options"></a> [nix\_options](#input_nix_options) | the options of nix | `map(string)` | `{}` | no |
| <a name="input_special_args"></a> [special\_args](#input_special_args) | A map exposed as NixOS's `specialArgs` thru a file. | `any` | `{}` | no |
## Outputs
| Name | Description |
| ----------------------------------------------------- | ----------- |
| <a name="output_result"></a> [result](#output_result) | n/a |
<!-- END_TF_DOCS -->

View file

@ -0,0 +1,17 @@
locals {
nix_options = jsonencode({
options = { for k, v in var.nix_options : k => v }
})
}
data "external" "nix-build" {
program = [ "${path.module}/nix-build.sh" ]
query = {
attribute = var.attribute
file = var.file
nix_options = local.nix_options
special_args = jsonencode(var.special_args)
}
}
output "result" {
value = data.external.nix-build.result
}

View file

@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -efu
declare file attribute nix_options special_args
eval "$(jq -r '@sh "attribute=\(.attribute) file=\(.file) nix_options=\(.nix_options) special_args=\(.special_args)"')"
if [ "${nix_options}" != '{"options":{}}' ]; then
options=$(echo "${nix_options}" | jq -r '.options | to_entries | map("--option \(.key) \(.value)") | join(" ")')
else
options=""
fi
if [[ ${special_args-} == "{}" ]]; then
# no special arguments, proceed as normal
if [[ -n ${file-} ]] && [[ -e ${file-} ]]; then
# shellcheck disable=SC2086
out=$(nix build --no-link --json $options -f "$file" "$attribute")
else
# shellcheck disable=SC2086
out=$(nix build --no-link --json ${options} "$attribute")
fi
else
if [[ ${file-} != 'null' ]]; then
echo "special_args are currently only supported when using flakes!" >&2
exit 1
fi
# pass the args in a pure fashion by extending the original config
rest="$(echo "${attribute}" | cut -d "#" -f 2)"
# e.g. config_path=nixosConfigurations.aarch64-linux.myconfig
config_path="${rest%.config.*}"
# e.g. config_attribute=config.system.build.toplevel
config_attribute="config.${rest#*.config.}"
# grab flake nar from error message
flake_rel="$(echo "${attribute}" | cut -d "#" -f 1)"
# e.g. flake_rel="."
flake_dir="$(readlink -f "${flake_rel}")"
flake_path="${flake_dir}/flake.nix"
flake_json="$(nix flake prefetch "${flake_dir}" --json)"
flake_nar="$(echo "$flake_json" | jq -r '.hash')"
store_path="$(echo "${flake_json}" | jq -r '.storePath')"
# while we have a store path now, for a repo this reflects its root level,
# so search for the largest child segment yielding a match in that store dir.
iter_path="${flake_path}"
while [[ ${iter_path} != "/" ]]; do
parent="$(dirname "${iter_path}")"
child_segment="${flake_path//$parent/}"
if [[ -f "${store_path}${child_segment}" ]]; then
target_segment="${child_segment}"
fi
iter_path="${parent}"
done
# substitute variables into the template
nix_expr="(builtins.getFlake ''file://${flake_dir}?dir=${target_segment//\/flake.nix/}&narHash=${flake_nar}'').${config_path}.extendModules { specialArgs = builtins.fromJSON ''${special_args}''; }"
# inject `special_args` into nixos config's `specialArgs`
# shellcheck disable=SC2086
out=$(nix build --no-link --json ${options} --expr "${nix_expr}" "${config_attribute}")
fi
printf '%s' "$out" | jq -c '.[].outputs'

View file

@ -0,0 +1,22 @@
variable "attribute" {
type = string
description = "the attribute to build, can also be a flake"
}
variable "file" {
type = string
description = "the nix file to evaluate, if not run in flake mode"
default = null
}
variable "nix_options" {
type = map(string)
description = "the options of nix"
default = {}
}
variable "special_args" {
type = any
default = {}
description = "A map exposed as NixOS's `specialArgs` thru a file."
}

View file

@ -0,0 +1,66 @@
# Nixos-rebuild
Update NixOS machine with nixos-rebuild on a remote machine
## Example
```hcl
locals {
ipv4 = "192.0.2.1"
}
module "system-build" {
source = "github.com/nix-community/nixos-anywhere//terraform/nix-build"
# with flakes
attribute = ".#nixosConfigurations.mymachine.config.system.build.toplevel"
# without flakes
# file can use (pkgs.nixos []) function from nixpkgs
#file = "${path.module}/../.."
#attribute = "config.system.build.toplevel"
}
module "deploy" {
source = "github.com/nix-community/nixos-anywhere//terraform/nixos-rebuild"
nixos_system = module.system-build.result.out
target_host = local.ipv4
}
```
<!-- BEGIN_TF_DOCS -->
## Requirements
No requirements.
## Providers
| Name | Version |
| --------------------------------------------------- | ------- |
| <a name="provider_null"></a> [null](#provider_null) | n/a |
## Modules
No modules.
## Resources
| Name | Type |
| -------------------------------------------------------------------------------------------------------------------- | -------- |
| [null_resource.nixos-rebuild](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
## Inputs
| Name | Description | Type | Default | Required |
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------- | -------- | :------: |
| <a name="input_ignore_systemd_errors"></a> [ignore\_systemd\_errors](#input_ignore_systemd_errors) | Ignore systemd errors happening during deploy | `bool` | `false` | no |
| <a name="input_nixos_system"></a> [nixos\_system](#input_nixos_system) | The nixos system to deploy | `string` | n/a | yes |
| <a name="input_ssh_private_key"></a> [ssh\_private\_key](#input_ssh_private_key) | Content of private key used to connect to the target\_host. If set to - no key is passed to openssh and ssh will use its own configuration | `string` | `"-"` | no |
| <a name="input_target_host"></a> [target\_host](#input_target_host) | DNS host to deploy to | `string` | n/a | yes |
| <a name="input_target_port"></a> [target\_port](#input_target_port) | SSH port used to connect to the target\_host | `number` | `22` | no |
| <a name="input_target_user"></a> [target\_user](#input_target_user) | User to deploy as | `string` | `"root"` | no |
## Outputs
No outputs.
<!-- END_TF_DOCS -->

View file

@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -uex -o pipefail
if [ "$#" -ne 6 ]; then
echo "USAGE: $0 NIXOS_SYSTEM TARGET_USER TARGET_HOST TARGET_PORT IGNORE_SYSTEMD_ERRORS INSTALL_BOOTLOADER" >&2
exit 1
fi
NIXOS_SYSTEM=$1
TARGET_USER=$2
TARGET_HOST=$3
TARGET_PORT=$4
IGNORE_SYSTEMD_ERRORS=$5
INSTALL_BOOTLOADER=$6
shift 6
TARGET="${TARGET_USER}@${TARGET_HOST}"
workDir=$(mktemp -d)
trap 'rm -rf "$workDir"' EXIT
sshOpts=(-p "${TARGET_PORT}")
sshOpts+=(-o UserKnownHostsFile=/dev/null)
sshOpts+=(-o StrictHostKeyChecking=no)
set +x
if [[ -n ${SSH_KEY+x} && ${SSH_KEY} != "-" ]]; then
sshPrivateKeyFile="$workDir/ssh_key"
# Create the file with 0700 - umask calculation: 777 - 700 = 077
(
umask 077
echo "$SSH_KEY" >"$sshPrivateKeyFile"
)
unset SSH_AUTH_SOCK # don't use system agent if key was supplied
sshOpts+=(-o "IdentityFile=${sshPrivateKeyFile}")
fi
set -x
try=1
until NIX_SSHOPTS="${sshOpts[*]}" nix copy -s --experimental-features nix-command --to "ssh://$TARGET" "$NIXOS_SYSTEM"; do
if [[ $try -gt 10 ]]; then
echo "retries exhausted" >&2
exit 1
fi
sleep 10
try=$((try + 1))
done
if [[ $INSTALL_BOOTLOADER == "true" ]]; then
extra_env='NIXOS_INSTALL_BOOTLOADER=1'
else
extra_env=''
fi
switchCommand="nix-env -p /nix/var/nix/profiles/system --set $(printf "%q" "$NIXOS_SYSTEM"); $extra_env /nix/var/nix/profiles/system/bin/switch-to-configuration switch"
if [[ $TARGET_USER != "root" ]]; then
switchCommand="sudo bash -c '$switchCommand'"
fi
deploy_status=0
# shellcheck disable=SC2029
ssh "${sshOpts[@]}" "$TARGET" "$switchCommand" || deploy_status="$?"
if [[ $IGNORE_SYSTEMD_ERRORS == "true" && $deploy_status == "4" ]]; then
exit 0
fi
exit "$deploy_status"

View file

@ -0,0 +1,11 @@
resource "null_resource" "nixos-rebuild" {
triggers = {
store_path = var.nixos_system
}
provisioner "local-exec" {
environment = {
SSH_KEY = var.ssh_private_key
}
command = "${path.module}/deploy.sh ${var.nixos_system} ${var.target_user} ${var.target_host} ${var.target_port} ${var.ignore_systemd_errors} ${var.install_bootloader}"
}
}

View file

@ -0,0 +1,39 @@
variable "nixos_system" {
type = string
description = "The nixos system to deploy"
}
variable "target_host" {
type = string
description = "DNS host to deploy to"
}
variable "target_user" {
type = string
default = "root"
description = "User to deploy as"
}
variable "target_port" {
type = number
description = "SSH port used to connect to the target_host"
default = 22
}
variable "ssh_private_key" {
type = string
description = "Content of private key used to connect to the target_host. If set to - no key is passed to openssh and ssh will use its own configuration"
default = "-"
}
variable "ignore_systemd_errors" {
type = bool
description = "Ignore systemd errors happening during deploy"
default = false
}
variable "install_bootloader" {
type = bool
description = "Install/re-install the bootloader"
default = false
}

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
files=()
find "${SCRIPT_DIR}"/* -type d | while read -r i; do
module_name=$(basename "$i")
markdown_file="${SCRIPT_DIR}/${module_name}.md"
terraform-docs --config "${SCRIPT_DIR}/.terraform-docs.yml" markdown table --output-file "${markdown_file}" --output-mode inject "${module_name}"
files+=("${markdown_file}")
done
cd ..
nix fmt -- --no-cache

View file

@ -0,0 +1,33 @@
{ withSystem, inputs, ... }:
{
flake.checks.x86_64-linux = withSystem "x86_64-linux" (
{
pkgs,
system,
inputs',
config,
...
}:
let
testInputsUnstable = {
inherit pkgs;
inherit (inputs.disko.nixosModules) disko;
nixos-anywhere = config.packages.nixos-anywhere;
kexec-installer = "${inputs'.nixos-images.packages.kexec-installer-nixos-unstable-noninteractive}/nixos-kexec-installer-noninteractive-${system}.tar.gz";
};
testInputsStable = testInputsUnstable // {
kexec-installer = "${inputs'.nixos-images.packages.kexec-installer-nixos-stable-noninteractive}/nixos-kexec-installer-noninteractive-${system}.tar.gz";
};
in
{
from-nixos = import ./from-nixos.nix testInputsUnstable;
from-nixos-stable = import ./from-nixos.nix testInputsStable;
from-nixos-with-sudo = import ./from-nixos-with-sudo.nix testInputsUnstable;
from-nixos-with-sudo-stable = import ./from-nixos-with-sudo.nix testInputsStable;
from-nixos-with-generated-config = import ./from-nixos-generate-config.nix testInputsUnstable;
from-nixos-build-on-remote = import ./from-nixos-build-on-remote.nix testInputsUnstable;
from-nixos-separated-phases = import ./from-nixos-separated-phases.nix testInputsUnstable;
}
);
}

View file

@ -0,0 +1,56 @@
(import ./lib/test-base.nix) (
{ pkgs, ... }:
{
name = "from-nixos-build-on-remote";
nodes = {
installer = ./modules/installer.nix;
installed = {
services.openssh.enable = true;
virtualisation.memorySize = 1024;
users.users.root.openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
};
};
testScript = ''
def create_test_machine(
oldmachine=None, **kwargs
): # taken from <nixpkgs/nixos/tests/installer.nix>
start_command = [
"${pkgs.qemu_test}/bin/qemu-kvm",
"-cpu",
"max",
"-m",
"1024",
"-virtfs",
"local,path=/nix/store,security_model=none,mount_tag=nix-store",
"-drive",
f"file={oldmachine.state_dir}/installed.qcow2,id=drive1,if=none,index=1,werror=report",
"-device",
"virtio-blk-pci,drive=drive1",
]
machine = create_machine(start_command=" ".join(start_command), **kwargs)
driver.machines.append(machine)
return machine
start_all()
installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--build-on-remote \
--kexec /etc/nixos-anywhere/kexec-installer \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
root@installed >&2
""")
try:
installed.shutdown()
except BrokenPipeError:
# qemu has already exited
pass
new_machine = create_test_machine(oldmachine=installed, name="after_install")
new_machine.start()
hostname = new_machine.succeed("hostname").strip()
assert "nixos-anywhere" == hostname, f"'nixos-anywhere' != '{hostname}'"
'';
}
)

View file

@ -0,0 +1,71 @@
(import ./lib/test-base.nix) {
name = "from-nixos-generate-config";
nodes = {
installer =
{ pkgs, ... }:
{
imports = [
./modules/installer.nix
];
environment.systemPackages = [ pkgs.jq ];
};
installed = {
services.openssh.enable = true;
virtualisation.memorySize = 1024;
users.users.root.openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
};
};
testScript = ''
start_all()
installer.fail("test -f /tmp/hw/config.nix")
installer.succeed("echo super-secret > /tmp/disk-1.key")
output = installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--kexec /etc/nixos-anywhere/kexec-installer \
--disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
--disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
--phases kexec,disko \
--generate-hardware-config nixos-generate-config /tmp/hw/config.nix \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
root@installed >&2
echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
root@installed cat /tmp/disk-1.key)'"
echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
root@installed cat /tmp/disk-2.key)'"
""")
installer.succeed("cat /tmp/hw/config.nix >&2")
installer.succeed("nix-instantiate --parse /tmp/hw/config.nix")
assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
installer.fail("test -f /test/hw/config.json")
output = installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--kexec /etc/nixos-anywhere/kexec-installer \
--disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
--disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
--phases kexec,disko \
--generate-hardware-config nixos-facter /tmp/hw/config.json \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
installed >&2
echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
root@installed cat /tmp/disk-1.key)'"
echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
root@installed cat /tmp/disk-2.key)'"
""")
installer.succeed("cat /tmp/hw/config.json >&2")
installer.succeed("jq < /tmp/hw/config.json")
assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
'';
}

View file

@ -0,0 +1,52 @@
(import ./lib/test-base.nix) {
name = "from-nixos-separated-phases";
nodes = {
installer = ./modules/installer.nix;
installed = {
services.openssh.enable = true;
virtualisation.memorySize = 1024;
users.users.nixos = {
isNormalUser = true;
openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
extraGroups = [ "wheel" ];
};
security.sudo.enable = true;
security.sudo.wheelNeedsPassword = false;
};
};
testScript = ''
start_all()
with subtest("Kexec Phase"):
installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--kexec /etc/nixos-anywhere/kexec-installer \
--phases kexec \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
nixos@installed >&2
""")
with subtest("Disko Phase"):
output = installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--phases disko \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
installed >&2
""")
with subtest("Install Phase"):
installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--phases install \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
root@installed >&2
""")
'';
}

View file

@ -0,0 +1,40 @@
(import ./lib/test-base.nix) {
name = "from-nixos-with-sudo";
nodes = {
installer = ./modules/installer.nix;
installed = {
services.openssh.enable = true;
virtualisation.memorySize = 1024;
users.users.nixos = {
isNormalUser = true;
openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
extraGroups = [ "wheel" ];
};
security.sudo.enable = true;
security.sudo.wheelNeedsPassword = false;
};
};
testScript = ''
start_all()
installer.succeed("echo super-secret > /tmp/disk-1.key")
output = installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--kexec /etc/nixos-anywhere/kexec-installer \
--phases kexec,disko \
--disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
--disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
nixos@installed >&2
echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
root@installed cat /tmp/disk-1.key)'"
echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
root@installed cat /tmp/disk-2.key)'"
""")
assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
'';
}

View file

@ -0,0 +1,76 @@
(import ./lib/test-base.nix) (
{ pkgs, ... }:
{
name = "from-nixos";
nodes = {
installer = ./modules/installer.nix;
installed = {
services.openssh.enable = true;
virtualisation.memorySize = 1024;
users.users.root.openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
};
};
testScript = ''
def create_test_machine(
oldmachine=None, **kwargs
): # taken from <nixpkgs/nixos/tests/installer.nix>
start_command = [
"${pkgs.qemu_test}/bin/qemu-kvm",
"-cpu",
"max",
"-m",
"1024",
"-virtfs",
"local,path=/nix/store,security_model=none,mount_tag=nix-store",
"-drive",
f"file={oldmachine.state_dir}/installed.qcow2,id=drive1,if=none,index=1,werror=report",
"-device",
"virtio-blk-pci,drive=drive1",
]
machine = create_machine(start_command=" ".join(start_command), **kwargs)
driver.machines.append(machine)
return machine
start_all()
installer.succeed("mkdir -p /tmp/extra-files/var/lib/secrets")
installer.succeed("echo value > /tmp/extra-files/var/lib/secrets/key")
installer.succeed("mkdir -p /tmp/extra-files/home/user/.ssh")
installer.succeed("echo secretkey > /tmp/extra-files/home/user/.ssh/id_ed25519")
installer.succeed("echo publickey > /tmp/extra-files/home/user/.ssh/id_ed25519.pub")
installer.succeed("chmod 600 /tmp/extra-files/home/user/.ssh/id_ed25519")
ssh_key_path = "/etc/ssh/ssh_host_ed25519_key.pub"
ssh_key_output = installer.wait_until_succeeds(f"""
ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
root@installed cat {ssh_key_path}
""")
installer.succeed("""
nixos-anywhere \
-i /root/.ssh/install_key \
--debug \
--kexec /etc/nixos-anywhere/kexec-installer \
--extra-files /tmp/extra-files \
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
--chown /home/user 1000:100 \
--copy-host-keys \
root@installed >&2
""")
try:
installed.shutdown()
except BrokenPipeError:
# qemu has already exited
pass
new_machine = create_test_machine(oldmachine=installed, name="after_install")
new_machine.start()
hostname = new_machine.succeed("hostname").strip()
assert "nixos-anywhere" == hostname, f"'nixos-anywhere' != '{hostname}'"
content = new_machine.succeed("cat /var/lib/secrets/key").strip()
assert "value" == content, f"secret does not have expected value: {content}"
ssh_key_content = new_machine.succeed(f"cat {ssh_key_path}").strip()
assert ssh_key_content in ssh_key_output, "SSH host identity changed"
priv_key_perms = new_machine.succeed("stat -c %a /home/user/.ssh/id_ed25519").strip()
assert priv_key_perms == "600", f"unexpected permissions for private key: {priv_key_perms}"
user_dir_ownership = new_machine.succeed("stat -c %u:%g /home/user").strip()
assert user_dir_ownership == "1000:100", f"unexpected user home dir permissions: {user_dir_ownership}"
'';
}
)

View file

@ -0,0 +1,20 @@
test:
{
pkgs ? import <nixpkgs> { },
nixos-anywhere ? pkgs.callPackage ../../src { },
disko ? "${builtins.fetchTarball "https://github.com/nix-community/disko/archive/master.tar.gz"}/module.nix",
kexec-installer ? builtins.fetchurl "https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-kexec-installer-noninteractive-${pkgs.stdenv.hostPlatform.system}.tar.gz",
...
}:
let
inherit (pkgs) lib;
nixos-lib = import (pkgs.path + "/nixos/lib") { };
in
(nixos-lib.runTest {
hostPkgs = pkgs;
# speed-up evaluation
defaults.documentation.enable = lib.mkDefault false;
# to accept external dependencies such as disko
node.specialArgs.inputs = { inherit nixos-anywhere disko kexec-installer; };
imports = [ test ];
}).config.result

View file

@ -0,0 +1,22 @@
{ pkgs, inputs, ... }:
let
disko = inputs.disko;
kexec-installer = inputs.kexec-installer;
system-to-install = pkgs.nixos [
./system-to-install.nix
disko
];
in
{
system.activationScripts.rsa-key = ''
${pkgs.coreutils}/bin/install -D -m600 ${./ssh-keys/ssh} /root/.ssh/install_key
'';
environment.systemPackages = [ inputs.nixos-anywhere ];
environment.etc = {
"nixos-anywhere/disko".source = system-to-install.config.system.build.diskoScriptNoDeps;
"nixos-anywhere/system-to-install".source = system-to-install.config.system.build.toplevel;
"nixos-anywhere/kexec-installer".source = kexec-installer;
};
}

View file

@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDM4kPg4V7DMYceuA8VIUdBvw30gM9qagERA8v1VGBBBQAAAJDpULAq6VCw
KgAAAAtzc2gtZWQyNTUxOQAAACDM4kPg4V7DMYceuA8VIUdBvw30gM9qagERA8v1VGBBBQ
AAAECpjfl5WMMIDvEyZJTeXzRNFzpDpj4fqdIXHZauKAlE5MziQ+DhXsMxhx64DxUhR0G/
DfSAz2pqAREDy/VUYEEFAAAACWxhc3NAbW9ycwECAwQ=
-----END OPENSSH PRIVATE KEY-----

View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMziQ+DhXsMxhx64DxUhR0G/DfSAz2pqAREDy/VUYEEF

View file

@ -0,0 +1,47 @@
{ modulesPath, self, ... }:
{
imports = [
(modulesPath + "/testing/test-instrumentation.nix")
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/profiles/minimal.nix")
];
networking.hostName = "nixos-anywhere";
documentation.enable = false;
hardware.enableAllFirmware = false;
networking.hostId = "8425e349"; # from profiles/base.nix, needed for zfs
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
disko.devices = {
disk = {
vda = {
device = "/dev/vda";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02";
};
ESP = {
size = "100M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View file

@ -0,0 +1,23 @@
{ inputs, ... }:
{
imports = [
inputs.treefmt-nix.flakeModule
];
perSystem =
{ config, pkgs, ... }:
{
treefmt = {
projectRootFile = "flake.nix";
programs.mdsh.enable = true;
programs.nixpkgs-fmt.enable = true;
programs.shellcheck.enable = true;
programs.shfmt.enable = true;
programs.deno.enable = !pkgs.deno.meta.broken;
settings.formatter.shellcheck.options = [
"-s"
"bash"
];
};
formatter = config.treefmt.build.wrapper;
};
}

View file

@ -0,0 +1,23 @@
To run `nixos-anywhere` from the repo:
```console
nix run . -- --help
```
To format the code:
```console
nix fmt
```
To run all tests:
```console
nix flake check -vL
```
To run an individual test:
```
nix build .#checks.x86_64-linux.from-nixos -vL
```

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Numtide
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,123 @@
# nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" width="150" height="150">
[Documentation Index](docs/INDEX.md)
## README
Setting up a new machine is time-consuming, and becomes complicated when it
needs to be done remotely. If you're installing NixOS, the **nixos-anywhere**
tool allows you to pre-configure the whole process including:
- Disk partitioning and formatting
- Configuring and installing NixOS
- Installing additional files and software
You can then initiate an unattended installation with a single CLI command.
Since **nixos-anywhere** can access the new machine using SSH, it's ideal for
remote installations.
Once you have initiated the command, there is no need to 'babysit' the
installation. It all happens automatically.
You can use the stored configuration to repeat the same installation if you need
to.
## Overview
If you have machines on a mix of platforms, you'll need a common installation
solution that works anywhere. **nixos-anywhere** is ideal in this situation.
**nixos-anywhere** can be used equally well for cloud servers, bare metal
servers such as Hetzner, and local servers accessible via a LAN. You can create
standard configurations, and use the same configuration to create identical
servers anywhere.
You first create Nix configurations to specify partitioning, formatting and
NixOS configurations. Further options can be controlled by a flake and by
run-time switches.
Once the configuration has been created, a single command will:
- Connect to the remote server via SSH
- Detect whether a NixOS installer is present; if not, it will use the Linux
`kexec` tool to boot into a Nixos installer.
- Use the [disko](https://github.com/nix-community/disko) tool to partition and
format the hard drive
- Install NixOS
- Optionally install any Nix packages and other software required.
- Optionally copy additional files to the new machine
It's also possible to use **nixos-anywhere** to simplify the installation on a
machine that has no current operating system, first booting from a NixOS
installer image. This feature is described in the
[how-to guide](./docs/howtos/no-os.md#installing-on-a-machine-with-no-operating-system).
It's useful because you can pre-configure your required software and
preferences, and build the new machine with a single command.
**Important Note:** Never use a production server as the target. It will be
completely overwritten and all data lost. This tool should only be used for
commissioning a new computer or repurposing an old machine once all important
data has been migrated.
## Prerequisites
- Source Machine:
- Can be any machine with Nix installed, e.g. a NixOS machine.
- Target Machine:
- Unless you're using the option to boot from a NixOS installer image, or
providing your own `kexec` image, it must be running x86-64 Linux with kexec
support. Most `x86_64` Linux systems do have kexec support. By providing
your own [image](./docs/howtos/custom-kexec.md#using-your-own-kexec-image)
you can also perform kexec for other architectures eg aarch64
- 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.
- When `kexec` is used the target must have at least 1 GB of RAM, excluding
swap.
## How to use nixos-anywhere
The [Quickstart Guide](./docs/quickstart.md) gives more information on how to
run **nixos-anywhere** in its simplest form. For more specific instructions to
suit individual requirements, see the [How To Guide](./docs/howtos/INDEX.md).
## Related Tools
**nixos-anywhere** makes use of the
[disko](https://github.com/nix-community/disko) tool to handle the partitioning
and formatting of the disks.
## Contact
For questions, come join us in the
[nixos-anywhere](https://matrix.to/#/#nixos-anywhere:nixos.org) matrix room.
## Licensing and Contribution details
This software is provided free under the
[MIT License](https://opensource.org/licenses/MIT).
---
This project is supported by [Numtide](https://numtide.com/).
![Untitledpng](https://codahosted.io/docs/6FCIMTRM0p/blobs/bl-sgSunaXYWX/077f3f9d7d76d6a228a937afa0658292584dedb5b852a8ca370b6c61dabb7872b7f617e603f1793928dc5410c74b3e77af21a89e435fa71a681a868d21fd1f599dd10a647dd855e14043979f1df7956f67c3260c0442e24b34662307204b83ea34de929d)
We are a team of independent freelancers that love open source.  We help our
customers make their project lifecycles more efficient by:
- Providing and supporting useful tools such as this one
- Building and deploying infrastructure, and offering dedicated DevOps support
- Building their in-house Nix skills, and integrating Nix with their workflows
- Developing additional features and tools
- Carrying out custom research and development.
[Contact us](https://numtide.com/contact) if you have a project in mind, or if
you need help with any of our supported tools, including this one. We'd love to
hear from you.

View file

@ -0,0 +1,11 @@
# Table of Content: - nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="149">
- [README](../README.md)
- [Quickstart](./quickstart.md)
- [System Requirements](./requirements.md)
- [How to Guide](./howtos/INDEX.md)
- [Reference](./reference.md)

View file

@ -0,0 +1,26 @@
# Summary: - nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="149">
The **nixos-anywhere** tool allows you to pre-configure the whole process of
installing NixOS, and run the install remotely with a single CLI command.
Refer to the following documentation for more information.
[System Requirements](./requirements.md): CPU and memory requirements
[Quickstart](./quickstart.md): Instructions for a typical installation
[How to Guide](./howtos/INDEX.md): Instructions for non-typical use cases
- [Installing on a machine with no operating system](./howtos/no-os.md)
- [Using your own kexec image](./howtos/custom-kexec.md)
- [Secrets and full disk encryption](./howtos/secrets.md)
- [Use without flakes](./howtos/use-without-flakes.md)
- [Terraform](./howtos/terraform.md)
- [Nix-channels / `NIX_PATH`](./howtos/nix-path.md)
- [IPv6-only targets](./howtos/ipv6.md)
[Reference](./reference.md): Reference Guide

View file

@ -0,0 +1,6 @@
[book]
authors = [ ]
language = "en"
multilingual = false
src = "."
title = "nixos-anywhere - install NixOS everywhere"

View file

@ -0,0 +1,63 @@
# CLI
```
Usage: nixos-anywhere [options] [<ssh-host>]
Options:
* -f, --flake <flake_uri>
set the flake to install the system from.
* --target-host <ssh-host>
specified the SSH target host to deploy onto.
* -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
* --env-password
set a password used by ssh-copy-id, the password should be set by
the environment variable SSHPASS
* -s, --store-paths <disko-script> <nixos-system>
set the store paths to the disko-script and nixos-system directly
if this is given, 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
* --kexec-extra-flags
extra flags to add into the call to kexec, e.g. "--no-sync"
* --ssh-store-setting <key> <value>
ssh store settings appended to the store URI, e.g. "compress true". <value> needs to be URI encoded.
* --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 <path>
contents of local <path> are recursively copied to the root (/) of the new NixOS installation. Existing files are overwritten
Copied files will be owned by root. See documentation for details.
* --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.
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
```

View file

@ -0,0 +1,21 @@
{
perSystem =
{ pkgs, lib, ... }:
{
packages.docs =
pkgs.runCommand "nixos-anywhere-docs"
{
passthru.serve = pkgs.writeShellScriptBin "serve" ''
set -euo pipefail
cd docs
workdir=$(${pkgs.coreutils}/bin/mktemp -d)
trap 'rm -rf "$workdir"' EXIT
${pkgs.mdbook}/bin/mdbook serve --dest-dir "$workdir"
'';
}
''
cp -r ${lib.cleanSource ./.}/* .
${pkgs.mdbook}/bin/mdbook build --dest-dir "$out"
'';
};
}

View file

@ -0,0 +1 @@
# How to Guide

View file

@ -0,0 +1,29 @@
# How To Guide: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="129">
[Documentation Index](./INDEX.md)
## Contents
[Installing on a machine with no operating system](./no-os.md)
[Kexec on systems with limited RAM](./limited-ram.md)
[Copying files to the new installation](./extra-files.md)
[Using your own kexec image](./custom-kexec.md)
[Repair installations without wiping data](./disko-modes.md)
[Secrets and full disk encryption](./secrets.md)
[Use without flakes](./use-without-flakes.md)
[Terraform](./terraform.md)
[Nix-channels / `NIX_PATH`](./nix-path.md)
[IPv6-only targets](./ipv6.md)

View file

@ -0,0 +1,40 @@
# Using your own kexec image
By default, `nixos-anywhere` downloads the kexec image from the
[NixOS images repository](https://github.com/nix-community/nixos-images#kexec-tarballs).
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:
```nix
{
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
}
```

View file

@ -0,0 +1,19 @@
# Repair installations without wiping data
By default, nixos-anywhere will reformat all configured disks before running the
installation. However it is also possible to mount the filesystems of an
existing installation and run `nixos-install`. This is useful to recover from a
misconfigured NixOS installation by first booting into a NixOS installer or
recovery system.
To only mount existing filesystems, add `--disko-mode mount` to
`nixos-anywhere`:
```
nix run github:nix-community/nixos-anywhere -- --disko-mode mount --flake <path to configuration>#<configuration name> --target-host root@<ip address>
```
1. This will first boot into a nixos-installer
2. Mounts disks with disko
3. Runs nixos-install based on the provided flake
4. Reboots the machine.

View file

@ -0,0 +1,114 @@
# Copying files to the new installation
The `--extra-files <path>` option allows copying files to the target host after
installation.
The contents of the `<path>` is recursively copied and overwrites the targets
root (/). The contents _must_ be in a structure and permissioned as it should be
on the target.
In this way, there is no need to repeatedly pass arguments (eg: a fictional
argument: `--copy <source> <dest>`) to `nixos-anywhere` to complete the intended
outcome.
The path and directory structure passed to `--extra-files` should be prepared
beforehand.
This allows a simple programmatic invocation of `nixos-anywhere` for multiple
hosts.
## Simple Example
You want `/etc/ssh/ssh_host_*` and `/persist` from the local system on the
target. The `<path>` contents will look like this:
```console
$ cd /tmp
$ root=$(mktemp -d)
$ sudo cp --verbose --archive --parents /etc/ssh/ssh_host_* ${root}
$ cp --verbose --archive --link /persist ${root}
```
The directory structure would look like this:
```console
drwx------ myuser1 users 20 tmp.d6nx5QUwPN
drwxr-xr-x root root 6 ├── etc
drwx------ myuser1 users 160 │ └── ssh
.rw------- root root 399 │ ├── ssh_host_ed25519_key
.rw-r--r-- root root 91 │ ├── ssh_host_ed25519_key.pub
drwxr-xr-x myuser1 users 22 └── persist
drwxr-xr-x myuser1 users 14 ├── all
drwxr-xr-x myuser1 users 22 │ ├── my
.rw-r--r-- myuser1 users 6 │ │ ├── test3
drwxr-xr-x myuser1 users 10 │ │ └── things
.rw-r--r-- myuser1 users 6 │ │ └── test4
.rw-r--r-- myuser1 users 6 │ └── test2
drwxr-xr-x myuser1 users 0 ├── blah
.rw-r--r-- myuser1 users 6 └── test
```
**NOTE**: Permissions will be copied, but ownership on the target will be root.
Then pass $root like:
> nixos-anywhere --flake ".#" --extra-files $root --target-host root@newhost
## Programmatic Example
```sh
for host in host1 host2 host3; do
root="target/${host}"
install -d -m755 ${root}/etc/ssh
ssh-keygen -A -C root@${host} -f ${root}
nixos-anywhere --extra-files "${root}" --flake ".#${host}" --target-host "root@${host}"
done
```
## Considerations
### Ownership
The new system may have differing UNIX user and group id's for users created
during installation.
When the files are extracted on the remote the copied data will be owned by
root.
If you wish to change the ownership after the files are copied onto the system,
you can use the `--chown` option.
For example, if you did `--chown /home/myuser/.ssh 1000:100`, this would equate
to running `chown -R /home/myuser/.ssh 1000:100` where the uid is 1000 and the
gid is 100. **Only do this when you can _guarantee_ what the uid and gid will
be.**
### Symbolic Links
Do not create symbolic links to reference data to copy.
GNU `tar` is used to do the copy over ssh. It is an archival tool used to
re/store directory structures as is. Thus `tar` copies symbolic links created
with `ln -s` by default. It does not follow them to copy the underlying file.
### Hard links
**NOTE**: hard links can only be created on the same filesystem.
If you have larger persistent data to copy to the target. GNU `tar` will copy
data referenced by hard links created with `ln`. A hard link does not create
another copy the data.
To copy a directory tree to the new target you can use the `cp` command with the
`--link` option which creates hard links.
#### Example
```sh
cd /tmp
root=$(mktemp -d)
cp --verbose --archive --link --parents /persist/home/myuser ${root}
```
`--parents` will create the directory structure of the source at the
destination.

View file

@ -0,0 +1,23 @@
# 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](https://numtide.com/). 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.

View file

@ -0,0 +1,29 @@
# Kexec on Systems with Limited RAM
When working with nixos-anywhere on systems with limited RAM (around 1GB), you
can use the `--no-disko-deps` option to reduce memory usage during installation.
## How it works
The `--no-disko-deps` option uploads only the disko partitioning script without
including its dependencies. This significantly reduces memory usage because:
1. The installer normally stores all dependencies in memory
2. Partitioning tools can be quite large when bundled with their dependencies
## Usage example
```bash
nix run github:nix-community/nixos-anywhere -- --no-disko-deps --flake <path to configuration>#<configuration name> --target-host root@<ip address>
```
## Trade-off
While this approach saves memory, it means the partitioning tools will be
whatever versions are available on the target system, rather than the specific
versions defined in your NixOS configuration. This could potentially lead to
version inconsistencies between the partitioning tools and the NixOS system
being installed.
This trade-off is usually acceptable for memory-constrained environments where
installation would otherwise fail due to insufficient RAM.

View file

@ -0,0 +1,57 @@
# 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` being 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:
```nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# ... other inputs
outputs = inputs@{ 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 every time the
flake is updated.
In your shell you will see something in your `$NIX_PATH`:
```shellSession
$ echo $NIX_PATH
/root/.nix-defexpr/channels:nixpkgs=/nix/store/8b61j28rpy11dg8hanbs2x710d8w3v0d-source
```
# Solution 2: Manually add the channel
On the installed machine, run:
```shellSession
$ nix-channel --add https://nixos.org/channels/nixos-unstable nixos
$ nix-channel --update
```

View file

@ -0,0 +1,71 @@
# 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](https://nixos.org/manual/nixos/stable/index.html#sec-booting-from-usb)
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' --target-host nixos@fec0::5054:ff:fe12:3456
```
This example assumes a flake in the current directory containing a configuration
named `myconfig`.

View file

@ -0,0 +1,76 @@
# 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](https://github.com/Mic92/sops-nix) or
[agenix](https://github.com/ryantm/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:
```bash
#!/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' --target-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`:
```bash
# 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.

View file

@ -0,0 +1,23 @@
# 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](https://registry.terraform.io/providers/hashicorp/null/latest) and
[external](https://registry.terraform.io/providers/hashicorp/external/latest)
provider.
You can get these by from nixpkgs like this:
```nix
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](https://github.com/nix-community/nixos-anywhere/tree/main/terraform)
for examples and module parameter on how to use the modules.

View file

@ -0,0 +1,82 @@
# Use without flakes
First,
[import the disko NixOS module](https://github.com/nix-community/disko/blob/master/docs/HowTo.md#installing-nixos-module)
in your NixOS configuration and define disko devices as described in the
[examples](https://github.com/nix-community/disko/tree/master/example).
Let's assume that your NixOS configuration lives in `configuration.nix` and your
target machine is called `machine`:
## 1. Download your favourite disk layout:
See https://github.com/nix-community/disko-templates/ for more examples:
The example below will work with both UEFI and BIOS-based systems.
```bash
curl https://raw.githubusercontent.com/nix-community/disko-templates/main/single-disk-ext4/disko-config.nix > ./disko-config.nix
```
## 2. Get a hardware-configuration.nix from on 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](https://github.com/nix-community/nixos-images#kexec-tarballs).
- **Generate Configuration**: Run the following command on the target machine:
```bash
nixos-generate-config --no-filesystems --dir /tmp/config
```
This creates the necessary configuration files under `/tmp/config/`. Copy
`/tmp/config/nixos/hardware-configuration.nix` to your local machine into the
same directory as `disko-config.nix`.
## 3. Set NixOS version to use
```nix
# default.nix
let
# replace nixos-24.11 with your preferred nixos version or revision from here: https://status.nixos.org/
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/refs/heads/nixos-24.11.tar.gz";
in
import (nixpkgs + "/nixos/lib/eval-config.nix") {
modules = [ ./configuration.nix ];
}
```
## 4. Write a NixOS configuration
```nix
# configuration.nix
{
imports = [
"${fetchTarball "https://github.com/nix-community/disko/tarball/master"}/module.nix"
./disko-config.nix
./hardware-configuration.nix
];
# Replace this with the system of the installation target you want to install!!!
disko.devices.disk.main.device = "/dev/sda";
# Set this to the NixOS version that you have set in the previous step.
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "24.11";
}
```
## 5. Build and deploy with nixos-anywhere
Your current directory now should contain the following files from the previous
step:
- `configuration.nix`, `default.nix`, `disko-config.nix` and
`hardware-configuration.nix`
Run `nixos-anywhere` as follows:
```bash
nixos-anywhere --store-paths $(nix-build -A config.system.build.formatScript -A config.system.build.toplevel --no-out-link) root@machine
```

Binary file not shown.

After

(image error) Size: 29 KiB

View file

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
width="512"
height="512"
id="svg322"
sodipodi:docname="logo.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
inkscape:export-filename="logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#">
<defs
id="defs326">
<marker
markerWidth="512"
markerHeight="512"
refX="256"
refY="256"
orient="auto"
id="marker11007">
<path
d="M 0,0 H 512 V 512 H 0 Z"
fill="#fbfdfc"
id="path246" />
</marker>
</defs>
<sodipodi:namedview
id="namedview324"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="2.2304247"
inkscape:cx="32.280848"
inkscape:cy="129.79591"
inkscape:window-width="3840"
inkscape:window-height="2090"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg322" />
<path
d="M0,0 L4,2 L18,10 L27,18 L30,26 L30,34 L27,46 L23,60 L17,73 L8,94 L3,105 L3,116 L7,119 L13,121 L39,126 L55,131 L64,137 L68,142 L70,146 L70,154 L66,162 L57,170 L45,176 L37,179 L20,182 L11,183 L-17,184 L-24,192 L-34,206 L-47,221 L-59,232 L-73,241 L-85,246 L-93,248 L-113,248 L-127,245 L-132,243 L-132,241 L-123,235 L-113,226 L-105,218 L-96,205 L-88,189 L-82,168 L-80,136 L-72,125 L-54,98 L-38,71 L-23,44 L-13,25 L-5,9 Z "
fill="#517ED0"
transform="translate(435,104)"
id="path248" />
<path
d="M0,0 L10,1 L19,6 L31,17 L40,26 L49,37 L57,48 L67,62 L75,74 L91,75 L107,78 L127,85 L141,93 L153,105 L160,115 L167,130 L170,139 L171,149 L162,146 L155,143 L138,139 L110,139 L93,143 L87,146 L80,146 L71,144 L57,142 L22,140 L-23,140 L-45,141 L-65,143 L-74,143 L-81,135 L-83,129 L-83,117 L-79,109 L-73,102 L-63,95 L-46,87 L-28,82 L-11,79 L14,79 L11,71 L-2,48 L-10,34 L-14,23 L-14,13 L-11,7 L-5,2 Z "
fill="#517DD0"
transform="translate(132,13)"
id="path250" />
<path
d="M0,0 L11,2 L20,5 L29,7 L55,7 L72,4 L80,2 L104,7 L126,9 L142,10 L234,10 L240,14 L245,20 L246,24 L246,33 L241,43 L234,50 L221,58 L205,65 L183,70 L162,73 L151,73 L159,87 L174,112 L179,122 L180,125 L180,136 L175,145 L167,150 L153,150 L143,143 L130,130 L117,113 L107,99 L95,80 L90,76 L71,74 L50,69 L34,62 L23,54 L15,46 L8,34 L2,17 L0,7 Z "
fill="#4DB0CE"
transform="translate(213,350)"
id="path252" />
<path
d="M0,0 L2,0 L7,17 L12,30 L21,45 L31,57 L39,64 L46,71 L47,73 L49,89 L54,107 L60,122 L72,147 L83,167 L99,191 L113,212 L117,220 L117,234 L114,240 L111,243 L80,243 L63,231 L50,219 L38,207 L31,199 L30,196 L27,196 L25,200 L11,215 L2,224 L-9,230 L-13,231 L-21,231 L-27,227 L-32,221 L-36,210 L-36,203 L-28,186 L-22,174 L-17,159 L-13,141 L-12,131 L-12,99 L-16,64 L-18,49 L-18,34 L-15,24 L-8,11 Z "
fill="#517ED0"
transform="translate(158,269)"
id="path256" />
<path
d="M0,0 L12,1 L28,8 L40,16 L50,24 L65,37 L75,48 L77,51 L79,51 L89,37 L98,27 L107,18 L117,14 L126,14 L134,19 L139,25 L142,32 L142,40 L137,54 L128,74 L123,88 L118,109 L117,117 L117,147 L121,169 L124,181 L124,200 L120,211 L111,225 L106,229 L100,214 L92,199 L80,183 L70,173 L64,168 L62,153 L57,139 L49,123 L34,97 L21,75 L6,53 L-6,36 L-12,26 L-13,23 L-13,11 L-9,5 L-4,1 Z "
fill="#4EB0CF"
transform="translate(246,0)"
id="path258" />
<path
d="M0,0 L6,0 L6,2 L11,1 L22,1 L32,6 L41,8 L53,8 L60,16 L62,17 L61,22 L56,28 L54,33 L54,43 L58,45 L58,48 L52,47 L46,42 L46,40 L30,45 L24,53 L24,61 L25,62 L31,62 L35,58 L39,59 L41,62 L43,71 L44,72 L50,72 L53,80 L53,82 L61,80 L65,78 L70,78 L72,80 L82,79 L92,84 L103,95 L106,98 L108,99 L117,100 L123,105 L123,114 L118,128 L110,142 L99,156 L88,165 L77,173 L60,180 L55,180 L56,176 L65,170 L67,166 L67,151 L72,144 L73,141 L73,134 L69,129 L65,127 L55,126 L47,116 L45,112 L45,102 L49,97 L55,94 L55,87 L49,83 L44,78 L34,72 L18,64 L11,58 L4,42 L0,35 L1,26 L2,19 L-2,16 L-8,13 L-9,12 L-9,6 L-6,2 Z "
fill="#4D99CF"
transform="translate(223,166)"
id="path260" />
<path
d="M0,0 L9,0 L20,8 L36,14 L44,17 L46,20 L46,27 L42,33 L42,44 L47,49 L46,52 L39,52 L25,46 L15,37 L6,27 L0,17 L-3,10 L-2,2 Z "
fill="#4FABD1"
transform="translate(176,284)"
id="path262"
style="fill:#4d99cf;fill-opacity:1" />
<path
d="M0,0 L14,0 L23,1 L29,5 L30,6 L30,11 L25,10 L24,9 L24,4 L-1,4 Z "
fill="#438ED7"
transform="translate(247,157)"
id="path266" />
<path
id="path268"
transform="translate(203,179)"
d="m 0,0 3,1 1,8 h 8 v 4 l -2,1 H 2 L -2,9 V 2 Z"
style="fill:#4d99cf;fill-opacity:1" />
<path
id="path288"
transform="translate(278,222)"
d="M 0,0 6,1 7,4 9,5 H 2 L -1,3 Z m 0,0 6,1 6,2 8,1 V 7 H 7 L 0,4 Z"
style="fill:#4d99cf;fill-opacity:1" />
<path
d="M0,0 L3,1 L-7,8 L-9,8 L-9,6 L-5,5 L-4,2 Z "
fill="#6184C5"
transform="translate(371,336)"
id="path304"
style="fill:#517ed0;fill-opacity:1" />
<path
id="path314"
style="fill:#4eb0cf;fill-opacity:1"
transform="translate(113,201)"
d="M 0,0 H 2 L 0,4 -2.8602037,5.4301018 -4,6 V 8 L -6,7 Z m 48,-40 h 25 l 14,3 4,2 -1,3 -10,8 -10,11 -9,14 -7,14 -5,17 -2,11 -1,15 -8,10 -12,21 -11,19 -6,10 -11,19 -12,22 -11,21 -9,20 -5,1 -10,-5 -8,-4 -4,-6 -1,-3 v -18 l 6,-26 5,-13 5,-12 6,-13 2,-5 v -6 l -5,-3 -29,-4 -18,-6 -10,-6 -5,-5 -2,-4 V 50 l 7,-8 10,-7 18,-6 10,-2 10,-1 18.016033,-0.600534 L -22,25 l 5,-5 8,-9 7,-9 8,-10 9,-10 9,-9 10,-7 8,-4 z" />
<metadata
id="metadata148">
<rdf:RDF>
<cc:Work
rdf:about="">
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>

After

(image error) Size: 6.4 KiB

View file

@ -0,0 +1,334 @@
# Quickstart Guide: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" width="150" height="150">
[Documentation Index](./INDEX.md)
## Introduction
This guide documents a simple installation of NixOS using **nixos-anywhere** on
a target machine running x86_64 Linux with
[kexec](https://man7.org/linux/man-pages/man8/kexec.8.html) 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](./howtos/INDEX.md) as and when they are available.
You will need:
- A [flake](https://wiki.nixos.org/wiki/Flakes) 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 that is reachable via SSH, either using keys or a password,
and the privilege to either log in directly as root or a user with
password-less sudo.
**nixos-anywhere** doesnt need to be installed. You can run it directly from
[the Github repository.](https://github.com/nix-community/nixos-anywhere)
Details of the flake, the disk configuration and the CLI command are discussed
below.
## Steps required to run nixos-anywhere
### 1. Enable Flakes
Check if your nix has flakes enabled by running `nix flake`. It will tell you if
it's not. To enable flakes, refer to the
[NixOS Wiki](https://wiki.nixos.org/wiki/Flakes#enable-flakes).
### 2. Initialize a Flake
The easiest way to start is to copy our
[example flake.nix](https://github.com/nix-community/nixos-anywhere-examples/blob/main/flake.nix)
into a new directory. This example is tailored for a virtual machine setup
similar to one on [Hetzner Cloud](https://www.hetzner.com/cloud), so you might
need to adapt it for your setup.
If you already have a flake, you can use it by adding
[disko configuration](https://github.com/nix-community/disko?tab=readme-ov-file#how-to-use-disko)
to it.
### 3. Configure your SSH key
If you cloned
[our nixos-anywhere-example](https://github.com/nix-community/nixos-anywhere-examples/blob/main/configuration.nix)
you will also replace the SSH key like this: In your configuration, locate the
line that reads:
```bash
# 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. If you
have a .pem file you can run
```bash
ssh-keygen -y -f /path/to/your/key.pem
```
then paste the result in between the quotes like "ssh-rsa AAA..."
### 4. Configure Storage
In the same directory, create a file called `disk-config.nix`. This file will
define the disk layout for the
[disko](https://github.com/nix-community/disko/blob/master/docs/INDEX.md) tool,
which is used by nixos-anywhere to partition, format, and mount the disks.
For a basic installation, you can copy the contents from the example provided
[here](https://github.com/nix-community/nixos-anywhere-examples/blob/main/disk-config.nix).
This configuration sets up a standard GPT (GUID Partition Table) that is
compatible with both EFI and BIOS systems and mounts the disk as `/dev/sda`. You
may need to adjust `/dev/sda` to match the correct disk on your machine. To
identify the disk, run the `lsblk` command and replace `sda` with the actual
disk name.
For example, on this machine, we would select `/dev/nvme0n1` as the disk:
```
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 1.8T 0 disk
```
If this setup does not match your requirements, you can choose an example that
better suits your disk layout from the
[disko examples](https://github.com/nix-community/disko/tree/master/example).
For more detailed information, refer to the
[disko documentation](https://github.com/nix-community/disko).
### 5. Lock your Flake
```
nix flake lock
```
This will download your flake dependencies and make a `flake.lock` file that
describes how to reproducibly build your system.
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.
### 6. Connectivity to the Target Machine
**nixos-anywhere** will create a temporary SSH key to use for the installation.
If your SSH key is not found, you will be asked for your password. If you are
using a non-root user, you must have access to sudo without a password. To avoid
SSH password prompts, set the `SSHPASS` environment variable to your password
and add `--env-password` to the `nixos-anywhere` command. If providing a
specific SSH key through `-i` (identity_file), this key will then be used for
the installation and no temporary SSH key will be created.
### 7. (Optional) Test your NixOS and Disko configuration
Skip this step and continue with Step 8, if you don't have a hardware
configuration (hardware-configuration.nix or facter.json) generated yet or make
sure you don't import non-existing hardware-configuration.nix or facter.json
during running the vm test.
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
containing `flake.nix` and `disk-config.nix`
- `<configuration name>` must match the name that immediately follows the text
`nixosConfigurations.` in the flake, as indicated by the comment in the
[example](https://github.com/nix-community/nixos-anywhere-examples/blob/main/flake.nix).
```
nix run github:nix-community/nixos-anywhere -- --flake <path to configuration>#<configuration name> --vm-test
```
### 8. Prepare Hardware Configuration
If you're not using a virtual machine, it's recommended to allow
`nixos-anywhere` to generate a hardware configuration during installation. This
ensures that essential drivers, such as those required for disk detection, are
properly configured.
To enable `nixos-anywhere` to integrate its generated configuration into your
NixOS setup, you need to include an import for the hardware configuration
beforehand.
Heres an example:
```diff
nixosConfigurations.generic = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./configuration.nix
+ ./hardware-configuration.nix
];
};
```
When running `nixos-anywhere`, this file is automatically generated by including
the following flags in your command:
`--generate-hardware-config nixos-generate-config ./hardware-configuration.nix`.
The second flag, `./hardware-configuration.nix`, specifies where
`nixos-generate-config` will store the configuration. Adjust this path to
reflect the location where you want the `hardware-configuration.nix` for this
machine to be saved.
#### 8.1 nixos-facter
As an alternative to `nixos-generate-config`, you can use the experimental
[nixos-facter](https://github.com/numtide/nixos-facter) command, which offers
more comprehensive hardware reports and advanced configuration options.
To use `nixos-facter`, add the following to your flake inputs:
```diff
{
+ inputs.nixos-facter-modules.url = "github:numtide/nixos-facter-modules";
}
```
Next, import the module into your configuration and specify `facter.json` as the
path where the hardware report will be saved:
```diff
nixosConfigurations.generic-nixos-facter = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
./configuration.nix
+ nixos-facter-modules.nixosModules.facter
+ { config.facter.reportPath = ./facter.json }
];
};
```
To generate the configuration for `nixos-facter` with `nixos-anywhere`, use the
following flags: `--generate-hardware-config nixos-facter ./facter.json`. The
second flag, `./facter.json`, specifies where `nixos-generate-config` will store
the hardware report. Adjust this path to suit the location where you want the
`facter.json` to be saved.
### 9. Run it
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
containing `flake.nix` and `disk-config.nix`
- `<configuration name>` must match the name that immediately follows the text
`nixosConfigurations.` in the flake, as indicated by the comment in the
[example](https://github.com/nix-community/nixos-anywhere-examples/blob/main/flake.nix).
- `<ip address>` is the IP address of the target machine.
```
nix run github:nix-community/nixos-anywhere -- --flake <path to configuration>#<configuration name> --target-host 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 is
`37.27.18.135`:
```
nix run github:nix-community/nixos-anywhere -- --flake /home/mydir/test#hetzner-cloud --target-host root@37.27.18.135
```
If you also need to generate hardware configuration amend flags for
nixos-generate-config:
```
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>
```
Or these flags if you are using nixos-facter instead:
```
nix run github:nix-community/nixos-anywhere -- --generate-hardware-config nixos-facter ./facter.json --flake <path to configuration>#<configuration name> --target-host root@<ip address>
```
Adjust the location of `./hardware-configuration.nix` and `./facter.json`
accordingly.
**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 the `known_hosts` file (or use the command
`ssh-keygen -R <ip_address>`). The next connection attempt will then treat this
as a new server.
The error message line `Offending ECDSA key in ~/.ssh/known_hosts:6` gives the
line number that needs to be removed from the `known_hosts` file (line 6 in this
example).
# Finished!
**nixos-anywhere**'s job is now done, as it is a tool to install NixOS onto the
target machine.
Any future changes to the configuration should be made to your flake. You would
reference this flake when using the NixOS `nixos-rebuild` command or a separate
3rd party deployment tool of your choice i.e.
[deploy-rs](https://github.com/serokell/deploy-rs),
[colmena](https://github.com/zhaofengli/colmena),
[nixinate](https://github.com/MatthewCroughan/nixinate),
[clan](https://clan.lol/) (author's choice).
To update on the machine locally (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>
```
To update remotely you will need to have configured an
[ssh server](https://search.nixos.org/options?show=services.sshd.enable) and
your ssh key for the
[root user](https://search.nixos.org/options?show=users.users.%3Cname%3E.openssh.authorizedKeys.keys):
```
nixos-rebuild switch --flake <URL to your flake> --target-host "root@<ip address>"
```
See the Nix documentation for use of the flake
[URL-like syntax](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake#url-like-syntax).
For more information on different use cases of **nixos-anywhere** please refer
to the [How to Guide](./howtos/INDEX.md), and for more technical information and
explanation of known error messages, refer to the
[Reference Manual](./reference.md).

View file

@ -0,0 +1,137 @@
# Reference Manual: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img title="" src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" alt="" width="141">
[Documentation Index](./INDEX.md)
TODO: Populate this guide properly
## Contents
[Command Line Usage](#command-line-usage)
[Explanation of known error messages](#explanation-of-known-error-messages)
## Command Line Usage
<!-- `$ bash ./src/nixos-anywhere.sh --help` -->
```
Usage: nixos-anywhere [options] [<ssh-host>]
Options:
* -f, --flake <flake_uri>
set the flake to install the system from. i.e.
nixos-anywhere --flake .#mymachine
Also supports variants:
nixos-anywhere --flake .#nixosConfigurations.mymachine.config.virtualisation.vmVariant
* --target-host <ssh-host>
set the SSH target host to deploy onto.
* -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
* --env-password
set a password used by ssh-copy-id, the password should be set by
the environment variable SSHPASS
* -s, --store-paths <disko-script> <nixos-system>
set the store paths to the disko-script and nixos-system directly
if this is given, flake is not needed
* --kexec <path>
use another kexec tarball to bootstrap NixOS
* --kexec-extra-flags
extra flags to add into the call to kexec, e.g. "--no-sync"
* --ssh-store-setting <key> <value>
ssh store settings appended to the store URI, e.g. "compress true". <value> needs to be URI encoded.
* --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
* --extra-files <path>
contents of local <path> are recursively copied to the root (/) of the new NixOS installation. Existing files are overwritten
Copied files will be owned by root unless specified by --chown option. See documentation for details.
* --chown <path> <ownership>
change ownership of <path> recursively. Recommended to use uid:gid as opposed to username:groupname for ownership.
Option can be specified more than once.
* --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
* --show-trace
show nix build traces
* --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.
* --generate-hardware-config nixos-facter|nixos-generate-config <path>
generate a hardware-configuration.nix file using the specified backend and write it to the specified path.
The backend can be either 'nixos-facter' or 'nixos-generate-config'.
* --phases
comma separated list of phases to run. Default is: kexec,disko,install,reboot
kexec: kexec into the nixos installer
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
install: install the system
reboot: unmount the filesystems, export any ZFS pools and reboot the machine
* --disko-mode disko|mount|format
set the disko mode to format, mount or destroy. Default is disko.
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
* --no-disko-deps
This will only upload the disko script and not the partitioning tools dependencies.
Installers usually have dependencies available.
Use this option if your target machine has not enough RAM to store the dependencies in memory.
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
```
## 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 attribute | 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 <operating system> | 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](./howtos#using-your-own-kexec-image) 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. |
| | | |
| | | |
| | | |
| | | |

View file

@ -0,0 +1,39 @@
# System Requirements: nixos-anywhere
**_Install NixOS everywhere via ssh_**
<img src="https://raw.githubusercontent.com/nix-community/nixos-anywhere/main/docs/logo.png" width="150" height="150">
[Documentation Index](./INDEX.md)
## Requirements
### Source Machine
1. **Supported Systems:**
- Linux or macOS computers with Nix installed.
- NixOS
- Windows systems using WSL2.
2. **Nix Installation:** If Nix is not yet installed on your system, refer to
the [nix installation page](https://nixos.org/download#download-nix).
### 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.
1. **Direct Boot Option:**
- Must be already running a NixOS installer.
2. **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](./howtos/INDEX.md#using-your-own-kexec-image)
manually.
- **Memory Requirements:**
- At least 1 GB of RAM (excluding swap space).

View file

@ -0,0 +1,132 @@
{
"nodes": {
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1741786315,
"narHash": "sha256-VT65AE2syHVj6v/DGB496bqBnu1PXrrzwlw07/Zpllc=",
"owner": "nix-community",
"repo": "disko",
"rev": "0d8c6ad4a43906d14abd5c60e0ffe7b587b213de",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "master",
"repo": "disko",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1741352980,
"narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixos-images": {
"inputs": {
"nixos-stable": [
"nixos-stable"
],
"nixos-unstable": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1741866599,
"narHash": "sha256-Re/T1Cjmiis0tdphj/Wjqt+c2RlMw/il7LBWzvwQPz0=",
"owner": "nix-community",
"repo": "nixos-images",
"rev": "63285ff93fc1daa2caac9f86e2302ae4edc5e84f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-images",
"type": "github"
}
},
"nixos-stable": {
"locked": {
"lastModified": 1741862977,
"narHash": "sha256-prZ0M8vE/ghRGGZcflvxCu40ObKaB+ikn74/xQoNrGQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cdd2ef009676ac92b715ff26630164bb88fec4e0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1742051767,
"narHash": "sha256-JpyjnalnIqJ7cvP8HzaoJN9/i2bDx83dToodHHjGuNg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ec886d10b507760c90ed01e2eac7f0679d0a47ae",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable-small",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"flake-parts": "flake-parts",
"nixos-images": "nixos-images",
"nixos-stable": "nixos-stable",
"nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1739829690,
"narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -0,0 +1,55 @@
{
description = "A universal nixos installer, just needs ssh access to the target system";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable-small";
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
# used for testing
disko = {
url = "github:nix-community/disko/master";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-stable.url = "github:NixOS/nixpkgs/nixos-24.11";
nixos-images.url = "github:nix-community/nixos-images";
nixos-images.inputs.nixos-unstable.follows = "nixpkgs";
nixos-images.inputs.nixos-stable.follows = "nixos-stable";
# used for development
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs:
inputs.flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-linux"
"aarch64-darwin"
];
imports = [
./src/flake-module.nix
./tests/flake-module.nix
./docs/flake-module.nix
# allow to disable treefmt in downstream flakes
] ++ inputs.nixpkgs.lib.optional (inputs.treefmt-nix ? flakeModule) ./treefmt/flake-module.nix;
perSystem =
{ self', lib, ... }:
{
checks =
let
packages = lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages;
devShells = lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells;
in
packages // devShells;
};
};
}

View file

@ -0,0 +1,38 @@
#!/usr/bin/env nix
#! nix shell nixpkgs#bash nixpkgs#gnused --command bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd "$SCRIPT_DIR/.."
version=${1:-}
if [[ -z $version ]]; then
echo "USAGE: $0 version" >&2
exit 1
fi
if [[ "$(git symbolic-ref --short HEAD)" != "main" ]]; then
echo "must be on main branch" >&2
exit 1
fi
# ensure we are up-to-date
uncommitted_changes=$(git diff --compact-summary)
if [[ -n $uncommitted_changes ]]; then
echo -e "There are uncommitted changes, exiting:\n${uncommitted_changes}" >&2
exit 1
fi
git pull git@github.com:nix-community/nixos-anywhere main
unpushed_commits=$(git log --format=oneline origin/main..main)
if [[ $unpushed_commits != "" ]]; then
echo -e "\nThere are unpushed changes, exiting:\n$unpushed_commits" >&2
exit 1
fi
sed -i -e "s!version = \".*\";!version = \"${version}\";!" src/default.nix
git add src/default.nix
nix-shell -p nix-fast-build --command "nix-fast-build --eval-workers 2"
git commit -m "bump version ${version}"
git tag "${version}"
echo "now run 'git push --tags origin main'"

View file

@ -0,0 +1,63 @@
{
stdenv,
openssh,
gitMinimal,
nixVersions,
nix,
coreutils,
curl,
gnugrep,
gnutar,
gawk,
findutils,
gnused,
sshpass,
terraform-docs,
lib,
makeWrapper,
mkShellNoCC,
}:
let
runtimeDeps = [
gitMinimal # for git flakes
# pinned because nix-copy-closure hangs if ControlPath provided for SSH: https://github.com/NixOS/nix/issues/8480
(if lib.versionAtLeast nix.version "2.16" then nix else nixVersions.nix_2_16)
coreutils
curl # when uploading tarballs
gnugrep
gawk
findutils
gnused # needed by ssh-copy-id
sshpass # used to provide password for ssh-copy-id
gnutar # used to upload extra-files
];
in
stdenv.mkDerivation {
pname = "nixos-anywhere";
version = "1.8.0";
src = ./..;
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
install -D --target-directory=$out/libexec/nixos-anywhere/ -m 0755 src/*.sh
# We prefer the system's openssh over our own, since it might come with features not present in ours:
# https://github.com/nix-community/nixos-anywhere/issues/62
makeShellWrapper $out/libexec/nixos-anywhere/nixos-anywhere.sh $out/bin/nixos-anywhere \
--prefix PATH : ${lib.makeBinPath runtimeDeps} --suffix PATH : ${lib.makeBinPath [ openssh ]}
'';
# Dependencies for our devshell
passthru.devShell = mkShellNoCC {
packages = runtimeDeps ++ [
openssh
terraform-docs
];
};
meta = with lib; {
description = "Install nixos everywhere via ssh";
homepage = "https://github.com/nix-community/nixos-anywhere";
license = licenses.mit;
platforms = platforms.all;
};
}

View file

@ -0,0 +1,11 @@
{
perSystem =
{ config, pkgs, ... }:
{
packages = {
nixos-anywhere = pkgs.callPackage ./. { };
default = config.packages.nixos-anywhere;
};
devShells.default = config.packages.nixos-anywhere.devShell;
};
}

View file

@ -0,0 +1,23 @@
#!/bin/sh
set -efu "${enableDebug:-}"
has() {
command -v "$1" >/dev/null && echo "y" || echo "n"
}
isNixos=$(if test -f /etc/os-release && grep -Eq 'ID(_LIKE)?="?nixos"?' /etc/os-release; then echo "y"; else echo "n"; fi)
cat <<FACTS
isOs=$(uname)
isArch=$(uname -m)
isKexec=$(if test -f /etc/is_kexec; then echo "y"; else echo "n"; fi)
isNixos=$isNixos
isInstaller=$(if [ "$isNixos" = "y" ] && grep -Eq 'VARIANT_ID="?installer"?' /etc/os-release; then echo "y"; else echo "n"; fi)
isContainer=$(if [ "$(has systemd-detect-virt)" = "y" ]; then systemd-detect-virt --container; else echo "none"; fi)
hasIpv6Only=$(if [ "$(has ip)" = "n" ] || ip r g 1 >/dev/null 2>/dev/null || ! ip -6 r g :: >/dev/null 2>/dev/null; then echo "n"; else echo "y"; fi)
hasTar=$(has tar)
hasCpio=$(has cpio)
hasSudo=$(has sudo)
hasDoas=$(has doas)
hasWget=$(has wget)
hasCurl=$(has curl)
hasSetsid=$(has setsid)
hasNixOSFacter=$(command -v nixos-facter >/dev/null && echo "y" || echo "n")
FACTS

View file

@ -0,0 +1,880 @@
#!/usr/bin/env bash
set -euo pipefail
here=$(dirname "${BASH_SOURCE[0]}")
flake=""
flakeAttr=""
kexecUrl=""
kexecExtraFlags=""
sshStoreSettings=""
enableDebug=""
nixBuildFlags=()
diskoAttr=""
diskoScript=""
diskoMode="disko"
diskoDeps=y
nixosSystem=""
extraFiles=""
vmTest="n"
nixOptions=(
--extra-experimental-features 'nix-command flakes'
"--no-write-lock-file"
)
SSH_PRIVATE_KEY=${SSH_PRIVATE_KEY-}
declare -A phases
phases[kexec]=1
phases[disko]=1
phases[install]=1
phases[reboot]=1
hardwareConfigBackend=none
hardwareConfigPath=
sshPrivateKeyFile=
if [ -t 0 ]; then # stdin is a tty, we allow interactive input to ssh i.e. passwords
sshTtyParam="-t"
else
sshTtyParam="-T"
fi
sshConnection=
postKexecSshPort=22
buildOnRemote=n
buildOn=auto
envPassword=n
# Facts set by get-facts.sh
isOs=
isArch=
isKexec=
isInstaller=
isContainer=
hasIpv6Only=
hasTar=
hasCpio=
hasSudo=
hasDoas=
hasWget=
hasCurl=
hasSetsid=
hasNixOSFacter=
sshKeyDir=$(mktemp -d)
trap 'rm -rf "$sshKeyDir"' EXIT
mkdir -p "$sshKeyDir"
declare -A diskEncryptionKeys=()
declare -A extraFilesOwnership=()
declare -a nixCopyOptions=()
declare -a sshArgs=()
showUsage() {
cat <<USAGE
Usage: nixos-anywhere [options] [<ssh-host>]
Options:
* -f, --flake <flake_uri>
set the flake to install the system from. i.e.
nixos-anywhere --flake .#mymachine
Also supports variants:
nixos-anywhere --flake .#nixosConfigurations.mymachine.config.virtualisation.vmVariant
* --target-host <ssh-host>
set the SSH target host to deploy onto.
* -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
* --env-password
set a password used by ssh-copy-id, the password should be set by
the environment variable SSHPASS
* -s, --store-paths <disko-script> <nixos-system>
set the store paths to the disko-script and nixos-system directly
if this is given, flake is not needed
* --kexec <path>
use another kexec tarball to bootstrap NixOS
* --kexec-extra-flags
extra flags to add into the call to kexec, e.g. "--no-sync"
* --ssh-store-setting <key> <value>
ssh store settings appended to the store URI, e.g. "compress true". <value> needs to be URI encoded.
* --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
* --extra-files <path>
contents of local <path> are recursively copied to the root (/) of the new NixOS installation. Existing files are overwritten
Copied files will be owned by root unless specified by --chown option. See documentation for details.
* --chown <path> <ownership>
change ownership of <path> recursively. Recommended to use uid:gid as opposed to username:groupname for ownership.
Option can be specified more than once.
* --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
* --show-trace
show nix build traces
* --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.
* --generate-hardware-config nixos-facter|nixos-generate-config <path>
generate a hardware-configuration.nix file using the specified backend and write it to the specified path.
The backend can be either 'nixos-facter' or 'nixos-generate-config'.
* --phases
comma separated list of phases to run. Default is: kexec,disko,install,reboot
kexec: kexec into the nixos installer
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
install: install the system
reboot: unmount the filesystems, export any ZFS pools and reboot the machine
* --disko-mode disko|mount|format
set the disko mode to format, mount or destroy. Default is disko.
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode
* --no-disko-deps
This will only upload the disko script and not the partitioning tools dependencies.
Installers usually have dependencies available.
Use this option if your target machine has not enough RAM to store the dependencies in memory.
* --build-on auto|remote|local
sets the build on settings to auto, remote or local. Default is auto.
auto: tries to figure out, if the build is possible on the local host, if not falls back gracefully to remote build
local: will build on the local host
remote: will build on the remote host
USAGE
}
abort() {
echo "aborted: $*" >&2
exit 1
}
step() {
echo "### $* ###"
}
parseArgs() {
local substituteOnDestination=y
local printBuildLogs=n
local buildOnRemote=n
while [[ $# -gt 0 ]]; do
case "$1" in
-f | --flake)
flake=$2
shift
;;
--target-host)
sshConnection=$2
shift
;;
-i)
sshPrivateKeyFile=$2
shift
;;
-p | --ssh-port)
sshArgs+=("-p" "$2")
shift
;;
--ssh-option)
sshArgs+=("-o" "$2")
shift
;;
-L | --print-build-logs)
printBuildLogs=y
;;
-s | --store-paths)
diskoScript=$(readlink -f "$2")
nixosSystem=$(readlink -f "$3")
shift
shift
;;
--generate-hardware-config)
if [[ $# -lt 3 ]]; then
abort "Missing arguments for --generate-hardware-config <backend> <path>"
fi
case "$2" in
nixos-facter | nixos-generate-config)
hardwareConfigBackend=$2
;;
*)
abort "Unknown hardware config backend: $2"
;;
esac
hardwareConfigPath=$3
shift
shift
;;
-t | --tty)
echo "the '$1' flag is deprecated, a tty is now detected automatically" >&2
;;
--help)
showUsage
exit 0
;;
--kexec)
kexecUrl=$2
shift
;;
--kexec-extra-flags)
kexecExtraFlags=$2
shift
;;
--ssh-store-setting)
key=$2
shift
value=$2
shift
sshStoreSettings+="$sshStoreSettings$key=$value&"
shift
;;
--post-kexec-ssh-port)
postKexecSshPort=$2
shift
;;
--copy-host-keys)
copyHostKeys=y
;;
--show-trace)
nixBuildFlags+=("--show-trace")
;;
--debug)
enableDebug="-x"
printBuildLogs=y
set -x
;;
--disko-mode)
case "$2" in
format | mount | disko)
diskoMode=$2
;;
*)
abort "Supported values for --disko-mode are disko, mount and format. Unknown mode : $2"
;;
esac
shift
;;
--no-disko-deps)
diskoDeps=n
;;
--build-on)
case "$2" in
auto | local | remote)
buildOn=$2
;;
*)
abort "Supported values for --build-on are auto, local and remote. Unknown mode : $2"
;;
esac
shift
;;
--extra-files)
extraFiles=$2
shift
;;
--chown)
extraFilesOwnership["$2"]="$3"
shift
shift
;;
--disk-encryption-keys)
diskEncryptionKeys["$2"]="$3"
shift
shift
;;
--phases)
phases[kexec]=0
phases[disko]=0
phases[install]=0
phases[reboot]=0
IFS=, read -r -a phaseList <<<"$2"
for phase in "${phaseList[@]}"; do
if [[ ${phases[$phase]:-unset} == unset ]]; then
abort "Unknown phase: $phase"
fi
phases[$phase]=1
done
shift
;;
--stop-after-disko)
echo "WARNING: --stop-after-disko is deprecated, use --phases kexec,disko instead" 2>&1
phases[kexec]=1
phases[disko]=1
phases[install]=0
phases[reboot]=0
;;
--no-reboot)
echo "WARNING: --no-reboot is deprecated, use --phases kexec,disko,install instead" 2>&1
phases[kexec]=1
phases[disko]=1
phases[install]=1
phases[reboot]=0
;;
--from)
nixCopyOptions+=("--from" "$2")
shift
;;
--option)
key=$2
shift
value=$2
shift
nixOptions+=("--option" "$key" "$value")
;;
--no-substitute-on-destination)
substituteOnDestination=n
;;
--build-on-remote)
echo "WARNING: --build-on-remote is deprecated, use --build-on remote instead" 2>&1
buildOnRemote=y
buildOn="remote"
;;
--env-password)
envPassword=y
;;
--vm-test)
vmTest=y
;;
*)
if [[ -z ${sshConnection} ]]; then
sshConnection="$1"
else
showUsage
exit 1
fi
;;
esac
shift
done
diskoAttr="${diskoMode}Script"
if [[ ${diskoDeps} == "n" ]]; then
diskoAttr="${diskoAttr}NoDeps"
fi
if [[ ${printBuildLogs} == "y" ]]; then
nixOptions+=("-L")
fi
if [[ $substituteOnDestination == "y" ]]; then
nixCopyOptions+=("--substitute-on-destination")
fi
if [[ $vmTest == "n" ]] && [[ -z ${sshConnection} ]]; then
abort "ssh-host must be set"
fi
if [[ $buildOn == "local" ]] && [[ $buildOnRemote == "y" ]]; then
abort "Conflicting flags: --build-on local and --build-on-remote used."
fi
if [[ -n ${flake} ]]; then
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
flake="${BASH_REMATCH[1]}"
flakeAttr="${BASH_REMATCH[2]}"
fi
if [[ -z ${flakeAttr} ]]; then
echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri." >&2
echo 'For example, to use the output nixosConfigurations.foo from the flake.nix, append "#foo" to the flake-uri.' >&2
exit 1
fi
# Support .#foo shorthand
if [[ $flakeAttr != nixosConfigurations.* ]]; then
flakeAttr="nixosConfigurations.\"$flakeAttr\".config"
fi
fi
}
# ssh wrapper
runSshNoTty() {
ssh -i "$sshKeyDir"/nixos-anywhere -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${sshArgs[@]}" "$sshConnection" "$@"
}
runSshTimeout() {
timeout 10 ssh -i "$sshKeyDir"/nixos-anywhere -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${sshArgs[@]}" "$sshConnection" "$@"
}
runSsh() {
ssh "$sshTtyParam" -i "$sshKeyDir"/nixos-anywhere -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${sshArgs[@]}" "$sshConnection" "$@"
}
nixCopy() {
NIX_SSHOPTS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $sshKeyDir/nixos-anywhere ${sshArgs[*]}" nix copy \
"${nixOptions[@]}" \
"${nixCopyOptions[@]}" \
"$@"
}
nixBuild() {
NIX_SSHOPTS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i $sshKeyDir/nixos-anywhere ${sshArgs[*]}" nix build \
--print-out-paths \
--no-link \
"${nixBuildFlags[@]}" \
"${nixOptions[@]}" \
"$@"
}
runVmTest() {
if [[ -z ${flakeAttr} ]]; then
echo "--vm-test is not supported with --store-paths" >&2
echo "Please use --flake instead or build config.system.build.installTest of your nixos configuration manually" >&2
exit 1
fi
if [[ ${buildOn} == "remote" ]]; then
echo "--vm-test is not supported with --build-on-remote" >&2
exit 1
fi
if [[ -n ${extraFiles} ]]; then
echo "--vm-test is not supported with --extra-files" >&2
exit 1
fi
if [ ${#diskEncryptionKeys[@]} -gt 0 ]; then
echo "--vm-test is not supported with --disk-encryption-keys" >&2
exit 1
fi
nix build \
--print-out-paths \
--no-link \
-L \
"${nixBuildFlags[@]}" \
"${nixOptions[@]}" \
"${flake}#${flakeAttr}.system.build.installTest"
}
uploadSshKey() {
# ssh-copy-id requires this directory
mkdir -p "$HOME/.ssh/"
if [[ -n ${sshPrivateKeyFile} ]]; then
cp "$sshPrivateKeyFile" "$sshKeyDir/nixos-anywhere"
ssh-keygen -y -f "$sshKeyDir/nixos-anywhere" >"$sshKeyDir/nixos-anywhere.pub"
else
# we generate a temporary ssh keypair that we can use during nixos-anywhere
ssh-keygen -t ed25519 -f "$sshKeyDir"/nixos-anywhere -P "" -C "nixos-anywhere" >/dev/null
fi
declare -a sshCopyIdArgs
if [[ -n ${sshPrivateKeyFile} ]]; then
unset SSH_AUTH_SOCK # don't use system agent if key was supplied
sshCopyIdArgs+=(-o "IdentityFile=${sshPrivateKeyFile}" -f)
fi
step Uploading install SSH keys
until
if [[ ${envPassword} == y ]]; then
sshpass -e \
ssh-copy-id \
-i "$sshKeyDir"/nixos-anywhere.pub \
-o ConnectTimeout=10 \
-o UserKnownHostsFile=/dev/null \
-o IdentitiesOnly=yes \
-o StrictHostKeyChecking=no \
"${sshCopyIdArgs[@]}" \
"${sshArgs[@]}" \
"$sshConnection"
else
ssh-copy-id \
-i "$sshKeyDir"/nixos-anywhere.pub \
-o ConnectTimeout=10 \
-o UserKnownHostsFile=/dev/null \
-o StrictHostKeyChecking=no \
"${sshCopyIdArgs[@]}" \
"${sshArgs[@]}" \
"$sshConnection"
fi
do
sleep 3
done
}
importFacts() {
step Gathering machine facts
local facts filteredFacts
if ! facts=$(runSsh -o ConnectTimeout=10 enableDebug=$enableDebug sh -- <"$here"/get-facts.sh); then
exit 1
fi
filteredFacts=$(echo "$facts" | grep -E '^(has|is)[A-Za-z0-9_]+=\S+')
if [[ -z $filteredFacts ]]; then
abort "Retrieving host facts via ssh failed. Check with --debug for the root cause, unless you have done so already"
fi
# make facts available in script
# shellcheck disable=SC2046
export $(echo "$filteredFacts" | xargs)
for var in isOs isArch isKexec isInstaller isContainer hasIpv6Only hasTar hasCpio hasSudo hasDoas hasWget hasCurl hasSetsid; do
if [[ -z ${!var} ]]; then
abort "Failed to retrieve fact $var from host"
fi
done
}
checkBuildLocally() {
local system extraPlatforms machineSystem
system="$(nix --extra-experimental-features 'nix-command flakes' config show system)"
extraPlatforms="$(nix --extra-experimental-features 'nix-command flakes' config show extra-platforms)"
if [[ $# -gt 0 ]]; then
machineSystem=$1
elif [[ -n ${nixosSystem} ]]; then
machineSystem="$(cat "${nixosSystem}"/system)"
else
machineSystem="$(nix --extra-experimental-features 'nix-command flakes' eval --raw "${flake}"#"${flakeAttr}".pkgs.system 2>/dev/null || echo "unknown")"
if [[ ${machineSystem} == "unknown" ]]; then
buildOn=auto
return
fi
fi
if [[ ${system} == "${machineSystem}" ]]; then
buildOn=local
return
fi
if [[ ${extraPlatforms} == "*${machineSystem}*" ]]; then
buildOn=local
return
fi
local entropy
entropy="$(date +'%Y%m%d%H%M%S')"
if nix build \
-L \
"${nixOptions[@]}" \
--expr \
"derivation { system = \"$system\"; name = \"env-$entropy\"; builder = \"/bin/sh\"; args = [ \"-c\" \"echo > \$out\" ]; }"; then
# The local build failed
buildOn=local
fi
buildOn=remote
}
generateHardwareConfig() {
local maybeSudo="$maybeSudo"
mkdir -p "$(dirname "$hardwareConfigPath")"
case "$hardwareConfigBackend" in
nixos-facter)
if [[ ${isInstaller} == "y" ]]; then
if [[ ${hasNixOSFacter} == "n" ]]; then
abort "nixos-facter is not available in booted installer, use nixos-generate-config. For nixos-facter, you may want to boot an installer image from here instead: https://github.com/nix-community/nixos-images"
fi
else
maybeSudo=""
fi
step "Generating hardware-configuration.nix using nixos-facter"
runSshNoTty -o ConnectTimeout=10 ${maybeSudo} "nixos-facter" >"$hardwareConfigPath"
;;
nixos-generate-config)
step "Generating hardware-configuration.nix using nixos-generate-config"
runSshNoTty -o ConnectTimeout=10 nixos-generate-config --show-hardware-config --no-filesystems >"$hardwareConfigPath"
;;
*)
abort "Unknown hardware config backend: $hardwareConfigBackend"
;;
esac
# to make sure nix knows about the new file
if command -v git >/dev/null; then
# handle relative paths
hardwareConfigPath="$(realpath "$hardwareConfigPath")"
pushd "$(dirname "$hardwareConfigPath")"
if git rev-parse --is-inside-work-tree >/dev/null; then
git add --intent-to-add --force -- "$hardwareConfigPath"
fi
popd
fi
}
runKexec() {
if [[ ${isKexec} == "y" ]] || [[ ${isInstaller} == "y" ]]; then
return
fi
if [[ ${isContainer} != "none" ]]; then
echo "WARNING: This script does not support running from a '${isContainer}' container. kexec will likely not work" >&2
fi
if [[ $kexecUrl == "" ]]; then
case "${isArch}" in
x86_64 | aarch64)
kexecUrl="https://github.com/nix-community/nixos-images/releases/download/nixos-24.11/nixos-kexec-installer-noninteractive-${isArch}-linux.tar.gz"
;;
*)
abort "Unsupported architecture: ${isArch}. Our default kexec images only support x86_64 and aarch64 cpus. Checkout https://github.com/nix-community/nixos-anywhere/#using-your-own-kexec-image for more information."
;;
esac
fi
step Switching system into kexec
runSsh sh <<SSH
set -efu ${enableDebug}
$maybeSudo rm -rf /root/kexec
$maybeSudo mkdir -p /root/kexec
SSH
# no way to reach global ipv4 destinations, use gh-v6.com automatically if github url
if [[ ${hasIpv6Only} == "y" ]] && [[ $kexecUrl == "https://github.com/"* ]]; then
kexecUrl=${kexecUrl/"github.com"/"gh-v6.com"}
fi
if [[ -f $kexecUrl ]]; then
runSsh "${maybeSudo} tar -C /root/kexec -xvzf-" <"$kexecUrl"
elif [[ ${hasCurl} == "y" ]]; then
runSsh "curl --fail -Ss -L '${kexecUrl}' | ${maybeSudo} tar -C /root/kexec -xvzf-"
elif [[ ${hasWget} == "y" ]]; then
runSsh "wget '${kexecUrl}' -O- | ${maybeSudo} tar -C /root/kexec -xvzf-"
else
curl --fail -Ss -L "${kexecUrl}" | runSsh "${maybeSudo} tar -C /root/kexec -xvzf-"
fi
runSsh <<SSH
TMPDIR=/root/kexec setsid ${maybeSudo} /root/kexec/kexec/run --kexec-extra-flags "${kexecExtraFlags}"
SSH
# use the default SSH port to connect at this point
for i in "${!sshArgs[@]}"; do
if [[ ${sshArgs[i]} == "-p" ]]; then
sshArgs[i + 1]=$postKexecSshPort
break
fi
done
# wait for machine to become unreachable.
while runSshTimeout -- exit 0; do sleep 1; done
# After kexec we explicitly set the user to root@
sshConnection="root@${sshHost}"
# waiting for machine to become available again
until runSsh -o ConnectTimeout=10 -- exit 0; do sleep 5; done
}
runDisko() {
local diskoScript=$1
for path in "${!diskEncryptionKeys[@]}"; do
step "Uploading ${diskEncryptionKeys[$path]} to $path"
runSsh "umask 077; mkdir -p \"$(dirname "$path")\"; cat > $path" <"${diskEncryptionKeys[$path]}"
done
if [[ -n ${diskoScript} ]]; then
nixCopy --to "ssh://$sshConnection?$sshStoreSettings" "$diskoScript"
elif [[ ${buildOn} == "remote" ]]; then
step Building disko script
# We need to do a nix copy first because nix build doesn't have --no-check-sigs
# Use ssh:// here to avoid https://github.com/NixOS/nix/issues/7359
nixCopy --to "ssh://$sshConnection?$sshStoreSettings" "${flake}#${flakeAttr}.system.build.${diskoMode}Script" \
--derivation --no-check-sigs
# If we don't use ssh-ng here, we get `error: operation 'getFSAccessor' is not supported by store`
diskoScript=$(
nixBuild "${flake}#${flakeAttr}.system.build.${diskoAttr}" \
--eval-store auto --store "ssh-ng://$sshConnection?ssh-key=$sshKeyDir%2Fnixos-anywhere&$sshStoreSettings"
)
fi
step Formatting hard drive with disko
runSsh "$diskoScript"
}
nixosInstall() {
local nixosSystem=$1
if [[ -n ${nixosSystem} ]]; then
step Uploading the system closure
nixCopy --to "ssh://$sshConnection?remote-store=local%3Froot=%2Fmnt&$sshStoreSettings" "$nixosSystem"
elif [[ ${buildOn} == "remote" ]]; then
step Building the system closure
# We need to do a nix copy first because nix build doesn't have --no-check-sigs
# Use ssh:// here to avoid https://github.com/NixOS/nix/issues/7359
nixCopy --to "ssh://$sshConnection?remote-store=local%3Froot=%2Fmnt&$sshStoreSettings" "${flake}#${flakeAttr}.system.build.toplevel" \
--derivation --no-check-sigs
# If we don't use ssh-ng here, we get `error: operation 'getFSAccessor' is not supported by store`
nixosSystem=$(
nixBuild "${flake}#${flakeAttr}.system.build.toplevel" \
--eval-store auto --store "ssh-ng://$sshConnection?ssh-key=$sshKeyDir%2Fnixos-anywhere&remote-store=local%3Froot=%2Fmnt&$sshStoreSettings"
)
fi
if [[ -n ${extraFiles} ]]; then
step Copying extra files
tar -C "$extraFiles" -cpf- . | runSsh "tar -C /mnt -xf- --no-same-owner"
runSsh "chmod 755 /mnt" # tar also changes permissions of /mnt
fi
if [[ ${#extraFilesOwnership[@]} -gt 0 ]]; then
# shellcheck disable=SC2016
printf "%s\n" "${!extraFilesOwnership[@]}" "${extraFilesOwnership[@]}" | pr -2t | runSsh 'while read file ownership; do chown -R "$ownership" "/mnt/$file"; done'
fi
step Installing NixOS
runSsh sh <<SSH
set -eu ${enableDebug}
# when running not in nixos we might miss this directory, but it's needed in the nixos chroot during installation
export PATH="\$PATH:/run/current-system/sw/bin"
# needed for installation if initrd-secrets are used
mkdir -p /mnt/tmp
chmod 777 /mnt/tmp
if [ ${copyHostKeys-n} = "y" ]; then
# NB we copy host keys that are in turn copied by kexec installer.
mkdir -m 755 -p /mnt/etc/ssh
for p in /etc/ssh/ssh_host_*; do
# Skip if the source file does not exist (i.e. glob did not match any files)
# or the destination already exists (e.g. copied with --extra-files).
if [ ! -e "\$p" ] || [ -e "/mnt/\$p" ]; then
continue
fi
cp -a "\$p" "/mnt/\$p"
done
fi
# https://stackoverflow.com/a/13864829
if [ ! -z ${NIXOS_NO_CHECK+0} ]; then
export NIXOS_NO_CHECK
fi
nixos-install --no-root-passwd --no-channel-copy --system "$nixosSystem"
if [[ ${phases[reboot]} == 1 ]]; then
if command -v zpool >/dev/null && [ "\$(zpool list)" != "no pools available" ]; then
# we always want to export the zfs pools so people can boot from it without force import
umount -Rv /mnt/
swapoff -a
zpool export -a || true
fi
nohup sh -c 'sleep 6 && reboot' >/dev/null &
fi
SSH
}
main() {
parseArgs "$@"
if [[ ${vmTest} == y ]]; then
if [[ ${hardwareConfigBackend} != "none" ]]; then
abort "--vm-test is not supported with --generate-hardware-config. You need to generate the hardware configuration before you can run the VM test." >&2
fi
runVmTest
exit 0
fi
if [[ ${buildOn} == "auto" ]]; then
checkBuildLocally
fi
# parse flake nixos-install style syntax, get the system attr
if [[ -n ${flake} ]]; then
if [[ ${buildOn} == "local" ]] && [[ ${hardwareConfigBackend} == "none" ]]; then
if [[ ${phases[disko]} == 1 ]]; then
diskoScript=$(nixBuild "${flake}#${flakeAttr}.system.build.${diskoAttr}")
fi
if [[ ${phases[install]} == 1 ]]; then
nixosSystem=$(nixBuild "${flake}#${flakeAttr}.system.build.toplevel")
fi
fi
elif [[ -n ${diskoScript} ]] && [[ -n ${nixosSystem} ]]; then
if [[ ! -e ${diskoScript} ]] || [[ ! -e ${nixosSystem} ]]; then
abort "${diskoScript} and ${nixosSystem} must be existing store-paths"
fi
else
abort "--flake or --store-paths must be set"
fi
if [[ -n ${SSH_PRIVATE_KEY} ]] && [[ -z ${sshPrivateKeyFile} ]]; then
# $sshKeyDir is getting deleted on trap EXIT
sshPrivateKeyFile="$sshKeyDir/from-env"
(
umask 077
printf '%s\n' "$SSH_PRIVATE_KEY" >"$sshPrivateKeyFile"
)
fi
sshSettings=$(ssh "${sshArgs[@]}" -G "${sshConnection}")
sshUser=$(echo "$sshSettings" | awk '/^user / { print $2 }')
sshHost=$(echo "$sshSettings" | awk '/^hostname / { print $2 }')
uploadSshKey
importFacts
if [[ ${hasTar-n} == "n" ]]; then
abort "no tar command found, but required to unpack kexec tarball"
fi
if [[ ${hasCpio-n} == "n" ]]; then
abort "no cpio command found, but required to build the new initrd"
fi
if [[ ${hasSetsid-n} == "n" ]]; then
abort "no setsid command found, but required to run the kexec script under a new session"
fi
maybeSudo=""
if [[ ${hasSudo-n} == "y" ]]; then
maybeSudo="sudo"
elif [[ ${hasDoas-n} == "y" ]]; then
maybeSudo="doas"
fi
if [[ ${isOs} != "Linux" ]]; then
abort "This script requires Linux as the operating system, but got $isOs"
fi
if [[ ${phases[kexec]} == 1 ]]; then
runKexec
fi
if [[ ${hardwareConfigBackend} != "none" ]]; then
generateHardwareConfig
fi
# Before we do not have a valid hardware configuration we don't know the machine system
if [[ ${buildOn} == "auto" ]]; then
local remoteSystem
remoteSystem=$(runSshNoTty -o ConnectTimeout=10 nix --extra-experimental-features nix-command config show system)
checkBuildLocally "${remoteSystem}"
# if we cannot figure it out at this point, we will build on the remote host
if [[ ${buildOn} == "auto" ]]; then
buildOn=remote
fi
fi
if [[ ${buildOn} != "remote" ]] && [[ -n ${flake} ]] && [[ -z ${diskoScript} ]]; then
if [[ ${phases[disko]} == 1 ]]; then
diskoScript=$(nixBuild "${flake}#${flakeAttr}.system.build.${diskoAttr}")
fi
if [[ ${phases[install]} == 1 ]]; then
nixosSystem=$(nixBuild "${flake}#${flakeAttr}.system.build.toplevel")
fi
fi
# Installation will fail if non-root user is used for installer.
# Switch to root user by copying authorized_keys.
if [[ ${isInstaller} == "y" ]] && [[ ${sshUser} != "root" ]]; then
# Allow copy to fail if authorized_keys does not exist, like if using /etc/ssh/authorized_keys.d/
runSsh "${maybeSudo} mkdir -p /root/.ssh; ${maybeSudo} cp ~/.ssh/authorized_keys /root/.ssh || true"
sshConnection="root@${sshHost}"
fi
if [[ ${phases[disko]} == 1 ]]; then
runDisko "$diskoScript"
fi
if [[ ${phases[install]} == 1 ]]; then
nixosInstall "$nixosSystem"
fi
if [[ ${phases[reboot]} == 1 ]]; then
step Waiting for the machine to become unreachable due to reboot
while runSshTimeout -- exit 0; do sleep 1; done
fi
step "Done!"
}
main "$@"

View file

@ -0,0 +1,21 @@
# NixOS-Anywhere Terraform Modules Overview
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.
Here's a brief overview of each module:
- **[All-in-One](all-in-one.md)**: This is a consolidated module that first
installs NixOS using nixos-anywhere and then keeps it updated with
nixos-rebuild. If you choose this, you won't need additional deployment tools
like colmena.
- **[Install](install.md)**: This module focuses solely on installing NixOS via
nixos-anywhere.
- **[NixOS-Rebuild](nixos-rebuild.md)**: Use this module to remotely update an
existing NixOS machine using nixos-rebuild.
- **[Nix-Build](nix-build.md)**: This is a handy helper module designed to build
a flake attribute or an attribute from a nix file.
For detailed information and usage examples, click on the respective module
links above.

View file

@ -0,0 +1,235 @@
# All-in-one
Combines the install and nixos-rebuild module in one interface to install NixOS
with nixos-anywhere and then keep it up-to-date with nixos-rebuild.
## Example
```hcl
locals {
ipv4 = "192.0.2.1"
}
module "deploy" {
source = "github.com/nix-community/nixos-anywhere//terraform/all-in-one"
# with flakes
nixos_system_attr = ".#nixosConfigurations.mymachine.config.system.build.toplevel"
nixos_partitioner_attr = ".#nixosConfigurations.mymachine.config.system.build.diskoScript"
# without flakes
# file can use (pkgs.nixos []) function from nixpkgs
#file = "${path.module}/../.."
#nixos_system_attr = "config.system.build.toplevel"
#nixos_partitioner_attr = "config.system.build.diskoScript"
target_host = local.ipv4
# when instance id changes, it will trigger a reinstall
instance_id = local.ipv4
# useful if something goes wrong
# debug_logging = true
# build the closure on the remote machine instead of locally
# build_on_remote = true
# script is below
extra_files_script = "${path.module}/decrypt-ssh-secrets.sh"
disk_encryption_key_scripts = [{
path = "/tmp/secret.key"
# script is below
script = "${path.module}/decrypt-zfs-key.sh"
}]
# Optional, arguments passed to special_args here will be available from a NixOS module in this example the `terraform` argument:
# { terraform, ... }: {
# networking.interfaces.enp0s3.ipv4.addresses = [{ address = terraform.ip; prefixLength = 24; }];
# }
# Note that this will means that your NixOS configuration will always depend on terraform!
# Skip to `Pass data persistently to the NixOS` for an alternative approach
#special_args = {
# terraform = {
# ip = "192.0.2.0"
# }
#}
}
```
_Note:_ You need to mark scripts as executable (`chmod +x`)
### ./decrypt-ssh-secrets.sh
```bash
#!/usr/bin/env bash
mkdir -p etc/ssh var/lib/secrets
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
umask 0177
sops --extract '["initrd_ssh_key"]' --decrypt "$SCRIPT_DIR/secrets.yaml" >./var/lib/secrets/initrd_ssh_key
# restore umask
umask 0022
for keyname in ssh_host_rsa_key ssh_host_rsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub; do
if [[ $keyname == *.pub ]]; then
umask 0133
else
umask 0177
fi
sops --extract '["'$keyname'"]' --decrypt "$SCRIPT_DIR/secrets.yaml" >"./etc/ssh/$keyname"
done
```
### ./decrypt-zfs-key.sh
```bash
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd "$SCRIPT_DIR"
sops --extract '["zfs-key"]' --decrypt "$SCRIPT_DIR/secrets.yaml"
```
## See also
- [nixos-wiki setup](https://github.com/NixOS/nixos-wiki-infra/blob/main/terraform/nixos-wiki/main.tf)
for hetzner-cloud
## Pass data persistently to the NixOS
This guide outlines how to pass data from Terraform to NixOS by generating a
file during Terraform execution and including it in your NixOS configuration.
This approach works well if your Terraform and NixOS configurations are stored
in the same Git repository.
### Why Use This Method?
This method provides a straightforward way to transfer values from Terraform to
NixOS without relying on special_args.
- **Advantages**:
- You can continue to use nix build or nixos-rebuild to evaluate your
configuration without interruption. Simplifies configuration management by
centralizing state in a single repository.
- **Disadvantages**:
- Deploying new machines requires tracking additional state. Every time
Terraform updates the JSON file, you'll need to commit these changes to your
repository.
### Implementation
Add the following snippet to your Terraform configuration to create and manage a
JSON file containing the necessary variables for NixOS. This file will be
automatically added to your Git repository, ensuring the data persists.
Assuming you have your terraform and nixos configuration in the same git
repository. You can use the following snippet to `git add` a file generated by
`terraform` during execution to pass data from terraform to NixOS. These changes
should be committed afterwards. This is an alternative over using
`special_args`. Advantage: you can still use nix build or nixos-rebuild on your
flake to evaluate your configuration. Disadvantage: Deploying new machines also
means you need to track additional state and make additional commits whenever
terraform updates the json file.
```hcl
locals {
nixos_vars_file = "nixos-vars.json" # Path to the JSON file containing NixOS variables
nixos_vars = {
ip = "192.0.2.0" # Replace with actual variables
}
}
resource "local_file" "nixos_vars" {
content = jsonencode(local.nixos_vars) # Converts variables to JSON
filename = local.nixos_vars_file # Specifies the output file path
file_permission = "600"
# Automatically adds the generated file to Git
provisioner "local-exec" {
interpreter = ["bash", "-c"]
command = "git add -f '${local.nixos_vars_file}'"
}
}
```
After applying the Terraform changes, ensure you commit the updated
`nixos-vars.json` file to your Git repository:
```bash
git commit -m "Update NixOS variables from Terraform"
```
You can import this json file into your configuration like this:
```nix
let
nixosVars = builtins.fromJSON (builtins.readFile ./nixos-vars.json);
in
{
# Example usage of imported variables
networking.hostName = "example-machine";
networking.interfaces.eth0.ipv4.addresses = [
{
address = nixosVars.ip; # Use the IP from nixos-vars.json
prefixLength = 24;
}
];
}
```
<!-- BEGIN_TF_DOCS -->
## Requirements
No requirements.
## Providers
No providers.
## Modules
| Name | Source | Version |
| -------------------------------------------------------------------------------------- | ---------------- | ------- |
| <a name="module_install"></a> [install](#module_install) | ../install | n/a |
| <a name="module_nixos-rebuild"></a> [nixos-rebuild](#module_nixos-rebuild) | ../nixos-rebuild | n/a |
| <a name="module_partitioner-build"></a> [partitioner-build](#module_partitioner-build) | ../nix-build | n/a |
| <a name="module_system-build"></a> [system-build](#module_system-build) | ../nix-build | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------- | :------: |
| <a name="input_debug_logging"></a> [debug\_logging](#input_debug_logging) | Enable debug logging | `bool` | `false` | no |
| <a name="input_deployment_ssh_key"></a> [deployment\_ssh\_key](#input_deployment_ssh_key) | Content of private key used to deploy to the target\_host after initial installation. To ensure maximum security, it is advisable to connect to your host using ssh-agent instead of relying on this variable | `string` | `null` | no |
| <a name="input_disk_encryption_key_scripts"></a> [disk\_encryption\_key\_scripts](#input_disk_encryption_key_scripts) | Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system | <pre>list(object({<br> path = string<br> script = string<br> }))</pre> | `[]` | no |
| <a name="input_extra_environment"></a> [extra\_environment](#input_extra_environment) | Extra environment variables to be set during installation. This can be useful to set extra variables for the extra\_files\_script or disk\_encryption\_key\_scripts | `map(string)` | `{}` | no |
| <a name="input_extra_files_script"></a> [extra\_files\_script](#input_extra_files_script) | A script that should place files in the current directory that will be copied to the targets / directory | `string` | `null` | no |
| <a name="input_file"></a> [file](#input_file) | Nix file containing the nixos\_system\_attr and nixos\_partitioner\_attr. Use this if you are not using flake | `string` | `null` | no |
| <a name="input_install_port"></a> [install\_port](#input_install_port) | SSH port used to connect to the target\_host, before installing NixOS. If null than the value of `target_port` is used | `string` | `null` | no |
| <a name="input_install_ssh_key"></a> [install\_ssh\_key](#input_install_ssh_key) | Content of private key used to connect to the target\_host during initial installation | `string` | `null` | no |
| <a name="input_install_user"></a> [install\_user](#input_install_user) | SSH user used to connect to the target\_host, before installing NixOS. If null than the value of `target_host` is used | `string` | `null` | no |
| <a name="input_instance_id"></a> [instance\_id](#input_instance_id) | The instance id of the target\_host, used to track when to reinstall the machine | `string` | `null` | no |
| <a name="input_kexec_tarball_url"></a> [kexec\_tarball\_url](#input_kexec_tarball_url) | NixOS kexec installer tarball url | `string` | `null` | no |
| <a name="input_nix_options"></a> [nix\_options](#input_nix_options) | the options of nix | `map(string)` | `{}` | no |
| <a name="input_nixos_facter_path"></a> [nixos\_facter\_path](#input_nixos_facter_path) | Path to which to write a `facter.json` generated by `nixos-facter`. | `string` | `""` | no |
| <a name="input_nixos_generate_config_path"></a> [nixos\_generate\_config\_path](#input_nixos_generate_config_path) | Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`. | `string` | `""` | no |
| <a name="input_nixos_partitioner_attr"></a> [nixos\_partitioner\_attr](#input_nixos_partitioner_attr) | Nixos partitioner and mount script i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.diskoNoDeps or just your-evaluated.config.system.build.diskNoDeps. `config.system.build.diskNoDeps` is provided by the disko nixos module | `string` | n/a | yes |
| <a name="input_nixos_system_attr"></a> [nixos\_system\_attr](#input_nixos_system_attr) | The nixos system to deploy i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.toplevel or just your-evaluated-nixos.config.system.build.toplevel if you are not using flakes | `string` | n/a | yes |
| <a name="input_no_reboot"></a> [no\_reboot](#input_no_reboot) | DEPRECATED: Use `phases` instead. Do not reboot after installation | `bool` | `false` | no |
| <a name="input_phases"></a> [phases](#input_phases) | Phases to run. See `nixos-anywhere --help` for more information | `set(string)` | <pre>[<br> "kexec",<br> "disko",<br> "install",<br> "reboot"<br>]</pre> | no |
| <a name="input_special_args"></a> [special\_args](#input_special_args) | A map exposed as NixOS's `specialArgs` thru a file. | `any` | `{}` | no |
| <a name="input_build_on_remote"></a> [build\_on\_remote](#input_build_on_remote) | Build the closure on the remote machine instead of building it locally and copying it over | `bool` | `false` | no |
| <a name="input_stop_after_disko"></a> [stop\_after\_disko](#input_stop_after_disko) | DEPRECATED: Use `phases` instead. Exit after disko formatting | `bool` | `false` | no |
| <a name="input_target_host"></a> [target\_host](#input_target_host) | DNS host to deploy to | `string` | n/a | yes |
| <a name="input_target_port"></a> [target\_port](#input_target_port) | SSH port used to connect to the target\_host after installing NixOS. If install\_port is not set than this port is also used before installing. | `number` | `22` | no |
| <a name="input_target_user"></a> [target\_user](#input_target_user) | SSH user used to connect to the target\_host after installing NixOS. If install\_user is not set than this user is also used before installing. | `string` | `"root"` | no |
## Outputs
| Name | Description |
| ----------------------------------------------------- | ----------- |
| <a name="output_result"></a> [result](#output_result) | n/a |
<!-- END_TF_DOCS -->

Some files were not shown because too many files have changed in this diff Show more