Our starting point here is a node that is freshly infected with NixOS. For changing the system, we have 2 options.

  1. SSH in and edit the configuration files (or copy them over) and then run nixos-rebuild switch which triggers the node to pull and build all the necessary items.

  2. Check and build the configuration locally, and copy the entire closure across.

At a glance there might not be an advantage to one or the other. But consider if you’re deploying custom content that isn’t part of nixOS builtin configuration. To use method (1) you would need to clone all your repos on the server to be able to rebuild. Then every update requires you to ssh in and pull.

That’s why (2) is the much better option. We will use deploy-rs to implement it on DigitalOcean.

1) Enable flakes on the server

If you followed the previous instructions you’ve got a local folder full of configuration pulled from your node. Go ahead and turn that into a git repo before getting too far into this.

Adjust your configuration.nix

{ pkgs, ... }: {
  imports = [
    ./hardware-configuration.nix
    ./networking.nix
  ];

  environment.systemPackages = with pkgs; [ vim ];

  boot.tmp.cleanOnBoot = true;
  zramSwap.enable = true;
  networking.hostName = "your-hostname";
  networking.domain = "";
  services.openssh.enable = true;
  users.users.root.openssh.authorizedKeys.keys = [''your key here'' ];
  system.stateVersion = "25.05";

  nix.settings.experimental-features = [ "nix-command" "flakes" ];
}

The main changes are to add the content of host.nix (personal preference but you can go ahead and delete that), and enable the flakes feature. Push the whole configuration folder back up and do a rebuild:

rsync -av --delete --exclude='.git' ./ [node-name]:/etc/nixos/
ssh [node-name] "sudo nixos-rebuild switch --flake /etc/nixos#default"

2) Wrap the original configuration in a flake

Now, working locally again, create a new flake.nix:

{
  description = "NixOS configuration";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
    deploy-rs = {
      url = "github:serokell/deploy-rs";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, deploy-rs }: let
    system = "x86_64-linux";
  in {
    nixosConfigurations.default = nixpkgs.lib.nixosSystem {
      inherit system;
      modules = [
        ./configuration.nix
      ];
    };

    deploy.nodes.default = {
      hostname = "[node-name]";
      profiles.system = {
        sshUser = "root";
        path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.default;
        user = "root";
      };
    };
    checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
  };
}

This flake:

  1. wraps the original system, importing it to nixosConfigurations.default.
  2. describes the deployment target in deploy.nodes.default.

3) Deploy it

You can run deploy-rs straight from github:

nix run github:serokell/deploy-rs .#default

or hit a nix profile install github:serokell/deploy-rs so that you can just use the deploy command.

This should succeed and leave your server right where you started (since the actual configuration is unchanged), but now you have a nice flake to work with locally.