aboutsummaryrefslogtreecommitdiff

Introduction

This is my NixOS configuration. It is a part of my monorepo, and this file automatically tangles to all the under the nix/ directory in my monorepo git repository. My monorepo also stores my website, as my website stores my elfeed and emacs configurations. Additionally, I want to track my emacs configuration with my Nix configuration. Having them in one repository means that my emacs configuration is pinned to my flake.

Hence, my monorepo serves a dual purpose, as do many of the files within my monorepo. They are often data files used in my configuration (i.e. emacs, elfeed, org-roam, agenda, journal, etc...) and they are webpages as well. This page is one such example of this concept.

Flake.nix

The flake is the entry point of the NixOS configuration. Here, I have a list of all the systems that I use with all the modules that they use. My NixOS configuration is heavily modularized, so that adding new configurations that add modifications is made simple.

{
  description = "Emacs centric configurations for a complete networked system";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
    nur.url = "github:nix-community/NUR";
    sops-nix.url = "github:Mic92/sops-nix";
    scripts.url = "github:ret2pop/scripts";
    wallpapers.url = "github:ret2pop/wallpapers";
    sounds.url = "github:ret2pop/sounds";
    nix-topology = {
      url = "github:oddlama/nix-topology";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    home-manager = {
      url = "github:nix-community/home-manager/release-24.11";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    disko = {
      url = "github:nix-community/disko";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    lanzaboote = {
      url = "github:nix-community/lanzaboote/v0.4.1";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    nixos-dns = {
      url = "github:Janik-Haag/nixos-dns";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, home-manager, nur, disko, lanzaboote, sops-nix, nix-topology, nixos-dns, ... }@attrs:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };
      generate = nixos-dns.utils.generate nixpkgs.legacyPackages."${system}";
      dnsConfig = {
        inherit (self) nixosConfigurations;
        extraConfig = import ./dns/default.nix;
      };
      mkConfigs = map (hostname: {
        name = "${hostname}";
        value = nixpkgs.lib.nixosSystem {
          inherit system;
          specialArgs = attrs;
          modules = if (hostname == "installer") then [
            (./. + "/systems/${hostname}/default.nix")
            { networking.hostName = "${hostname}"; }
            nix-topology.nixosModules.default
          ] else [
            nix-topology.nixosModules.default
            lanzaboote.nixosModules.lanzaboote
            disko.nixosModules.disko
            home-manager.nixosModules.home-manager
            sops-nix.nixosModules.sops
            nixos-dns.nixosModules.dns
            {
              nixpkgs.overlays = [ nur.overlays.default ];
              home-manager.extraSpecialArgs = attrs;
              networking.hostName = "${hostname}";
            }
            (./. + "/systems/${hostname}/default.nix")
          ];
        };
      });
    in {
      nixosConfigurations = builtins.listToAttrs (mkConfigs [
        "affinity"
        "continuity"
        "installer"
        "spontaneity"
      ]);

      topology."${system}" = import nix-topology {
        pkgs = import nixpkgs {
          inherit system;
          overlays = [ nix-topology.overlays.default ];
        };
        modules = [
          ./topology/default.nix
          { nixosConfigurations = self.nixosConfigurations; }
        ];
      };

      devShell."${system}" = with pkgs; mkShell {
        buildInputs = [
          fira-code
          python3
          poetry
        ];
        shellHook = ''
poetry shell
        '';
      };

      packages."${system}" = {
        zoneFiles = generate.zoneFiles dnsConfig;
        octodns = generate.octodnsConfig {
          inherit dnsConfig;

          config = {
            providers = {
              cloudflare = {
                class = "octodns_cloudflare.CloudflareProvider";
                token = "env/CLOUDFLARE_TOKEN";
              };
              config = {
                check_origin = false;
              };
            };
          };
          zones = {
            "ret2pop.net." = nixos-dns.utils.octodns.generateZoneAttrs [ "cloudflare" ];
          };
        };
      };
    };
}

Note that the configurations are automatically generated with he mkConfigs function.

Sops Configuration

In order to use the sops configuration, you must change the age public key to the one that you own:

keys:
  - &primary age165ul43e8rc0qwzz2f2q9cw02psm2mkudsrwavq2e0pxs280p64yqy2z0dr
creation_rules:
  - path_regex: secrets/secrets.yaml$
    key_groups:
      - age:
        - *primary

also note that you will have to write your own secrets.yaml file, with an entry called mail, which is used for the imaps and smtps password.

Nix DNS

{
  defaultTTL = 120;
}

Nix Topology

Nix Topology generates a nice graph of all my hosts. You can view this graph by running nix build .#topology.x86_64-linux.config.output.

{ config, ... }:
let
  inherit
    (config.lib.topology);
in
{
  nodes = {
    spontaneity = {
      interfaces.wan.network = "remote";
    };
    installer = {
      interfaces.lan.network = "home";
    };
    affinity = {
      interfaces.lan = {
        network = "home";
        physicalConnections = [
          {
            node = "spontaneity";
            interface = "wan";
          }
          {
            node = "installer";
            interface = "lan";
          }
        ];
      };
    };
    continuity = {
      interfaces.lan = {
        network = "home";
        physicalConnections = [
          {
            node = "spontaneity";
            interface = "wan";
          }
          {
            node = "affinity";
            interface = "lan";
          }
        ];
      };
    };
  };
  networks = {
    home = {
      name = "Home Network";
      cidrv4 = "192.168.1.1/24";
    };
    remote = {
      name = "Remote Network";
      cidrv4 = "144.202.27.169/32";
    };
  };
}

Modules

Vars

Variables used for regular configuration in your system defafult.nix file. The options are largely self-documenting.

{ lib, ... }:
{
  options.monorepo.vars = {
    userName = lib.mkOption {
      type = lib.types.str;
      default = "preston";
      example = "myUser";
      description = "system username";
    };

    fullName = lib.mkOption {
      type = lib.types.str;
      default = "Preston Pan";
      example = "John Doe";
      description = "Full Name";
    };

    gpgKey = lib.mkOption {
      type = lib.types.str;
      default = "AEC273BF75B6F54D81343A1AC1FE6CED393AE6C1";
      example = "1234567890ABCDEF...";
      description = "GPG key fingerprint";
    };

    remoteHost = lib.mkOption {
      type = lib.types.str;
      default = "ret2pop.net";
      example = "example.com";
      description = "Address to push to and pull from for website and git repos";
    };

    timeZone = lib.mkOption {
      type = lib.types.str;
      default = "America/Vancouver";
      example = "America/Chicago";
      description = "Linux timezone";
    };

    monitors = lib.mkOption {
      type = lib.types.listOf lib.types.str;
      default = [
        "HDMI-A-1"
        "eDP-1"
        "DP-2"
        "DP-3"
        "DP-4"
        "LVDS-1"
      ];
      example = [];
      description = "Monitors that waybar will use";
    };
  };
}

Default Profile

Again, these are self documenting variables that you may see used below. These are to be used under default.nix in the systems folder.

{ lib, config, pkgs, ... }:
{
  imports = [
    ./configuration.nix
    ./vars.nix
  ];

  options = {
    monorepo = {
      profiles = {
          cuda.enable = lib.mkEnableOption "Enables CUDA support";
          documentation.enable = lib.mkEnableOption "Enables documentation on system.";
          secureBoot.enable = lib.mkEnableOption "Enables secure boot. See sbctl.";
          pipewire.enable = lib.mkEnableOption "Enables pipewire low latency audio setup";
          tor.enable = lib.mkEnableOption "Enables tor along with torsocks";
          home.enable = lib.mkEnableOption "Enables home user";
          server.enable = lib.mkEnableOption "Enables server services";
        ttyonly.enable = lib.mkEnableOption "TTY only, no xserver";
        grub.enable = lib.mkEnableOption "Enables grub instead of systemd-boot";
        workstation.enable = lib.mkEnableOption "Enables workstation services";
      };
    };
  };

  config = {
    environment.systemPackages = lib.mkIf config.monorepo.profiles.documentation.enable (with pkgs; [
      linux-manual
      man-pages
      man-pages-posix
    ]);
    boot.loader.grub = lib.mkIf config.monorepo.profiles.grub.enable {
      enable = true;
    };

    monorepo = {
      profiles = {
          documentation.enable = lib.mkDefault true;
          pipewire.enable = lib.mkDefault true;
          tor.enable = lib.mkDefault true;
          home.enable = lib.mkDefault true;
      };
    };
  };
}

X11

My Xorg configuration is used as a backup for when wayland applications don\'t work. Note that using this configuration is extremely inefficient and my i3 configuration is unoptimized. Still, it is suitable for using Krita.

{ lib, config, pkgs, ... }:
{
  services.xserver = {
    enable = lib.mkDefault true;
    displayManager = {
      startx.enable = true;
    };

    windowManager = {
      i3 = {
        enable = ! config.monorepo.profiles.ttyonly.enable;
        package = pkgs.i3-gaps;
      };
    };

    desktopManager = {
      runXdgAutostartIfNone = true;
    };

    xkb = {
      layout = "us";
      variant = "";
      options = "caps:escape";
    };

    videoDrivers = (if config.monorepo.profiles.cuda.enable then [ "nvidia" ] else []);
  };
}

You should add your own video drivers in a custom machine configuration.

Pipewire

My low latency pipewire configuration is used for music production, as well as for regular desktop usage. Pipewire is much better than pulseaudio because it supports jack with the same underlying interface and it breaks significantly less often.

{ lib, config, ... }:
{
  services.pipewire = {
    enable = lib.mkDefault config.monorepo.profiles.pipewire.enable;
    alsa = {
      enable = true;
      support32Bit = true;
    };
    pulse.enable = true;
    jack.enable = true;
    wireplumber.enable = true;
    extraConfig.pipewire-pulse."92-low-latency" = {
      "context.properties" = [
        {
          name = "libpipewire-module-protocol-pulse";
          args = { };
        }
      ];
      "pulse.properties" = {
        "pulse.min.req" = "32/48000";
        "pulse.default.req" = "32/48000";
        "pulse.max.req" = "32/48000";
        "pulse.min.quantum" = "32/48000";
        "pulse.max.quantum" = "32/48000";
      };
      "stream.properties" = {
        "node.latency" = "32/48000";
        "resample.quality" = 1;
      };
    };
  };
}

SSH

My SSH daemon configuration.

{ config, lib, ... }:
{
  services.openssh = {
    enable = true;
    settings = {
      PasswordAuthentication = lib.mkDefault (! config.monorepo.profiles.server.enable);
      AllowUsers = [ config.monorepo.vars.userName "root" "git" ];
      PermitRootLogin = "yes";
      KbdInteractiveAuthentication = false;
    };
  };
}

Tor

This is my tor configuration, used for my cryptocurrency wallets and whatever else I want it to do.

{ config, lib, ... }:
{
  services.tor = {
    enable = lib.mkDefault config.monorepo.profiles.tor.enable;
    openFirewall = true;
    client = {
      enable = lib.mkDefault config.monorepo.profiles.tor.enable;
      socksListenAddress = {
        IsolateDestAddr = true;
        addr = "127.0.0.1";
        port = 9050;
      };
      dns.enable = true;
    };
    torsocks = {
      enable = lib.mkDefault config.monorepo.profiles.tor.enable;
      server = "127.0.0.1:9050";
    };
  };
}

Kubo IPFS

I use IPFS for my website and also for my ISOs for truly declarative and deterministic configuration. NixOS might be moving to IPFS for binary cache distribution and package distribution soon, and I\'m waiting on that.

{ config, pkgs, ... }:
{
  services.kubo = {
    enable = true;
  };
}

i2pd

I use i2p for some p2p connections. We enable it with the server profile:

{ config, lib, ... }:
{
  services.i2pd = {
    enable = lib.mkDefault config.monorepo.profiles.server.enable;
    address = "0.0.0.0";
    inTunnels = {
    };
    outTunnels = {
    };
  };
}

Ollama

Use ollama for serving large language models to my other computers.

{ config, lib, ... }:
{
  services.ollama = {
    enable = lib.mkDefault config.monorepo.profiles.workstation.enable;
    acceleration = "cuda";
    host = "0.0.0.0";
  };
}

Dovecot

My server sets up dovecot in order to use imaps.

{ config, lib, ... }:
{
  services.dovecot2 = {
    enable = lib.mkDefault config.monorepo.profiles.server.enable;
    enableImap = true;
    enablePop3 = true;
  };
}

Postfix

Use postfix as an smtps server.

{ config, lib, ... }:
{
  services.postfix = {
    enable = lib.mkDefault config.monorepo.profiles.server.enable;
    config = {
    };
  };
}

Git Server

{ config, lib, ... }:
{
  services.gitDaemon = {
    enable = lib.mkDefault config.monorepo.profiles.server.enable;
    exportAll = true;
    basePath = "/srv/git";
  };
}

Nginx

{ config, lib, services, ... }:
{
  services.nginx = {
    enable = lib.mkDefault config.monorepo.profiles.server.enable;
    user = "nginx";
    # Use recommended settings
    recommendedGzipSettings = true;
    recommendedOptimisation = true;
    recommendedProxySettings = true;
    recommendedTlsSettings = true;

    # Only allow PFS-enabled ciphers with AES256
    # sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL";

    appendHttpConfig = '''';

    gitweb = {
      enable = true;
      virtualHost = "${config.monorepo.vars.remoteHost}";
    };

    virtualHosts = {
      "matrix.${config.monorepo.vars.remoteHost}" = {
        enableACME = true;
        forceSSL = true;
        listen = [
          {
            addr = "0.0.0.0";
            port = 443;
            ssl = true;
          }
          {
            addr = "[::]";
            port = 443;
            ssl = true;
          }          {
            addr = "0.0.0.0";
            port = 8448;
            ssl = true;
          }
          {
            addr = "[::]";
            port = 8448;
            ssl = true;
          }
        ];
        locations."/_matrix/" = {
          proxyPass = "http://127.0.0.1:6167";
          extraConfig = ''
            proxy_set_header Host $host;
            proxy_buffers 32 16k;
            proxy_read_timeout 5m;
          '';
        };

        extraConfig = ''
          merge_slashes off;
        '';
      };
      "${config.monorepo.vars.remoteHost}" = {
        serverName = "${config.monorepo.vars.remoteHost}";
        root = "/var/www/ret2pop-website/";
        addSSL = true;
        enableACME = true;
      };
    };
  };
}

Git Web Interface

{ lib, config, ... }:
{
  services.gitweb = {
    gitwebTheme = true;
    projectroot = "/srv/git/";
  };
}

Conduit

{ config, lib, ... }:
{
  services.matrix-conduit = {
    enable = lib.mkDefault config.monorepo.profiles.server.enable;