Featured image of post Remote deployments on NixOs

Remote deployments on NixOs

A quick and dirty guide to get started with building systems to remote instances

With the capabilities of Nix & Nixos, we can tailor-make services on our local computer, build the system, and then transmit it to a remote server using the --target-host argument for nixos-rebuild command. This is an efficient method of deploying services to distant servers because you don’t have to connect to the machine via SSH and set up the files locally before building them there.

# How remote deployments work

NixOS allows for a seamless build process, where you can create the system on your local computer and then use SSH to transfer the configuration files to a remote machine where the services are deployed.
This process is easy to manage and streamline. Ez to learn!

# Getting started

First you will need a machine running NixOs. Duh. But also you will need to have root access to it in some way.
If you have the root user and its password then you are set to go and can skip to the section where I explain how to get ready for remote deployment

# Setting up NixOs on a server.

# Installing

I assume if you are reading this article you know how to install an operating system.

You will need to flash an usb drive with NixOs, for the sake of an easy install I will use latest-nixos-plasma5-x86_64-linux.iso since It comes with a fearly easy to use Calamares installer.

Installer You can go ahead and click through it and it will install.

# First boot

For remote deployment to work you will need to enable ssh and configure some parameters for it to work. We are enabling root login and also sftp for file transfer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# configuration.nix
  services.openssh = {
    enable = true;
    allowSFTP = true;
    settings = {
      PasswordAuthentication = true;
      PermitRootLogin = "yes";
    };
  };
  networking.hostName = "server1";
  networking.domain = "localhost";

Rebuild the system

1
sudo nixos-rebuild switch

# Setting up key auth

To facilitate easy deployments, you can transfer your public SSH key to the remote machine, allowing you to log in without having to enter the password for each rebuild. This method is both more convenient and safe

1
ssh-copy-id root@server

# Create a directory for the remote

On your local machine, it’s important to organize your files into a directory, particularly when working with multiple servers. This is demonstrated below, where I showcase my personal file organization method.

      • configuration.nix
      • flake.nix
      • flake.lock
      • configuration.nix
        • matrix.nix
        • webserver.nix
      • flake.nix
      • flake.lock
  • # Copying essential files from remote

    NixOS does not support partial builds, so you will need to transfer all the necessary files from /etc/nixos to your local machine. This includes files such as hardware-configuration.nix and configuration.nix.

    1
    
    scp root@server:/etc/nixos/configuration.nix configuration.nix
    
    1
    
    scp root@server:/etc/nixos/hardware-configuration.nix hardware-configuration.nix
    

    # Create flakes

    You can create a file called flake.nix or use nix to do so.

    1
    
    nix flake init .
    

    Paste the following in the file.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    # flake.nix
    {
      description = "Server1 deployments";
    
      inputs = {
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
      };
    
      outputs =
        { self
        , nixpkgs
        , ...
        }:
        let
          system = "x86_64-linux";
        in
        {
          nixosConfigurations.server1 = nixpkgs.lib.nixosSystem {
            inherit system;
            modules = [
              ./configuration.nix
            ];
          };
        };
    }
    

    Here is an example of how your configuration.nix should look like.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    # configuration.nix
    
    { config, pkgs, ... }:
    
    {
      imports =
        [
          ./hardware-configuration.nix
        ];
    
      networking.hostName = "server1";
      networking.domain = "example.com";
    
      services.openssh = {
          enable = true;
          allowSFTP = true;
          settings = {
            PasswordAuthentication = true;
            PermitRootLogin = "yes";
          };
        };
    
    }
    

    # Set up a service

    This little example shows a dummy nginx service.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    # nginx.nix
    services.nginx = {
        enable = true;
        virtualHosts = {
          "example.com" = {
            locations."/" = {
                # ...
            };
          };
        };
    }
    

    Insert it in the configuration.nix imports section.

    1
    2
    3
    4
    
    imports = [
            ./hardware-configuration.nix
            ./nginx.nix
        ];
    

    # Deploy!

    After all this we can just go ahead and execute a rebuild like we would do on our local machine. Except in this case we have to add the --flake to the rebuild command and also add the hostname we are building for.

    1
    
    nixos-rebuild switch --flake .#server1 --target-host root@server --show-trace
    

    # Service management

    Since NixOs uses systemd we can utilize its tools such as journalctl or systemcl to check up on how our services are doing. Here are a few commands I recommend using

    Prints the last 100 logs of nginx

    1
    
    journalctl -u nginx.service -n 200
    

    Displays the status of the service

    1
    
    systemctl status nginx.service
    

    # Afterthoughts

    The first-boot section could be skipped if you create a custom nixos installation media then flash that to the server. With a custom media you can define ssh to already have these options enabled and also can add your public key. This is how I’ve been doing my deployments for the past 1 month for the 4 of my servers. It’s much easier than my old-school method of ssh-ing into my alpine machines and manage my deployments with docker-compose