logo
vision scalability social networking revelation

Nixos

abstract: Unfortunately Nix and Nixos has fallen to the social justice warriors who are turning it into broken rubbish.

Alternatives: Primary alternative is Guix, which has its own init system, Shepherd, and uses Scheme as its language.
I know nothing about nix. This file exists to record my learning experience.

The plan is to be able to install nix on debian, then load a reproducible configuration file so that I can instantly and reproducibly produce a host setup the way I want it.

Nixos is primarily a package manager with a declarative functional language as its package manager.

Which makes it possible to reproducibly create a setup. Unfortunately the packages are hard to customise, because access to the configuration files is restricted and non trivial – you have to create your own package.

Nixos solves the problem of dll hell by having any number of configurations living on the same machine – which leads to massive and rapid accumulation of garbage. Garbage collection is very slow, and requires either a lot of ram or a lot of swap (12GB swap recommended. This is a feature I do not want, but wind up suffering, for the advantage of reproducible setups.

To avoid bloat, can use a strategy of re-install from scratch, which Nixos makes less painful. I notice the mail server insists on pinning to a specific Nixos release.

1 Install Nixos

On linux running systemd, with SELinux disabled, with curl, as root

bash <(curl -L https://nixos.org/nix/install) --daemon

log off, then open a new terminal

nix-shell -p nix-info --run "nix-info -m"

You can open an issue at https://github.com/NixOS/nix/issues/new?labels=installer&template=installer.md

Or get in touch with the community: https://nixos.org/community

2 minimal server

This describes nixos, which I would like to use, but probably cannot be conveniently installed in a hosting service.

ssh and avahi daemon, pubkeys setup for ssh, users created.

2.1 configuration.nix

# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).

{ config, lib, pkgs, ... }:

{
  imports =
    [ # Include the results of the hardware scan.
        ./hardware-configuration.nix
    ];

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # networking.hostName = "nixos"; # Define your hostname.
  # Pick only one of the below networking options.
  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
  # networking.networkmanager.enable = true;  # Easiest to use and most distros use this by default.

  # Set your time zone.
  # time.timeZone = "Europe/Amsterdam";

  # Configure network proxy if necessary
  # networking.proxy.default = "http://user:password@proxy:port/";
  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";

  # Select internationalisation properties.
  i18n.defaultLocale = "en_US.UTF-8";
  #  console = {
  # font = "Lat2-Terminus16";
  # keyMap = "us";
  # useXkbConfig = true; # use xkb.options in tty.
  #  };

  # Enable the X11 windowing system.
  # services.xserver.enable = true;

  # Configure keymap in X11
  # services.xserver.xkb.layout = "us";
  # services.xserver.xkb.options = "eurosign:e,caps:escape";

  # Enable CUPS to print documents.
  # services.printing.enable = true;

  # Enable sound.
  # hardware.pulseaudio.enable = true;
  # OR
  # services.pipewire = {
  #   enable = true;
  #   pulse.enable = true;
  # };

  # Enable touchpad support (enabled default in most desktopManager).
  services.libinput.enable = false;

  #enable avahi-daemon
  services.avahi = {
    enable = true;
    ipv6 = true;
    ipv4 = true;
    publish = {
        enable = true;
        addresses = true;
    };
#   nssmdns4 = true;
  };

  # guest additions
  # not very useful unless desktop enabled, or maybe it just does not work at all
  #virtualisation.virtualbox.guest.enable = true;

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.cherry = {
    isNormalUser = true;
    extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
    openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAVcyLSWwsa8aN+v2PaS1wuHXGVhTdC+43B3eZ9j/C/M" ];
  # packages = with pkgs; [
  #     firefox
  #     tree
  #   ];
  };

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.root = {
    openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAVcyLSWwsa8aN+v2PaS1wuHXGVhTdC+43B3eZ9j/C/M" ];
  };

  # List packages installed in system profile. To search, run:
  # $ nix search wget
  # environment.systemPackages = with pkgs; [
  #   vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
  #   wget
  # ];

  # Some programs need SUID wrappers, can be configured further or are
  # started in user sessions.
  # programs.mtr.enable = true;
  # programs.gnupg.agent = {
  #   enable = true;
  #   enableSSHSupport = true;
  # };

  # List services that you want to enable:

  # Enable the OpenSSH daemon.
  services.openssh = {
    enable = true;
    hostKeys = [
        {
            path = "/etc/ssh/ssh_host_ed25519_key";
            rounds = 100;
            type = "ed25519";
        }
    ];
    settings = {
        PasswordAuthentication = false;
        PubkeyAuthentication = true;
        PermitRootLogin = "prohibit-password";
        UsePAM = false;
        ChallengeResponseAuthentication = false;
        Ciphers = [ "chacha20-poly1305@openssh.com" ];
        GatewayPorts = "Yes";
        KbdInteractiveAuthentication = false;
        KexAlgorithms = [ "curve25519-sha256" "curve25519-sha256@libssh.org" ];
        Macs = [ "hmac-sha2-256-etm@openssh.com" ];
    };
  };

  # Open ports in the firewall.
  # networking.firewall.allowedTCPPorts = [ ... ];
  # networking.firewall.allowedUDPPorts = [ ... ];
  # Or disable the firewall altogether.
  networking.firewall.enable = false;

  # Copy the NixOS configuration file and link it from the resulting system
  # (/run/current-system/configuration.nix). This is useful in case you
  # accidentally delete configuration.nix.
  # system.copySystemConfiguration = true;

  # This option defines the first version of NixOS you have installed on this particular machine,
  # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
  #
  # Most users should NEVER change this value after the initial install, for any reason,
  # even if you've upgraded your system to a new NixOS release.
  #
  # This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
  # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
  # to actually do that.
  #
  # This value being lower than the current NixOS release does NOT mean your system is
  # out of date, out of support, or vulnerable.
  #
  # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
  # and migrated your data accordingly.
  #
  # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
  system.stateVersion = "24.05"; # Did you read the comment?
}

2.2 hardware-configuration.nix

This should be set up automatically by the install process. For a human to do it is very difficult.

{
  imports = [ ];

  boot.initrd.availableKernelModules = [ "ata_piix" "ohci_pci" "ehci_pci" "ahci" "sd_mod" "sr_mod" ];
  boot.initrd.kernelModules = [ ];
  boot.kernelModules = [ ];
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/bf0ee7f8-0397-44d6-a3f7-462b848d0912";
      fsType = "ext4";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/B4E2-93D5";
      fsType = "vfat";
      options = [ "fmask=0077" "dmask=0077" ];
    };

  swapDevices =
    [ { device = "/dev/disk/by-uuid/2b67021b-3b31-4e2d-a521-05362ffb39f8"; }
    ];

  # 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.enp0s3.useDHCP = lib.mkDefault true;

  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
  virtualisation.virtualbox.guest.enable = true;
}

3 change config

nano /etc/nixos/conf*.nix
df -h .
nixos-rebuild test
nixos-rebuild boot
df -h .

3.1 garbage collect old configs

nix-env --delete-generations old
nix-store --gc --print-dead
nix-store --gc --print-live
nix-store --gc

4 Install nginx, mariadb, and php

Nginx setup

5 Nixos mail server

This has the huge advantage that it only needs a small computer.

Setup is also decribed as ridiculously easy – compare and contrast with much grief while setting up on debian.

And the huge disadvantage that it only exists for Nix 23.05, while the latest “stable” (not very stable at all) release is 24.05

It also has only a minimal nginx setup. Not at all sure what will happen when I combine it with a real nginx setup.

We have to pin to a particular release: The following code is an example of such pinning

{ config, pkgs, ... }: {
  imports = [
    # builtins.fetchTarbll pins Nixos to a particular release, prevents the lastest release (24.05)from being default installed.
    (builtins.fetchTarball {
      # Pick a release version you are interested in and set its hash, e.g.
      url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-23.05/nixos-mailserver-nixos-23.05.tar.gz";
      # To get the sha256 of the nixos-mailserver tarball, we can use the nix-prefetch-url command:
      # release="nixos-23.05"; nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack
      sha256 = "0000000000000000000000000000000000000000000000000000";
    })
  ];

  mailserver = {
    enable = true;
    fqdn = "mail.example.com";
    domains = [ "example.com" ];

    # A list of all login accounts. To create the password hashes, use
    # nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'
    loginAccounts = {
      "user1@example.com" = {
        hashedPasswordFile = "/a/file/containing/a/hashed/password";
        aliases = ["postmaster@example.com"];
      };
      "user2@example.com" = { ... };
    };

    # Use Let's Encrypt certificates. Note that this needs to set up a stripped
    # down nginx and opens port 80.
    certificateScheme = "acme-nginx";
  };
  security.acme.acceptTerms = true;
  security.acme.defaults.email = "security@example.com";
}

Creative Commons License reaction.la gpg key 154588427F2709CD9D7146B01C99BB982002C39F
This work is licensed under the Creative Commons Attribution 4.0 International License.