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;