← Blog

Moving from channels to flakes

I’ve been running my homelab on NixOS for a few months now. The migration from debian enables me to have a single configuration that involves only doing the following steps to replicate the single machine setup in a new environment

  1. Rotate the sops-nix backed secrets.
  2. Setup ssh to the VPN provider, cloud backup provider.
  3. Clone the backed-up data directory to local. I still use restic for backups.
  4. Applying the single-machine configuration, I get an exact replica of the machine that I’m migrating from. (Nope!)

The default way Nix runs is through Channels. A NixOS system subscribes to a channel, for example a 25.05 release isn’t fixed. The security/minor version patches are continuously sent to this channel and the versions of dependencies of your environment are not exactly pinned. So when you move to a new system and try to replicate your deterministic environment, your backed-up data might not be compatible with the operating system you just installed.

Running these commands will catch your system up to the latest nixpkgs (the source of your dependency tree) in an existing system.

sudo nixos-rebuild switch --upgrade

Without doing this, if you migrate to a new system it automatically picks up the latest commit in 25.05 even though you might be in an older commit of this channel in your old environment, and thus the same Nix config produces different environments.

Flakes are experimental and as you already guessed, an alternative to the default way of writing Nix configuration (both for the NixOS, and “Nix” the build system).

But unlike channels, they lock the “nixpkgs” repository to a specific commit alongside the system configuration in your resulting system, and with it if you run the same NixOS configuration in a different machine, you will have a deterministic, and reproduced environment. Although, you must update the nixpkgs source in a timely manner, to receive security patches. But by using flakes, you own this responsibility instead of relying on NixOS Channels.

Practically you do the following steps

  1. nixos-version --revision prints the the running system’s revision. To keep the individual packages stable, the new flake based system MUST use this revision.

  2. The flake alongside your configuration.nix looks like this. It only acts as a tiny wrapper that propagates nixpkgs but it could look more complex eventually.

    {
      description = "NixOS configuration (migrated from channels)";
    
      inputs = {
        nixpkgs.url = "github:NixOS/nixpkgs/<REVISION>";
      };
    
      outputs = { self, nixpkgs, ... }: {
        nixosConfigurations.newmachina = nixpkgs.lib.nixosSystem {
          system = "x86_64-linux";
          modules = [
            ./configuration.nix
          ];
        };
      };
    }
    
  3. nixos-rebuild build --flake .#newmachina outputs a ./result that’s the closure of the new version of your machine. One can run nix store diff-closures /run/current-system ./result to ensure that the new closure only has the following diffs. This guarantees that none of your working software would fail when you rebuild.

    1. The updated version from your local channel-provided nixpkgs to a pinned nixpkgs:<REVISION>.
    2. The source code of the newly checked-in nixpkgs:<REVISION>.

I would also recommend practicing this exercise in a “staging” system first before changing your actual homeserver. That’s it, do a sudo nixos-rebuild switch --flake .#newmachina and your flake-based NixOS is working independent of channels.

And, as a result if you apply this configuration as mentioned in Step 4 during the start of this document, you’ll have a bit for bit replica of your old system’s build.