Deployment#
NGI Forge applications can be deployed to a physical or virtual machine as a complete NixOS system. The deployment consists of three steps:
Create a NixOS system configuration that contains the application.
Describe disk partitioning with disko.
Install the system on a remote machine with nixos-anywhere.
To get started, pick an application which supports the nixos or container
runtimes from the NGI Forge web UI and click the
Deploy button in the top right.
Note
Deployment requires Nix with Flakes.
Configuration#
The deployment configuration is a Nix Flake
stored in a flake.nix file. It uses NGI Forge as an
input and defines:
NixOS system configuration (
flake.nixosConfigurations.<app-name>) - the operating system of the target machine, consisting of a list of NixOS modules: disk partitioning, boot loader, remote access and the application module.Application configuration (
perSystem.forge.<app-name>) - custom configuration of the application.User access configuraion (
users.users.root.openssh.authorizedKeys.keys) public SSH key used to log in to the deployed machine.Deployment environment (
perSystem.devShells.default) - tools needed for the deployment.
Disk partitioning#
disko describes disk partitioning, formatting and mounting declaratively as a part of the NixOS configuration.
Following example configuration creates a GPT partition table on the /dev/sda
disk with three partitions and support to boot on both legacy BIOS and UEFI
machines.
Partition |
Size |
Purpose |
|---|---|---|
|
1 MB |
BIOS boot partition ( |
|
512 MB |
EFI system partition ( |
|
remaining space |
root filesystem formatted as |
1disko.devices.disk.main = {
2 type = "disk";
3 device = "/dev/sda";
4 content = {
5 type = "gpt";
6 partitions = {
7 boot = {
8 size = "1M";
9 type = "EF02";
10 };
11 ESP = {
12 size = "512M";
13 type = "EF00";
14 content = {
15 type = "filesystem";
16 format = "vfat";
17 mountpoint = "/boot";
18 mountOptions = [ "umask=0077" ];
19 };
20 };
21 root = {
22 size = "100%";
23 content = {
24 type = "filesystem";
25 format = "ext4";
26 mountpoint = "/";
27 };
28 };
29 };
30 };
31};
1boot.loader.grub = {
2 efiSupport = true;
3 efiInstallAsRemovable = true;
4};
Warning
Don’t forget to match the main device to the disk of the target machine, where
you want to deploy. To list the available disks, run lsblk and choose the
appropriate one (e.g. /dev/sda, /dev/vda or /dev/nvme0n1).
See the disko examples for other layouts, such as LVM, LUKS encryption, RAID or ZFS.
Test configuration#
Evaluate the configuration to quickly check it for errors:
nix eval .#nixosConfigurations.my-app.config.system.build.toplevel
Then test the configuration in a local virtual machine:
nix run .#nixosConfigurations.my-app.config.system.build.vm
Deployment environment#
The deployment configuration also defines a development shell
(perSystem.devShells.default) containing all tools needed for the
deployment:
nixos-anywhere - installs NixOS on the target machine
nixos-rebuild - updates the deployed system
hcloud - manages Hetzner Cloud servers (Hetzner Cloud deployment only)
Launch the deployment environment in the directory containing flake.nix:
nix develop
All commands in the following sections are expected to run in this environment.
Deployment#
Bare metal preparation#
Download the NixOS Minimal ISO image for your machine architecture from the NixOS download page.
Write the image to a USB flash drive (replace
/dev/sdXwith the USB drive device, all data on it will be erased):
sudo dd if=nixos-minimal-<version>-x86_64-linux.iso of=/dev/sdX bs=4M status=progress conv=fsync
Boot the target machine from the USB flash drive.
In the installer console, set a password for the
rootuser to enable SSH login and find out the machine IP address:
sudo passwd root
ip address
Hetzner Cloud preparation#
Create an API token in the Hetzner Cloud Console and upload your public SSH key.
Create a server running Ubuntu:
export HCLOUD_TOKEN=<api-token>
hcloud server create \
--name my-app \
--type <server-type> \
--image ubuntu-24.04 \
--ssh-key <ssh-key-name>
NixOS deployment#
Replace Ubuntu with NixOS by running the nixos-anywhere command above with the IP address of the created server.
nixos-anywhere installs
NixOS on a remote machine over SSH. The target machine must be running any
Linux system (for example Ubuntu) or a rescue system, and must be reachable as
root over SSH with your SSH key.
nixos-anywhere boots the target machine into a temporary NixOS installer (using kexec), partitions and formats the disks with disko, installs the NixOS system configuration and reboots into it.
Warning
All data on the target machine disk will be erased.
nixos-anywhere --flake .#my-app --target-host root@<ip-address>
After the deployment finishes, log in to the machine with:
ssh root@<ip-address>
Update#
To apply configuration changes to an already deployed machine, use
nixos-rebuild instead of reinstalling the system:
nixos-rebuild switch --flake .#my-app --target-host root@<ip-address>
Security hardening#
The default deployment configuration allows root user to log in over SSH,
which is convenient for the initial installation. For a machine exposed to the
internet, it is recommended to apply the following basic hardening steps.
Create an administrator user#
Instead of logging in as root, create a regular admin user with
administrator (sudo) privileges and your public SSH key. Add the following
configuration to the Boot, remote access and firewall module in flake.nix:
1users.users.admin = {
2 isNormalUser = true;
3 extraGroups = [ "wheel" ]; # allow to use sudo
4 openssh.authorizedKeys.keys = [
5 "ssh-ed25519 AAAA... user@host"
6 ];
7};
8
9# Allow to use sudo without password (the user has no password set)
10security.sudo.wheelNeedsPassword = false;
11
12# Allow administrators to deploy system updates
13nix.settings.trusted-users = [ "@wheel" ];
Disable root login and password authentication#
Allow SSH login only with SSH keys and only for regular users. Replace the
users.users.root.openssh.authorizedKeys.keys list in flake.nix with the
following configuration:
1users.users.root.openssh.authorizedKeys.keys = [ ];
2
3services.openssh.settings = {
4 PermitRootLogin = "no";
5 PasswordAuthentication = false;
6 KbdInteractiveAuthentication = false;
7};
Block brute-force attacks#
fail2ban monitors SSH login attempts and temporarily bans IP addresses with too many failed attempts:
1services.fail2ban.enable = true;
Firewall#
The firewall is enabled by default. The deployment configuration opens only
the SSH port and the ports used by the application services
(networking.firewall.allowedTCPPorts). Don’t open any additional ports unless
they are required.
Apply the changes#
Apply the configuration changes as root user for the last time:
nixos-rebuild switch --flake .#my-app --target-host root@<ip-address>
Warning
Before closing the current SSH session, verify that you can log in as the
admin user (ssh admin@<ip-address>), otherwise you may lose access to the
machine.
From now on, log in and deploy updates as the admin user:
ssh admin@<ip-address>
nixos-rebuild switch --flake .#my-app --target-host admin@<ip-address> --sudo