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 only 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

    The permanent MAC of a Wi-Fi card differs from its current MAC, which is often random. The permanent one matters, visible in the permaddr field.

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

Wi-Fi interfaces do not need to appear in gateway.lan.interfaces : hostapd places its interface in the bridge by itself. If you list one anyway, the framework removes it from the networkd bridge — otherwise networkd would try to attach it before hostapd has switched it to AP mode, fail in a loop, then give up managing the link.

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 to do it, since the machine is reinstalled anyway. An alias carrying the new name must then be deleted : the generator refuses a name that is both an alias and a hostname.

On the machine folder side, git mv usr/machines/<old> usr/machines/gw, then delete install/disko.nix (deleting the old disk) and keep install/state.nix. just generate recreates install/disko.nix from the disko block above.

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 by host: the authenticated user must match the first path segment. The renamed repository therefore remains inaccessible until the server republishes the new account.

Fenêtre de terminal
restic-system-main snapshots

This check is the real control: it must list the history of the old machine. An empty repository signals successful authentication but on the wrong path; do not restore anything in that case.

  1. Stop the services whose state is about 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 ownership, by name.

  5. Restart the services and verify.

ContrôleAttendu
systemctl --failedaucune unité
ls /var/lib/postgresqlune seule majeure
Comptes et dépôts applicatifsceux d’avant la migration
restic-system-main snapshotshistorique complet
Adresse sur le maillage VPNinchangée
Points d’accès Wi-Fiprésents, clients servis
Redémarrage à froidla machine remonte seule

The disko block of etc/config.yaml is only read at installation : once the migration is complete, it is frozen in install/disko.nix and can be removed. Details of the recipes in Justfile and of the configuration keys in Le fichier config.yaml.