NixOS for all my system
Posted in linux on November 15, 2025 by Adrian Wyssmann ‐ 4 min read
I introduced you to Nixos in my previous post. This is an amazing way of managing your linux in a declarative way with easy rollback.
For my other nodes in my home lab I use(d) Ansible and Terraform which also allows me to create reproducible environments. However, I don’t want to use different tools, so why not use Nixos everywhere.
Overall approach
As a starting point on my journey, I will install Nixos on a spare notebook manually and then apply further changes from the config I have locally on my developer machine. So what I did:
- Download latest iso image
- Burn image to an usb stick
- Boot notebook from usb stick
- Install nixos manually on the notebook –> I allowed ssh-access with password, which is important as the trusted ssh from my developer machine is not on the target host yet.
So far still lot of manual steps - especially the manual installation. There are better ways, which I will show in the next post. My focus here is currently to start managing a remote system from my dev machine.
I extend flake.nix with a new hosts - see gitlab for details
envy = nixpkgs-master.lib.nixosSystem {
specialArgs = { inherit inputs; };
inherit system;
modules = [
./configuration.nix
./hosts/envy # Include the results of the hardware scan.
inputs.sops-nix.nixosModules.sops
];
};In the subfolder hosts/envy I add all the host specific configuration for example hardware.nix, which I manually grabbed from the remote host and added it
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot = {
initrd = {
availableKernelModules = [
"xhci_pci"
"thunderbolt"
"nvme"
"usb_storage"
"usbhid"
];
kernelModules = [ ];
};
kernelModules = [ "kvm-intel" ];
kernelPackages = pkgs.linuxPackages_latest;
extraModulePackages = [ ];
};
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp0s20f0u1.useDHCP = lib.mkDefault true
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}The import of not-detected.nix comes from the manual installation, where the installer handles the hardware detection behinde the scenes and serves as a “catch-all” module for hardware configurations that nixos-generate-config couldn’t automatically map to a more specific hardware profile. After some iterations, this is my current hardware.nix
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/9501a69b-01b2-4c99-9bfe-bea99a58942c";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/232F-3A24";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
# networking.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp1s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}Once I had a valid config, could start applying the changes. I ran the following command
sudo -E nixos-rebuild switch --flake '.#envy' \
--upgrade --target-host \
[email protected] --sudoAs I allowed ssh access via username/password that worked just fine. After that my config is applied on the target host. As part of this apply, I would also close the ssh config and only allow ssk keyy authentication. So my config configurations
users.users.nixos = {
...
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOrOn3Kj/+ztMtQAaq4pVvXgTsIs1ZOqQDbsA+nJMuRM nixos@envy from clawfinger"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFTCwPNpVjW6R9vqpKgNSWgGS5hZMZcHwexAMl7E/OI2 nixos@envy from clawfinger"
];
};and
services.openssh.settings = {
PasswordAuthentication = false;
PermitRootLogin = "no";
};Secrets and SOPS
If you are using nix-sops, it’s also important to add the host key to your .sops.yaml - in my case it’s called server_envy
keys:
- &admin_papanito age12q4dwh0zqgfxfswzydr3mq7ppm5htv73aqkrfpel9ppcmml3eqds5zzzhr
- &admin_nixos age1pu3n34surq08wa0xa7xrhd4ukcah8au6pqw5mj8mgpvypw8e4d0swhf9v2
- &server_clawfinger age155ygrv7uzel70wp7tde2fp3xg9kjsht3kcu49rt3l89qw5j0tgsqsvccye
- &server_envy age1fyvzwcvfv2s3s9jr7hdpkkdc3fup65rksgeu9uahvntnrvg243fs4lm0qz
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *admin_papanito
- *server_clawfinger
- *server_envy
- path_regex: secrets/clawfinger/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *admin_papanito
- *server_clawfinger
- path_regex: secrets/envy/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *admin_papanito
- *admin_nixos
- *server_envyAfter that, where necessary - e.g. secrets/secrets.yaml and secrets/envy/secrets.yaml re-encrypt the secrets
sops updatekeys secrets/envy/secrets.yaml
sops updatekeys secrets/secrets.yaml
...Next
As a next step, I want to install nixos on a remote system without actually sitting on the remote host and doing a manual installation. Let’s see how that works.