Skip to content

Migrating a gateway's hardware

Replace a gateway computer without losing its configuration or data. The difference from a fresh install boils down to one sentence : the machine is new, the state is not.

That is where almost all the pitfalls on this page come from.

What carries over on its own, what does not

Section titled “What carries over on its own, what does not”
Repris automatiquementÀ reprendre à la main
DNS and DHCP for the zoneService data
Service registry, portal, reverse proxyBackup repository
Monitoring targets, deployment tagsVPN identity
sops secret recipientsOwners of restored files
Deployment SSH keyssystem.stateVersion

Secrets are not tied to the hostname : the sops rules file only knows the administration key and the infrastructure key. Renaming changes nothing here.

Nothing is modified at this stage : you collect what will need to be written into the configuration.

  1. Boot from the USB drive (just build-iso image), USB Wi-Fi card plugged into the port that will be its final port.

  2. Record interfaces, MAC addresses and disks

    Fenêtre de terminal
    ip -o link
    lsblk -o NAME,SIZE,MODEL,SERIAL,TYPE

    A Wi-Fi card’s permanent MAC differs from its current, often random, MAC. The permanent one is what matters, visible in the permaddr field.

  3. Physically identify which port will be the WAN and which the LAN, by unplugging and replugging. Label the sockets.

The Wi-Fi interfaces do not need to appear in gateway.lan.interfaces : hostapd places its own interface in the bridge.

etc/config.yaml
zones:
maison:
gateway:
wan:
interface: "eno0"
lan:
interfaces: ["eno1", "eno2", "eno3"]
hosts:
- hostname: "gw"
zone: "maison:1.1"
profile: "gateway"
mac: "00:00:5e:00:53:00"
disko:
profile: "luks-btrfs-1-disk"
devices:
main: "/dev/nvme0n1"

Renaming the host along the way is the right time, since the machine is being reinstalled anyway. An alias carrying the new name must then be removed : the generator refuses a name being both an alias and a hostname.

On the machine folder side, git mv usr/machines/<ancien> usr/machines/gw, then import ./generated-configuration.nix and ./disko.nix in default.nix.

Fenêtre de terminal
just install gw nixos <ip>

The command formats, installs and reboots. On an encrypted disk, the installation passphrase is requested during partitioning : note it down, just luks will need it.

Prefer the separate steps to just full-install : on an encrypted disk, waiting for the machine to come back assumes a boot without intervention, which entering the passphrase forbids.

  1. Back up, on the old gateway

    Fenêtre de terminal
    systemctl stop forgejo postgresql
    systemctl start restic-backups-system-main.service

    Do not stop the zone’s DNS resolver : the backup target is referred to by name, stopping it makes it unfindable.

  2. Rename the repository, on the backup server

    Fenêtre de terminal
    mv /mnt/backup/restic/<ancien> /mnt/backup/restic/gw

    A repository’s path contains the hostname. Renaming keeps the whole history, whereas a new name would start from an empty repository.

  3. Swap the hardware: shut down, move the data disks, identical wiring, boot and unlock the disk.

  4. Configure and deploy

    Fenêtre de terminal
    just configure gw
    just apply-verbose gw

    just configure pushes the infrastructure key : this is the step that unlocks sops, and therefore the emergency console access.

  5. Republish the backup accounts on the server that hosts them

    Fenêtre de terminal
    just apply-local

The backup server isolates repositories per host : the authenticated user must equal the path’s first segment. The renamed repository therefore stays inaccessible until the server has republished the new account.

Fenêtre de terminal
restic-system-main snapshots

This check is the real control : it must list the old machine’s history. An empty repository signals a successful authentication but against the wrong path, restore nothing in that case.

  1. Stop the services whose state is going to be replaced.

  2. Empty the state directories before restoring.

  3. Restore, one mode at a time

    Fenêtre de terminal
    restic-system-main restore latest --target / \
    --include /var/lib/forgejo --include /var/lib/postgresql
  4. Restore the owners, by name.

  5. Restart the services and check.

ContrôleAttendu
systemctl --failedno failed units
ls /var/lib/postgresqla single major version
Application accounts and repositoriesthe ones from before the migration
restic-system-main snapshotsfull history
Address on the VPN meshunchanged
Wi-Fi access pointspresent, clients served
Cold rebootthe machine comes back on its own

The disko block in etc/config.yaml is only read at install time, comment it out once the migration is complete. Recipes detail in Justfile and configuration keys in Le fichier config.yaml.