about summary refs log tree commit diff
path: root/modules/nixos
diff options
context:
space:
mode:
authorAzat Bahawi <azat@bahawi.net>2022-12-17 16:39:09 +0300
committerAzat Bahawi <azat@bahawi.net>2022-12-17 16:39:09 +0300
commit8f137c28230623259a964484adcf31fe00756594 (patch)
tree82bce6a13fda125087cf6d9dc80aa91d9230d6c4 /modules/nixos
parent2022-11-20 (diff)
2022-12-17
Diffstat (limited to 'modules/nixos')
-rw-r--r--modules/nixos/acme.nix32
-rw-r--r--modules/nixos/alertmanager.nix63
-rw-r--r--modules/nixos/android.nix18
-rw-r--r--modules/nixos/bluetooth.nix28
-rw-r--r--modules/nixos/common/console.nix6
-rw-r--r--modules/nixos/common/default.nix19
-rw-r--r--modules/nixos/common/documentation.nix31
-rw-r--r--modules/nixos/common/home-manager.nix3
-rw-r--r--modules/nixos/common/kernel.nix39
-rw-r--r--modules/nixos/common/locale.nix24
-rw-r--r--modules/nixos/common/networking.nix108
-rw-r--r--modules/nixos/common/nix.nix39
-rw-r--r--modules/nixos/common/secrets.nix45
-rw-r--r--modules/nixos/common/security.nix29
-rw-r--r--modules/nixos/common/services.nix10
-rw-r--r--modules/nixos/common/shell.nix3
-rw-r--r--modules/nixos/common/systemd.nix22
-rw-r--r--modules/nixos/common/tmp.nix18
-rw-r--r--modules/nixos/common/users.nix19
-rw-r--r--modules/nixos/common/xdg.nix87
-rw-r--r--modules/nixos/default.nix59
-rw-r--r--modules/nixos/discord.nix22
-rw-r--r--modules/nixos/docker.nix41
-rw-r--r--modules/nixos/dwm.nix159
-rw-r--r--modules/nixos/emacs.nix30
-rw-r--r--modules/nixos/endlessh-go.nix30
-rw-r--r--modules/nixos/endlessh.nix24
-rw-r--r--modules/nixos/fail2ban.nix32
-rw-r--r--modules/nixos/fonts.nix45
-rw-r--r--modules/nixos/games/default.nix38
-rw-r--r--modules/nixos/games/gamemode.nix13
-rw-r--r--modules/nixos/games/gog.nix18
-rw-r--r--modules/nixos/games/lutris.nix32
-rw-r--r--modules/nixos/games/mangohud.nix26
-rw-r--r--modules/nixos/games/minecraft.nix50
-rw-r--r--modules/nixos/games/steam-run.nix73
-rw-r--r--modules/nixos/games/steam.nix25
-rw-r--r--modules/nixos/git.nix117
-rw-r--r--modules/nixos/gnupg.nix38
-rw-r--r--modules/nixos/gotify.nix75
-rw-r--r--modules/nixos/grafana.nix119
-rw-r--r--modules/nixos/hydra.nix57
-rw-r--r--modules/nixos/ipfs.nix167
-rw-r--r--modules/nixos/kde.nix49
-rw-r--r--modules/nixos/libvirtd.nix44
-rw-r--r--modules/nixos/lidarr.nix28
-rw-r--r--modules/nixos/loki.nix102
-rw-r--r--modules/nixos/lxc.nix16
-rw-r--r--modules/nixos/matrix/default.nix1
-rw-r--r--modules/nixos/matrix/dendrite.nix157
-rw-r--r--modules/nixos/matrix/element.nix59
-rw-r--r--modules/nixos/matrix/synapse.nix93
-rw-r--r--modules/nixos/monitoring/dashboards/endlessh.json1457
-rw-r--r--modules/nixos/monitoring/dashboards/nginx.json567
-rw-r--r--modules/nixos/monitoring/dashboards/postgresql.json3086
-rw-r--r--modules/nixos/monitoring/dashboards/unbound.json2991
-rw-r--r--modules/nixos/monitoring/default.nix176
-rw-r--r--modules/nixos/nextcloud.nix133
-rw-r--r--modules/nixos/nginx.nix99
-rw-r--r--modules/nixos/node-exporter.nix34
-rw-r--r--modules/nixos/nsd.nix174
-rw-r--r--modules/nixos/openssh.nix34
-rw-r--r--modules/nixos/podman.nix41
-rw-r--r--modules/nixos/postgresql.nix87
-rw-r--r--modules/nixos/profiles/default.nix33
-rw-r--r--modules/nixos/profiles/dev/containers.nix27
-rw-r--r--modules/nixos/profiles/dev/default.nix19
-rw-r--r--modules/nixos/profiles/headful.nix88
-rw-r--r--modules/nixos/profiles/headless.nix42
-rw-r--r--modules/nixos/prometheus.nix49
-rw-r--r--modules/nixos/promtail.nix53
-rw-r--r--modules/nixos/psd.nix60
-rw-r--r--modules/nixos/radarr.nix28
-rw-r--r--modules/nixos/radicale.nix52
-rw-r--r--modules/nixos/rss-bridge.nix31
-rw-r--r--modules/nixos/rtorrent.nix297
-rw-r--r--modules/nixos/searx.nix78
-rw-r--r--modules/nixos/shadowsocks.nix116
-rw-r--r--modules/nixos/soju.nix117
-rw-r--r--modules/nixos/solaar.nix54
-rw-r--r--modules/nixos/sonarr.nix28
-rw-r--r--modules/nixos/sound.nix21
-rw-r--r--modules/nixos/syncthing.nix145
-rw-r--r--modules/nixos/throttled.nix119
-rw-r--r--modules/nixos/unbound.nix197
-rw-r--r--modules/nixos/vaultwarden.nix134
-rw-r--r--modules/nixos/wireguard.nix198
-rw-r--r--modules/nixos/x11.nix92
-rw-r--r--modules/nixos/xmonad.nix28
89 files changed, 13467 insertions, 0 deletions
diff --git a/modules/nixos/acme.nix b/modules/nixos/acme.nix
new file mode 100644
index 0000000..d3ad661
--- /dev/null
+++ b/modules/nixos/acme.nix
@@ -0,0 +1,32 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.acme;
+in {
+  imports = [
+    (mkAliasOptionModule ["certs"] ["security" "acme" "certs"])
+  ];
+
+  options.nixfiles.modules.acme = {
+    enable = mkEnableOption "ACME";
+
+    email = mkOption {
+      description = "Email for notifications.";
+      type = with types; str;
+      default = "admin+acme@${my.domain.shire}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.acme = {
+      acceptTerms = true;
+      defaults = {
+        inherit (cfg) email;
+        validMinDays = 60;
+      };
+    };
+  };
+}
diff --git a/modules/nixos/alertmanager.nix b/modules/nixos/alertmanager.nix
new file mode 100644
index 0000000..871b0c4
--- /dev/null
+++ b/modules/nixos/alertmanager.nix
@@ -0,0 +1,63 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.alertmanager;
+in {
+  options.nixfiles.modules.alertmanager = {
+    enable = mkEnableOption "Alertmanager";
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 30112;
+    };
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; nullOr str;
+      default = "alertmanager.${config.networking.domain}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.nginx = with cfg; {
+      enable = true;
+      upstreams.alertmanager.servers."127.0.0.1:${toString cfg.port}" = {};
+      virtualHosts.${cfg.domain} = {
+        locations."/".proxyPass = "http://alertmanager";
+        extraConfig = nginxInternalOnly;
+      };
+    };
+
+    services.prometheus.alertmanager = {
+      enable = true;
+
+      listenAddress = "127.0.0.1";
+      inherit (cfg) port;
+
+      extraFlags = ["--web.external-url=https://${cfg.domain}"];
+
+      configuration = {
+        global = {
+          smtp_from = "alertmanager@${my.domain.shire}";
+          smtp_smarthost = "${my.domain.shire}:584";
+        };
+
+        route = {
+          receiver = my.username;
+          group_by = ["alertname"];
+        };
+
+        receivers = [
+          {
+            name = my.username;
+            email_configs = [{to = "${my.username}+alert@${my.domain.shire}";}];
+          }
+        ];
+      };
+    };
+  };
+}
diff --git a/modules/nixos/android.nix b/modules/nixos/android.nix
new file mode 100644
index 0000000..307490a
--- /dev/null
+++ b/modules/nixos/android.nix
@@ -0,0 +1,18 @@
+{
+  config,
+  lib,
+  inputs,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.android;
+in {
+  options.nixfiles.modules.android.enable = mkEnableOption "support for Android devices";
+
+  config = mkIf cfg.enable {
+    programs.adb.enable = true;
+
+    my.extraGroups = ["adbusers"];
+  };
+}
diff --git a/modules/nixos/bluetooth.nix b/modules/nixos/bluetooth.nix
new file mode 100644
index 0000000..8347361
--- /dev/null
+++ b/modules/nixos/bluetooth.nix
@@ -0,0 +1,28 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.bluetooth;
+in {
+  options.nixfiles.modules.bluetooth.enable =
+    mkEnableOption "Bluetooth support";
+
+  config = mkIf cfg.enable {
+    hardware.bluetooth = {
+      enable = true;
+      settings.General.FastConnectable = true;
+    };
+
+    environment = {
+      etc."bluetooth/input.conf".text = generators.toINI {} {
+        General = {
+          IdleTimeout = 15;
+          UserspaceHID = true;
+        };
+      };
+    };
+  };
+}
diff --git a/modules/nixos/common/console.nix b/modules/nixos/common/console.nix
new file mode 100644
index 0000000..3c73695
--- /dev/null
+++ b/modules/nixos/common/console.nix
@@ -0,0 +1,6 @@
+{config, ...}: {
+  console = {
+    earlySetup = true;
+    useXkbConfig = config.services.xserver.enable;
+  };
+}
diff --git a/modules/nixos/common/default.nix b/modules/nixos/common/default.nix
new file mode 100644
index 0000000..8724c8b
--- /dev/null
+++ b/modules/nixos/common/default.nix
@@ -0,0 +1,19 @@
+_: {
+  imports = [
+    ./console.nix
+    ./documentation.nix
+    ./home-manager.nix
+    ./kernel.nix
+    ./locale.nix
+    ./networking.nix
+    ./nix.nix
+    ./secrets.nix
+    ./security.nix
+    ./services.nix
+    ./shell.nix
+    ./systemd.nix
+    ./tmp.nix
+    ./users.nix
+    ./xdg.nix
+  ];
+}
diff --git a/modules/nixos/common/documentation.nix b/modules/nixos/common/documentation.nix
new file mode 100644
index 0000000..f909108
--- /dev/null
+++ b/modules/nixos/common/documentation.nix
@@ -0,0 +1,31 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; {
+  config = mkIf this.isHeadful {
+    documentation = {
+      dev.enable = true;
+      nixos.enable = true;
+
+      man.man-db.manualPages =
+        (pkgs.buildEnv {
+          name = "man-paths";
+          paths = with config;
+            environment.systemPackages ++ hm.home.packages;
+          pathsToLink = ["/share/man"];
+          extraOutputsToInstall = ["man"];
+          ignoreCollisions = true;
+        })
+        .overrideAttrs (_: _: {__contentAddressed = true;});
+    };
+
+    environment.sessionVariables = {
+      MANOPT = "--no-hyphenation";
+      MANPAGER = "${pkgs.less}/bin/less -+F";
+    };
+  };
+}
diff --git a/modules/nixos/common/home-manager.nix b/modules/nixos/common/home-manager.nix
new file mode 100644
index 0000000..52f2fd3
--- /dev/null
+++ b/modules/nixos/common/home-manager.nix
@@ -0,0 +1,3 @@
+{inputs, ...}: {
+  imports = [inputs.home-manager.nixosModule];
+}
diff --git a/modules/nixos/common/kernel.nix b/modules/nixos/common/kernel.nix
new file mode 100644
index 0000000..2fc40f9
--- /dev/null
+++ b/modules/nixos/common/kernel.nix
@@ -0,0 +1,39 @@
+{lib, ...}:
+with lib; {
+  boot = {
+    # I don't use it even on laptops. It's also /required/ to disable it for
+    # ZFS[1].
+    # [1]: https://github.com/openzfs/zfs/issues/260
+    # [1]: https://github.com/openzfs/zfs/issues/12842
+    kernelParams = ["hibernate=no"];
+
+    kernel.sysctl = {
+      "fs.file-max" = pow 2 17;
+      "fs.inotify.max_user_watches" = pow 2 19;
+      "fs.suid_dumpable" = 0;
+      "kernel.core_uses_pid" = 1;
+      "kernel.exec-shield" = 1;
+      "kernel.kptr_restrict" = 1;
+      "kernel.maps_protect" = 1;
+      "kernel.msgmax" = pow 2 16;
+      "kernel.msgmnb" = pow 2 16;
+      "kernel.pid_max" = pow 2 16;
+      "kernel.randomize_va_space" = 2;
+      "kernel.shmall" = pow 2 28;
+      "kernel.shmmax" = pow 2 28;
+      "kernel.sysrq" = 0;
+      "vm.dirty_background_bytes" = pow 2 22;
+      "vm.dirty_background_ratio" = 5;
+      "vm.dirty_bytes" = pow 2 22;
+      "vm.dirty_ratio" = 30;
+      "vm.min_free_kbytes" = pow 2 16;
+      "vm.mmap_min_addr" = pow 2 12;
+      "vm.overcommit_memory" = mkDefault 0;
+      "vm.overcommit_ratio" = mkDefault 50;
+      "vm.vfs_cache_pressure" = 50;
+    };
+  };
+
+  # https://docs.kernel.org/admin-guide/mm/ksm.html
+  hardware.ksm.enable = true;
+}
diff --git a/modules/nixos/common/locale.nix b/modules/nixos/common/locale.nix
new file mode 100644
index 0000000..62d19f4
--- /dev/null
+++ b/modules/nixos/common/locale.nix
@@ -0,0 +1,24 @@
+{lib, ...}:
+with lib; {
+  i18n = {
+    defaultLocale = mkDefault "en_GB.UTF-8";
+    supportedLocales = [
+      "C.UTF-8/UTF-8"
+      "en_GB.UTF-8/UTF-8"
+      "en_US.UTF-8/UTF-8"
+      "ja_JP.UTF-8/UTF-8"
+      "ru_RU.UTF-8/UTF-8"
+    ];
+  };
+
+  services.xserver = {
+    layout = comcat ["us" "ru"];
+    xkbVariant = comcat ["" "phonetic"];
+    xkbOptions = comcat [
+      "terminate:ctrl_alt_bksp"
+      "caps:escape"
+      "compose:menu"
+      "grp:win_space_toggle"
+    ];
+  };
+}
diff --git a/modules/nixos/common/networking.nix b/modules/nixos/common/networking.nix
new file mode 100644
index 0000000..6109933
--- /dev/null
+++ b/modules/nixos/common/networking.nix
@@ -0,0 +1,108 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; {
+  # TODO Support multiple interfaces and IP addresses.
+  networking = mkMerge [
+    {
+      domain = my.domain.shire;
+
+      hostName = this.hostname;
+      hostId = substring 0 8 (builtins.hashString "md5" this.hostname);
+
+      # Remove default hostname mappings. This is required at least by the current
+      # implementation of the montoring module.
+      hosts = {
+        "127.0.0.2" = mkForce [];
+        "::1" = mkForce [];
+      };
+
+      nameservers = mkDefault dns.const.quad9.default;
+
+      useDHCP = false;
+
+      firewall = {
+        enable = true;
+
+        rejectPackets = false;
+
+        allowPing = true;
+        pingLimit = "--limit 1/minute --limit-burst 5";
+
+        logRefusedConnections = false;
+        logRefusedPackets = false;
+        logRefusedUnicastsOnly = false;
+        logReversePathDrops = false;
+      };
+    }
+    (let
+      interface = "eth0"; # This assumes `usePredictableInterfaceNames` is false.
+    in
+      mkIf (hasAttr "ipv4" this && hasAttr "ipv6" this) {
+        usePredictableInterfaceNames = false; # NOTE This can break something!
+        interfaces.${interface} = {
+          ipv4.addresses = with this.ipv4;
+            optional (isString address && isInt prefixLength) {
+              inherit address prefixLength;
+            };
+
+          ipv6.addresses = with this.ipv6;
+            optional (isString address && isInt prefixLength) {
+              inherit address prefixLength;
+            };
+        };
+        defaultGateway = with this.ipv4;
+          mkIf (isString gatewayAddress) {
+            inherit interface;
+            address = gatewayAddress;
+          };
+        defaultGateway6 = with this.ipv6;
+          mkIf (isString gatewayAddress) {
+            inherit interface;
+            address = gatewayAddress;
+          };
+      })
+    (mkIf this.isHeadful {
+      interfaces = {
+        eth0.useDHCP = mkDefault true;
+        wlan0.useDHCP = mkDefault true;
+      };
+
+      networkmanager = {
+        enable = mkDefault true;
+        wifi.backend = "iwd";
+      };
+
+      wireless = {
+        enable = false;
+        iwd.enable = mkDefault true;
+        userControlled.enable = true;
+        allowAuxiliaryImperativeNetworks = true;
+      };
+    })
+  ];
+
+  environment.shellAliases = listToAttrs (map
+    ({
+      name,
+      value,
+    }:
+      nameValuePair name "${pkgs.iproute2}/bin/${value}") [
+      {
+        name = "bridge";
+        value = "bridge -color=always";
+      }
+      {
+        name = "ip";
+        value = "ip -color=always";
+      }
+      {
+        name = "tc";
+        value = "tc -color=always";
+      }
+    ]);
+}
diff --git a/modules/nixos/common/nix.nix b/modules/nixos/common/nix.nix
new file mode 100644
index 0000000..07136a0
--- /dev/null
+++ b/modules/nixos/common/nix.nix
@@ -0,0 +1,39 @@
+{
+  config,
+  inputs,
+  lib,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.common.nix;
+in {
+  options.nixfiles.modules.common.nix.allowedUnfreePackages = mkOption {
+    description = "A list of allowed unfree packages.";
+    type = with types; listOf str;
+    default = [];
+  };
+
+  config = {
+    nix.settings.trusted-users = ["@wheel"];
+
+    nixpkgs = {
+      config.allowUnfreePredicate = p: elem (getName p) cfg.allowedUnfreePackages;
+
+      overlays = with inputs; [
+        agenix.overlay
+        # nix-minecraft-servers.overlays.default
+        xmonad-ng.overlays.default
+      ];
+    };
+
+    system.stateVersion = with builtins;
+      head (split "\n" (readFile "${inputs.nixpkgs}/.version"));
+
+    environment = {
+      sessionVariables.NIX_SHELL_PRESERVE_PROMPT = "1";
+      localBinInPath = true;
+      defaultPackages = [];
+    };
+  };
+}
diff --git a/modules/nixos/common/secrets.nix b/modules/nixos/common/secrets.nix
new file mode 100644
index 0000000..4fcdc61
--- /dev/null
+++ b/modules/nixos/common/secrets.nix
@@ -0,0 +1,45 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; {
+  imports = [
+    inputs.agenix.nixosModule
+    (mkAliasOptionModule ["secrets"] ["age" "secrets"])
+  ];
+
+  config = {
+    age = {
+      identityPaths =
+        if this.isHeadful
+        then ["${config.my.home}/.ssh/id_${my.ssh.type}"]
+        else
+          map (attr: attr.path) (filter (attr: attr.type == my.ssh.type)
+            config.services.openssh.hostKeys);
+
+      # This can be used to auto-add all secrets, thus eleminating the need to
+      # specify path to each envrypted file. The drawback is that this will
+      # expose *all* secrets to all machines and try to decrypt them all even on
+      # machines where the secret will not be used.
+      #
+      # secrets =
+      #   let
+      #     secretsSourceDir = "${inputs.self}/age";
+      #   in
+      #   mapAttrs'
+      #     (name: _:
+      #       nameValuePair name {
+      #         file = "${secretsSourceDir}/${name}";
+      #         owner = mkDefault my.username;
+      #         group = mkDefault config.my.group;
+      #       })
+      #     (builtins.readDir secretsSourceDir);
+    };
+
+    environment.systemPackages = with pkgs; [agenix];
+  };
+}
diff --git a/modules/nixos/common/security.nix b/modules/nixos/common/security.nix
new file mode 100644
index 0000000..09c5da1
--- /dev/null
+++ b/modules/nixos/common/security.nix
@@ -0,0 +1,29 @@
+{
+  inputs,
+  lib,
+  ...
+}:
+with lib; {
+  security = {
+    sudo = {
+      enable = true;
+      execWheelOnly = true;
+      wheelNeedsPassword = false;
+      # https://mwl.io/archives/1000
+      extraConfig = ''
+        Defaults env_keep += "SSH_CLIENT SSH_CONNECTION SSH_TTY SSH_AUTH_SOCK"
+      '';
+    };
+
+    polkit = {
+      enable = true;
+      # https://wiki.archlinux.org/title/Polkit#Bypass_password_prompt
+      extraConfig = ''
+        polkit.addRule(function (action, subject) {
+          if (subject.isInGroup('wheel'))
+            return polkit.Result.YES;
+        });
+      '';
+    };
+  };
+}
diff --git a/modules/nixos/common/services.nix b/modules/nixos/common/services.nix
new file mode 100644
index 0000000..725502a
--- /dev/null
+++ b/modules/nixos/common/services.nix
@@ -0,0 +1,10 @@
+_: {
+  services = {
+    # https://github.com/Irqbalance/irqbalance/issues/54#issuecomment-319245584
+    # https://unix.stackexchange.com/questions/710603/should-the-irqbalance-daemon-be-used-on-a-modern-desktop-x86-system
+    irqbalance.enable = true;
+
+    # https://github.com/NixOS/nixpkgs/issues/135888
+    nscd.enableNsncd = true;
+  };
+}
diff --git a/modules/nixos/common/shell.nix b/modules/nixos/common/shell.nix
new file mode 100644
index 0000000..5fbc441
--- /dev/null
+++ b/modules/nixos/common/shell.nix
@@ -0,0 +1,3 @@
+_: {
+  programs.command-not-found.enable = false;
+}
diff --git a/modules/nixos/common/systemd.nix b/modules/nixos/common/systemd.nix
new file mode 100644
index 0000000..5c7282d
--- /dev/null
+++ b/modules/nixos/common/systemd.nix
@@ -0,0 +1,22 @@
+{pkgs, ...}: {
+  hm.systemd.user.startServices = "sd-switch";
+
+  services.journald.extraConfig = ''
+    SystemMaxUse=5G
+  '';
+
+  systemd = let
+    extraConfig = ''
+      DefaultTimeoutStartSec=30s
+      DefaultTimeoutStopSec=15s
+    '';
+  in {
+    inherit extraConfig;
+    user = {inherit extraConfig;};
+  };
+
+  environment.sessionVariables = {
+    SYSTEMD_PAGER = "${pkgs.less}/bin/less";
+    SYSTEMD_LESS = "FRSXMK";
+  };
+}
diff --git a/modules/nixos/common/tmp.nix b/modules/nixos/common/tmp.nix
new file mode 100644
index 0000000..d56e2b6
--- /dev/null
+++ b/modules/nixos/common/tmp.nix
@@ -0,0 +1,18 @@
+_: {
+  systemd.mounts = [
+    {
+      type = "tmpfs";
+      what = "tmpfs";
+      where = "/tmp";
+      mountConfig.Options = [
+        "huge=within_size"
+        "mode=1777"
+        "noatime"
+        "nodev"
+        "nosuid"
+        "rw"
+        "size=25%"
+      ];
+    }
+  ];
+}
diff --git a/modules/nixos/common/users.nix b/modules/nixos/common/users.nix
new file mode 100644
index 0000000..22e8023
--- /dev/null
+++ b/modules/nixos/common/users.nix
@@ -0,0 +1,19 @@
+{lib, ...}:
+with lib; {
+  users = {
+    mutableUsers = false;
+
+    users = {
+      root.hashedPassword = "@HASHED_PASSWORD@";
+
+      ${my.username} = {
+        isNormalUser = true;
+        uid = 1000;
+        description = my.fullname;
+        inherit (my) hashedPassword;
+        openssh.authorizedKeys.keys = [my.ssh.key];
+        extraGroups = ["wheel"];
+      };
+    };
+  };
+}
diff --git a/modules/nixos/common/xdg.nix b/modules/nixos/common/xdg.nix
new file mode 100644
index 0000000..8ddf1ac
--- /dev/null
+++ b/modules/nixos/common/xdg.nix
@@ -0,0 +1,87 @@
+{
+  config,
+  lib,
+  this,
+  ...
+}:
+with lib; {
+  imports = let
+    withBase = s: ["home-manager" "users" my.username "xdg" s];
+  in [
+    (mkAliasOptionModule ["dirs" "cache"] (withBase "cacheHome"))
+    (mkAliasOptionModule ["dirs" "config"] (withBase "configHome"))
+    (mkAliasOptionModule ["dirs" "data"] (withBase "dataHome"))
+    (mkAliasOptionModule ["dirs" "state"] (withBase "stateHome"))
+    (mkAliasOptionModule ["userDirs"] (withBase "userDirs"))
+  ];
+
+  hm.xdg = mkMerge [
+    {
+      enable = true;
+
+      userDirs = let
+        inherit (config.my) home;
+        tmp = home + "/tmp";
+      in {
+        enable = true;
+
+        desktop = tmp;
+        documents = "${home}/doc";
+        download = tmp;
+        music = tmp;
+        pictures = tmp;
+        publicShare = "${home}/share";
+        templates = tmp;
+        videos = tmp;
+      };
+    }
+    (mkIf this.isHeadful {
+      mimeApps = {
+        enable = true;
+        defaultApplications = mkMerge (mapAttrsToList
+          (n: v: genAttrs v (_: ["${n}.desktop"]))
+          {
+            emacsclient = [
+              "application/json"
+              "application/vnd.ms-publisher"
+              "application/x-desktop"
+              "application/x-shellscript"
+              "application/x-trash"
+              "application/x-wine-extension-ini"
+              "application/xml"
+              "text/markdown"
+              "text/plain"
+            ];
+            firefox = [
+              "text/html"
+              "x-scheme-handler/http"
+              "x-scheme-handler/https"
+            ];
+            imv = [
+              "image/bmp"
+              "image/gif"
+              "image/jpeg"
+              "image/jpg"
+              "image/png"
+              "image/svg+xml"
+              "image/tiff"
+              "image/webp"
+            ];
+            mpv = [
+              "audio/aac"
+              "audio/flac"
+              "audio/mp3"
+              "audio/ogg"
+              "audio/wav"
+              "audio/webm"
+              "video/mkv"
+              "video/mp4"
+              "video/ogg"
+              "video/webm"
+              "video/x-matroska"
+            ];
+          });
+      };
+    })
+  ];
+}
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix
new file mode 100644
index 0000000..b35e461
--- /dev/null
+++ b/modules/nixos/default.nix
@@ -0,0 +1,59 @@
+_: {
+  imports = [
+    ./acme.nix
+    ./alertmanager.nix
+    ./android.nix
+    ./bluetooth.nix
+    ./common
+    ./discord.nix
+    ./docker.nix
+    ./dwm.nix
+    ./emacs.nix
+    ./endlessh-go.nix
+    ./endlessh.nix
+    ./fail2ban.nix
+    ./fonts.nix
+    ./games
+    ./git.nix
+    ./gnupg.nix
+    ./gotify.nix
+    ./grafana.nix
+    ./hydra.nix
+    ./ipfs.nix
+    ./kde.nix
+    ./libvirtd.nix
+    ./lidarr.nix
+    ./loki.nix
+    ./lxc.nix
+    ./matrix
+    ./monitoring
+    ./nextcloud.nix
+    ./nginx.nix
+    ./node-exporter.nix
+    ./nsd.nix
+    ./openssh.nix
+    ./podman.nix
+    ./postgresql.nix
+    ./profiles
+    ./prometheus.nix
+    ./promtail.nix
+    ./psd.nix
+    ./radarr.nix
+    ./radicale.nix
+    ./rss-bridge.nix
+    ./rtorrent.nix
+    ./searx.nix
+    ./shadowsocks.nix
+    ./soju.nix
+    ./solaar.nix
+    ./sonarr.nix
+    ./sound.nix
+    ./syncthing.nix
+    ./throttled.nix
+    ./unbound.nix
+    ./vaultwarden.nix
+    ./wireguard.nix
+    ./x11.nix
+    ./xmonad.nix
+  ];
+}
diff --git a/modules/nixos/discord.nix b/modules/nixos/discord.nix
new file mode 100644
index 0000000..190b5fc
--- /dev/null
+++ b/modules/nixos/discord.nix
@@ -0,0 +1,22 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.discord;
+in {
+  options.nixfiles.modules.discord.enable =
+    mkEnableOption "Steam runtime";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.common.nix.allowedUnfreePackages = ["discord"];
+
+    hm.home.packages = with pkgs; [
+      (discord.override {
+        withOpenASAR = true;
+      })
+    ];
+  };
+}
diff --git a/modules/nixos/docker.nix b/modules/nixos/docker.nix
new file mode 100644
index 0000000..e642030
--- /dev/null
+++ b/modules/nixos/docker.nix
@@ -0,0 +1,41 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.docker;
+in {
+  options.nixfiles.modules.docker.enable = mkEnableOption "Docker";
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !config.nixfiles.modules.podman.enable;
+        message = "Pick only one!";
+      }
+    ];
+
+    secrets.containers-auth = {
+      file = "${inputs.self}/secrets/containers-auth";
+      path = "${config.my.home}/.docker/config.json";
+      owner = my.username;
+      inherit (config.my) group;
+    };
+
+    virtualisation.docker.enable = true;
+
+    environment.systemPackages = with pkgs; [docker-compose];
+
+    my.extraGroups = ["docker"];
+
+    hm.programs.bash = {
+      shellAliases.d = "${pkgs.docker}/bin/docker";
+      initExtra = mkAfter ''
+        _complete_alias d _docker docker
+      '';
+    };
+  };
+}
diff --git a/modules/nixos/dwm.nix b/modules/nixos/dwm.nix
new file mode 100644
index 0000000..618d8ed
--- /dev/null
+++ b/modules/nixos/dwm.nix
@@ -0,0 +1,159 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.dwm;
+in {
+  options.nixfiles.modules.dwm.enable = mkEnableOption "dwm";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.x11.enable = true;
+
+    hm.xsession = {
+      enable = true;
+
+      windowManager.command = let
+        pkg = pkgs.dwm.override {
+          conf = let
+            font = with config.fontScheme.monospaceFont; "${family}:size=${toString size}";
+            colour = config.colourScheme;
+          in ''
+            static const unsigned int borderpx = 1;
+            static const unsigned int snap = 32;
+            static const int showbar = 1;
+            static const int topbar = 1;
+
+            static const char *fonts[] = {
+              "${font}"
+            };
+
+            static const char *colors[][3] = {
+              [SchemeNorm] = {
+                "${colour.white}",
+                "${colour.black}",
+                "${colour.black}",
+              },
+              [SchemeSel] = {
+                "${colour.black}",
+                "${colour.white}",
+                "${colour.white}",
+              },
+            };
+
+            static const char *tags[] = {
+              "1",
+              "2",
+              "3",
+              "4",
+              "5",
+              "6",
+              "7",
+              "8",
+              "9"
+            };
+
+            static const Rule rules[] = {
+              { "Emacs", NULL, NULL, 1 << 0, 0, -1 },
+            };
+
+            static const float mfact = 0.666;
+            static const int nmaster = 1;
+            static const int resizehints = 0;
+            static const int lockfullscreen = 1;
+
+            static const Layout layouts[] = {
+              { "[]=", tile },
+              { "><>", NULL },
+              { "[M]", monocle },
+            };
+
+            #define MODKEY Mod4Mask
+            #define TAGKEYS(KEY,TAG) \
+              { MODKEY,                       KEY, view,       { .ui = 1 << TAG } }, \
+              { MODKEY|ControlMask,           KEY, toggleview, { .ui = 1 << TAG } }, \
+              { MODKEY|ShiftMask,             KEY, tag,        { .ui = 1 << TAG } }, \
+              { MODKEY|ControlMask|ShiftMask, KEY, toggletag,  { .ui = 1 << TAG } },
+
+            static char dmenumon[2] = "0";
+            static const char *dmenucmd[] = {
+              "${pkgs.dmenu}/bin/dmenu_run",
+              "-m", dmenumon,
+              "-fn", "${font}",
+              "-nb", "${colour.black}",
+              "-nf", "${colour.white}",
+              "-sb", "${colour.white}",
+              "-sf", "${colour.black}",
+              NULL,
+            };
+            static const char *termcmd[] = {
+              "${pkgs.alacritty}/bin/alacritty",
+              NULL,
+            };
+
+            static const Key keys[] = {
+              { MODKEY,           XK_x,      spawn,          {.v = dmenucmd } },
+              { MODKEY,           XK_Return, spawn,          {.v = termcmd } },
+              { MODKEY,           XK_b,      togglebar,      {0} },
+              { MODKEY,           XK_j,      focusstack,     {.i = +1 } },
+              { MODKEY,           XK_k,      focusstack,     {.i = -1 } },
+              { MODKEY|ShiftMask, XK_k,      incnmaster,     {.i = +1 } },
+              { MODKEY|ShiftMask, XK_j,      incnmaster,     {.i = -1 } },
+              { MODKEY,           XK_comma,  setmfact,       {.f = -0.05} },
+              { MODKEY,           XK_period, setmfact,       {.f = +0.05} },
+              { MODKEY,           XK_p,      zoom,           {0} },
+              { MODKEY,           XK_Tab,    view,           {0} },
+              { MODKEY,           XK_d,      killclient,     {0} },
+              { MODKEY,           XK_t,      setlayout,      {.v = &layouts[0]} },
+              { MODKEY,           XK_m,      setlayout,      {.v = &layouts[1]} },
+              { MODKEY,           XK_f,      setlayout,      {.v = &layouts[2]} },
+              { MODKEY,           XK_o,      togglefloating, {0} },
+              { MODKEY,           XK_0,      view,           {.ui = ~0 } },
+              { MODKEY|ShiftMask, XK_0,      tag,            {.ui = ~0 } },
+              { MODKEY,           XK_h,      focusmon,       {.i = -1 } },
+              { MODKEY,           XK_l,      focusmon,       {.i = +1 } },
+              { MODKEY|ShiftMask, XK_h,      tagmon,         {.i = -1 } },
+              { MODKEY|ShiftMask, XK_l,      tagmon,         {.i = +1 } },
+              TAGKEYS(            XK_1,                      0)
+              TAGKEYS(            XK_2,                      1)
+              TAGKEYS(            XK_3,                      2)
+              TAGKEYS(            XK_4,                      3)
+              TAGKEYS(            XK_5,                      4)
+              TAGKEYS(            XK_6,                      5)
+              TAGKEYS(            XK_7,                      6)
+              TAGKEYS(            XK_8,                      7)
+              TAGKEYS(            XK_9,                      8)
+              { MODKEY|ShiftMask, XK_q,      quit,           {0} },
+            };
+
+            static const Button buttons[] = {
+              { ClkLtSymbol,   0,      Button1, setlayout,      {0} },
+              { ClkLtSymbol,   0,      Button3, setlayout,      {.v = &layouts[2]} },
+              { ClkWinTitle,   0,      Button2, zoom,           {0} },
+              { ClkStatusText, 0,      Button2, spawn,          {.v = termcmd } },
+              { ClkClientWin,  MODKEY, Button1, movemouse,      {0} },
+              { ClkClientWin,  MODKEY, Button2, togglefloating, {0} },
+              { ClkClientWin,  MODKEY, Button3, resizemouse,    {0} },
+              { ClkTagBar,     0,      Button1, view,           {0} },
+              { ClkTagBar,     0,      Button3, toggleview,     {0} },
+              { ClkTagBar,     MODKEY, Button1, tag,            {0} },
+              { ClkTagBar,     MODKEY, Button3, toggletag,      {0} },
+            };
+          '';
+        };
+      in "${pkg}/bin/dwm";
+    };
+
+    hm.services.dwm-status = {
+      enable = true;
+      # package = pkgs.dwm-status.override {
+      #   enableAlsaUtils = false;
+      # };
+      order = ["audio" "backlight" "battery" "cpu_load" "network" "time"];
+    };
+
+    services.xserver.displayManager.startx.enable = true;
+  };
+}
diff --git a/modules/nixos/emacs.nix b/modules/nixos/emacs.nix
new file mode 100644
index 0000000..800d411
--- /dev/null
+++ b/modules/nixos/emacs.nix
@@ -0,0 +1,30 @@
+{
+  config,
+  inputs,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.emacs;
+in {
+  config = mkIf cfg.enable {
+    secrets.authinfo = {
+      file = "${inputs.self}/secrets/authinfo";
+      owner = my.username;
+      inherit (config.my) group;
+    };
+
+    nixfiles.modules.x11.enable = true;
+
+    hm = {
+      programs.doom-emacs.extraConfig = ''
+        (appendq! auth-sources '("${config.secrets.authinfo.path}"))
+      '';
+
+      services.emacs = {
+        enable = true;
+        client.enable = true;
+      };
+    };
+  };
+}
diff --git a/modules/nixos/endlessh-go.nix b/modules/nixos/endlessh-go.nix
new file mode 100644
index 0000000..435305d
--- /dev/null
+++ b/modules/nixos/endlessh-go.nix
@@ -0,0 +1,30 @@
+{
+  config,
+  lib,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.endlessh-go;
+in {
+  options.nixfiles.modules.endlessh-go.enable = mkEnableOption "endlessh-go";
+
+  config = let
+    port = 22;
+  in
+    mkIf cfg.enable {
+      services.endlessh-go = {
+        enable = true;
+        listenAddress = "0.0.0.0";
+        inherit port;
+        prometheus = {
+          enable = true;
+          listenAddress = this.wireguard.ipv4.address;
+          port = 9229;
+        };
+        extraOptions = ["-geoip_supplier=ip-api" "-v=1"];
+      };
+
+      networking.firewall.allowedTCPPorts = [port];
+    };
+}
diff --git a/modules/nixos/endlessh.nix b/modules/nixos/endlessh.nix
new file mode 100644
index 0000000..67789fd
--- /dev/null
+++ b/modules/nixos/endlessh.nix
@@ -0,0 +1,24 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.endlessh;
+in {
+  options.nixfiles.modules.endlessh.enable = mkEnableOption "endlessh";
+
+  config = let
+    port = 22;
+  in
+    mkIf cfg.enable {
+      services.endlessh = {
+        enable = true;
+        inherit port;
+        extraOptions = ["-v" "-4"];
+      };
+
+      networking.firewall.allowedTCPPorts = [port];
+    };
+}
diff --git a/modules/nixos/fail2ban.nix b/modules/nixos/fail2ban.nix
new file mode 100644
index 0000000..5ac3c9c
--- /dev/null
+++ b/modules/nixos/fail2ban.nix
@@ -0,0 +1,32 @@
+{
+  config,
+  lib,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.fail2ban;
+in {
+  options.nixfiles.modules.fail2ban.enable =
+    mkEnableOption "fail2ban";
+
+  config = mkIf cfg.enable {
+    services.fail2ban = {
+      enable = true;
+
+      bantime-increment = {
+        enable = true;
+        maxtime = "24h";
+        rndtime = "8m";
+      };
+
+      ignoreIP =
+        optionals (hasAttr "wireguard" this)
+        (with config.nixfiles.modules.wireguard; [ipv4.subnet ipv6.subnet]);
+
+      jails.DEFAULT = ''
+        blocktype = DROP
+      '';
+    };
+  };
+}
diff --git a/modules/nixos/fonts.nix b/modules/nixos/fonts.nix
new file mode 100644
index 0000000..d4a7330
--- /dev/null
+++ b/modules/nixos/fonts.nix
@@ -0,0 +1,45 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.fonts;
+in {
+  config = mkMerge [
+    (mkIf cfg.enable {
+      hm.fonts.fontconfig.enable = true;
+      fonts.fontconfig = {
+        enable = true;
+
+        defaultFonts = {
+          monospace = [
+            "Iosevka"
+            "Sarasa Mono K"
+            "Sarasa Mono J"
+            "Sarasa Mono SC"
+            "Sarasa Mono CL"
+          ];
+          sansSerif = [
+            "Iosevka Aile"
+            "Sarasa Gothic K"
+            "Sarasa Gothic J"
+            "Sarasa Gothic SC"
+            "Sarasa Gothic CL"
+          ];
+          serif = [
+            "Iosevka Etoile"
+            "Sarasa Gothic K"
+            "Sarasa Gothic J"
+            "Sarasa Gothic SC"
+            "Sarasa Gothic CL"
+          ];
+        };
+      };
+    })
+    (mkIf (!cfg.enable) {
+      hm.fonts.fontconfig.enable = false;
+      fonts.fontconfig.enable = false;
+    })
+  ];
+}
diff --git a/modules/nixos/games/default.nix b/modules/nixos/games/default.nix
new file mode 100644
index 0000000..1c5766b
--- /dev/null
+++ b/modules/nixos/games/default.nix
@@ -0,0 +1,38 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games;
+in {
+  imports = [
+    ./gamemode.nix
+    ./gog.nix
+    ./lutris.nix
+    ./mangohud.nix
+    ./minecraft.nix
+    ./steam-run.nix
+    ./steam.nix
+  ];
+
+  options.nixfiles.modules.games.enable32BitSupport =
+    mkEnableOption "support for games";
+
+  config = mkIf cfg.enable32BitSupport {
+    services = {
+      jack.alsa.support32Bit = config.services.jack.alsa.enable;
+
+      pipewire.alsa.support32Bit = config.services.pipewire.alsa.enable;
+    };
+
+    hardware = {
+      opengl = mkIf config.hardware.opengl.enable {
+        extraPackages32 = config.hardware.opengl.extraPackages;
+        driSupport32Bit = config.hardware.opengl.driSupport;
+      };
+
+      pulseaudio.support32Bit = config.hardware.pulseaudio.enable;
+    };
+  };
+}
diff --git a/modules/nixos/games/gamemode.nix b/modules/nixos/games/gamemode.nix
new file mode 100644
index 0000000..051d12e
--- /dev/null
+++ b/modules/nixos/games/gamemode.nix
@@ -0,0 +1,13 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games.gamemode;
+in {
+  options.nixfiles.modules.games.gamemode.enable =
+    mkEnableOption "Feral GameMode";
+
+  config = mkIf cfg.enable {programs.gamemode.enable = true;};
+}
diff --git a/modules/nixos/games/gog.nix b/modules/nixos/games/gog.nix
new file mode 100644
index 0000000..86039f1
--- /dev/null
+++ b/modules/nixos/games/gog.nix
@@ -0,0 +1,18 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games.gog;
+in {
+  options.nixfiles.modules.games.gog.enable =
+    mkEnableOption "stand-alone GOG clients and the ability to run GOG games";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.games.steam-run.enable = true;
+
+    hm.home.packages = with pkgs; [lgogdownloader];
+  };
+}
diff --git a/modules/nixos/games/lutris.nix b/modules/nixos/games/lutris.nix
new file mode 100644
index 0000000..72179fc
--- /dev/null
+++ b/modules/nixos/games/lutris.nix
@@ -0,0 +1,32 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games.lutris;
+in {
+  options.nixfiles.modules.games.lutris.enable = mkEnableOption "Lutris";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.games = {
+      gamemode.enable = true;
+      mangohud.enable = true;
+      steam-run.enable = true;
+    };
+
+    hm.home.packages = with pkgs; [
+      (lutris.override {
+        lutris-unwrapped = lutris-unwrapped.override {
+          wine = buildFHSUserEnv {
+            # We don't really need Wine because Lutris downloads a required
+            # runtime for us.
+            name = "empty";
+          };
+        };
+      })
+      vkBasalt
+    ];
+  };
+}
diff --git a/modules/nixos/games/mangohud.nix b/modules/nixos/games/mangohud.nix
new file mode 100644
index 0000000..d693c82
--- /dev/null
+++ b/modules/nixos/games/mangohud.nix
@@ -0,0 +1,26 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games.mangohud;
+in {
+  options.nixfiles.modules.games.mangohud.enable = mkEnableOption "MangoHud";
+
+  config = mkIf cfg.enable {
+    hm.programs.mangohud = {
+      enable = true;
+      settings = {
+        fps = true;
+        frame_timing = true;
+        gpu_stats = true;
+        gpu_temp = true;
+        cpu_stats = true;
+        cpu_temp = true;
+        ram = true;
+        vram = true;
+      };
+    };
+  };
+}
diff --git a/modules/nixos/games/minecraft.nix b/modules/nixos/games/minecraft.nix
new file mode 100644
index 0000000..e53f9eb
--- /dev/null
+++ b/modules/nixos/games/minecraft.nix
@@ -0,0 +1,50 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games.minecraft;
+in {
+  options.nixfiles.modules.games.minecraft = {
+    client.enable = mkEnableOption "Minecraft client";
+    server = {
+      enable = mkEnableOption "Minecraft server";
+
+      memory = mkOption {
+        description = "Amount of RAM to allocate.";
+        type = types.str;
+        default = "2G";
+      };
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.client.enable {
+      hm.home.packages = with pkgs; [pollymc];
+    })
+    (mkIf cfg.server.enable {
+      # Configurations, opslist, whitelist and plugins are managed imperatively.
+      # TODO Make it declarative.
+      services.minecraft-server = {
+        enable = true;
+        eula = true;
+
+        package = pkgs.minecraftServers.purpur_1_19_2;
+
+        # TODO Make a PR fixing trailing whitespace on this.
+        jvmOpts =
+          (concatStringsSep " " [
+            "-Xmx${cfg.server.memory}"
+            "-Xms${cfg.server.memory}"
+            "--add-modules=jdk.incubator.vector"
+          ])
+          + " ";
+      };
+
+      # Defined in /var/lib/minecraft/server.properties.
+      networking.firewall.allowedTCPPorts = [55565];
+    })
+  ];
+}
diff --git a/modules/nixos/games/steam-run.nix b/modules/nixos/games/steam-run.nix
new file mode 100644
index 0000000..1a1e61f
--- /dev/null
+++ b/modules/nixos/games/steam-run.nix
@@ -0,0 +1,73 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games.steam-run;
+in {
+  options.nixfiles.modules.games.steam-run = {
+    enable = mkEnableOption "native Steam runtime";
+
+    quirks = {
+      mountAndBladeWarband = mkEnableOption ''fixes for "Mount & Blade: Warband" issues'';
+      cryptOfTheNecrodancer = mkEnableOption ''fixes for "Crypt of the NecroDancer" issues'';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      common.nix.allowedUnfreePackages = ["steam" "steam-run"];
+
+      games = {
+        enable32BitSupport = true;
+        gamemode.enable = true;
+      };
+    };
+
+    hm.home.packages = with pkgs; [
+      (steam.override {
+        extraLibraries = _:
+          with cfg.quirks;
+            []
+            ++ optionals mountAndBladeWarband [
+              (glew.overrideAttrs (_: super: let
+                opname = super.pname;
+              in rec {
+                pname = "${opname}-runfix";
+                inherit (super) version;
+                src = fetchurl {
+                  url = "mirror://sourceforge/${opname}/${opname}-${version}.tgz";
+                  hash = "sha256-BN6R5+Z2MDm8EZQAlc2cf4gLq6ghlqd2X3J6wFqZPJU=";
+                };
+              }))
+              (fmodex.overrideAttrs (_: super: let
+                opname = super.pname;
+              in rec {
+                pname = "${opname}-runfix";
+                inherit (super) version;
+                installPhase = let
+                  libPath = makeLibraryPath [
+                    alsa-lib
+                    libpulseaudio
+                    stdenv.cc.cc
+                  ];
+                in ''
+                  install -Dm755 api/lib/libfmodex64-${version}.so $out/lib/libfmodex64.so
+                  patchelf --set-rpath ${libPath} $out/lib/libfmodex64.so
+                '';
+              }))
+            ]
+            ++ optionals cryptOfTheNecrodancer [
+              (import (builtins.fetchTarball {
+                url = "https://github.com/NixOS/nixpkgs/archive/d1c3fea7ecbed758168787fe4e4a3157e52bc808.tar.gz";
+                sha256 = "0ykm15a690v8lcqf2j899za3j6hak1rm3xixdxsx33nz7n3swsyy";
+              }) {inherit (config.nixpkgs) config localSystem;})
+              .flac
+            ];
+      })
+      .run
+    ];
+  };
+}
diff --git a/modules/nixos/games/steam.nix b/modules/nixos/games/steam.nix
new file mode 100644
index 0000000..8dfa72c
--- /dev/null
+++ b/modules/nixos/games/steam.nix
@@ -0,0 +1,25 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.games.steam;
+in {
+  options.nixfiles.modules.games.steam.enable =
+    mkEnableOption "Steam runtime";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      common.nix.allowedUnfreePackages = ["steam" "steam-original"];
+
+      games = {
+        enable32BitSupport = true;
+        gamemode.enable = true;
+      };
+    };
+
+    hm.home.packages = with pkgs; [steam];
+  };
+}
diff --git a/modules/nixos/git.nix b/modules/nixos/git.nix
new file mode 100644
index 0000000..f754588
--- /dev/null
+++ b/modules/nixos/git.nix
@@ -0,0 +1,117 @@
+{
+  config,
+  lib,
+  inputs,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.git;
+in {
+  options.nixfiles.modules.git.server = {
+    enable = mkEnableOption "Git server";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; nullOr str;
+      default = "git.${config.networking.domain}";
+    };
+
+    package = mkOption {
+      description = "Package.";
+      type = types.package;
+      default = pkgs.cgit-pink;
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.client.enable {
+      secrets = {
+        glab-cli-config = {
+          file = "${inputs.self}/secrets/glab-cli-config";
+          path = "${config.dirs.config}/glab-cli/config.yml";
+          owner = my.username;
+          inherit (config.my) group;
+        };
+        gh-hosts = {
+          file = "${inputs.self}/secrets/gh-hosts";
+          path = "${config.dirs.config}/gh/hosts.yml";
+          owner = my.username;
+          inherit (config.my) group;
+        };
+        hut = {
+          file = "${inputs.self}/secrets/hut";
+          path = "${config.dirs.config}/hut/config";
+          owner = my.username;
+          inherit (config.my) group;
+        };
+      };
+    })
+    (mkIf cfg.server.enable {
+      nixfiles.modules.nginx = {
+        enable = true;
+        virtualHosts.${cfg.server.domain} = {
+          locations = {
+            "/".extraConfig = let
+              cgitrc = pkgs.writeText "cgitrc" ''
+                root-title=azahi’s git stuff
+                root-desc=鯛も一人はうまからず
+
+                about-filter=${cfg.server.package}/lib/cgit/filters/about-formatting.sh
+                source-filter=${cfg.server.package}/lib/cgit/filters/syntax-highlighting.py
+                commit-filter=${cfg.server.package}/lib/cgit/filters/commit-links.sh
+
+                enable-git-config=1
+                enable-gitweb-owner=1
+                remove-suffix=1
+
+                snapshots=tar.gz tar.bz2 zip
+
+                readme=:README
+                readme=:README.md
+                readme=:README.org
+                readme=:README.txt
+                readme=:readme
+                readme=:readme.md
+                readme=:readme.org
+                readme=:readme.txt
+
+                scan-path=${config.services.gitolite.dataDir}/repositories
+              '';
+            in ''
+              include ${config.services.nginx.package}/conf/fastcgi_params;
+              fastcgi_split_path_info ^(/?)(.+)$;
+              fastcgi_pass unix:${config.services.fcgiwrap.socketAddress};
+              fastcgi_param SCRIPT_FILENAME ${cfg.server.package}/cgit/cgit.cgi;
+              fastcgi_param CGIT_CONFIG ${cgitrc};
+              fastcgi_param PATH_INFO $uri;
+              fastcgi_param QUERY_STRING $args;
+              fastcgi_param HTTP_HOST $server_name;
+            '';
+            # FIXME This breaks sources previewing for these files.
+            "~* ^/(.+.(ico|css|png))$".extraConfig = ''
+              alias ${cfg.server.package}/cgit/$1;
+            '';
+          };
+        };
+      };
+
+      services = let
+        user = "git";
+        group = "git";
+      in {
+        gitolite = {
+          # TODO Make the configuration purely declarative.
+          enable = true;
+          inherit user group;
+          adminPubkey = my.ssh.key;
+        };
+
+        fcgiwrap = {
+          enable = true;
+          inherit user group;
+        };
+      };
+    })
+  ];
+}
diff --git a/modules/nixos/gnupg.nix b/modules/nixos/gnupg.nix
new file mode 100644
index 0000000..b86be9b
--- /dev/null
+++ b/modules/nixos/gnupg.nix
@@ -0,0 +1,38 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.gnupg;
+in {
+  options.nixfiles.modules.gnupg.pinentry = mkOption {
+    description = "Name of a pinentry implementation.";
+    type = types.str;
+    default = "curses";
+  };
+
+  config = mkIf cfg.enable {
+    hm = {
+      programs.gpg.homedir = "${config.dirs.data}/gnupg";
+
+      services.gpg-agent = {
+        enable = true;
+
+        enableSshSupport = true;
+        enableScDaemon = false;
+
+        defaultCacheTtl = 999999;
+        defaultCacheTtlSsh = 999999;
+        maxCacheTtl = 999999;
+        maxCacheTtlSsh = 999999;
+
+        grabKeyboardAndMouse = true;
+
+        sshKeys = [my.pgp.grip];
+
+        pinentryFlavor = cfg.pinentry;
+      };
+    };
+  };
+}
diff --git a/modules/nixos/gotify.nix b/modules/nixos/gotify.nix
new file mode 100644
index 0000000..db47bb4
--- /dev/null
+++ b/modules/nixos/gotify.nix
@@ -0,0 +1,75 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.gotify;
+in {
+  options.nixfiles.modules.gotify = {
+    enable = mkEnableOption "Gotify";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "gotify.${config.networking.domain}";
+    };
+  };
+
+  config = let
+    db = "gotify";
+  in
+    mkIf cfg.enable {
+      nixfiles.modules = {
+        nginx = {
+          enable = true;
+          upstreams.gotify.servers."127.0.0.1:${toString config.services.gotify.port}" = {};
+          virtualHosts.${cfg.domain} = {
+            locations."/" = {
+              proxyPass = "http://gotify";
+              proxyWebsockets = true;
+            };
+            extraConfig = nginxInternalOnly;
+          };
+        };
+        postgresql = {
+          enable = true;
+          extraPostStart = [
+            ''
+              $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"'
+            ''
+          ];
+        };
+      };
+
+      services = {
+        gotify = {
+          enable = true;
+          port = 7665;
+        };
+
+        postgresql = {
+          ensureDatabases = [db];
+          ensureUsers = [
+            {
+              name = db;
+              ensurePermissions."DATABASE \"${db}\"" = "ALL";
+            }
+          ];
+        };
+      };
+
+      systemd.services.gotify-server = {
+        after = ["network-online.target" "postgresql.service"];
+        environment = {
+          GOTIFY_DATABASE_DIALECT = "postgres";
+          GOTIFY_DATABASE_CONNECTION = concatStringsSep " " [
+            "host=/run/postgresql"
+            "user=${db}"
+            "dbname=${db}"
+            "sslmode=disable"
+          ];
+        };
+      };
+    };
+}
diff --git a/modules/nixos/grafana.nix b/modules/nixos/grafana.nix
new file mode 100644
index 0000000..a614502
--- /dev/null
+++ b/modules/nixos/grafana.nix
@@ -0,0 +1,119 @@
+{
+  config,
+  inputs,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.grafana;
+in {
+  options.nixfiles.modules.grafana = {
+    enable = mkEnableOption "Grafana";
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 30101;
+    };
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; nullOr str;
+      default = "grafana.${config.networking.domain}";
+    };
+  };
+
+  config = let
+    db = "grafana";
+  in
+    mkIf cfg.enable {
+      secrets = {
+        grafana-key = {
+          file = "${inputs.self}/secrets/grafana-key";
+          owner = "grafana";
+          group = "grafana";
+        };
+        grafana-admin-password = {
+          file = "${inputs.self}/secrets/grafana-admin-password";
+          owner = "grafana";
+          group = "grafana";
+        };
+        grafana-smtp-password = {
+          file = "${inputs.self}/secrets/grafana-smtp-password";
+          owner = "grafana";
+          group = "grafana";
+        };
+      };
+
+      nixfiles.modules = {
+        nginx = {
+          enable = true;
+          upstreams.grafana.servers."127.0.0.1:${toString cfg.port}" = {};
+          virtualHosts.${cfg.domain} = {
+            locations."/" = {
+              proxyPass = "http://grafana";
+              proxyWebsockets = true;
+            };
+            extraConfig = nginxInternalOnly;
+          };
+        };
+        postgresql = {
+          enable = true;
+          extraPostStart = [
+            ''
+              $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"'
+            ''
+          ];
+        };
+      };
+
+      services = {
+        grafana = {
+          enable = true;
+
+          settings = {
+            server = with cfg; {
+              protocol = "http";
+              http_addr = "127.0.0.1";
+              http_port = port;
+              inherit domain;
+              enable_gzip = true;
+            };
+            database = {
+              type = "postgres";
+              host = "/run/postgresql";
+              name = db;
+              user = db;
+            };
+            smtp = {
+              enable = true;
+              user = "azahi@shire.me";
+              host = my.domain.shire;
+              password = "$__file{${config.secrets.grafana-smtp-password.path}}";
+            };
+            user = {
+              allow_org_create = false;
+              allow_sign_up = false;
+              auto_assign_org = false;
+              auto_assign_org_role = "Viewer";
+            };
+            security = with config.secrets; {
+              secret_key = grafana-key.path;
+              admin_password = grafana-admin-password.path;
+            };
+            analytics.reporting_enable = false;
+          };
+        };
+
+        postgresql = {
+          ensureDatabases = [db];
+          ensureUsers = [
+            {
+              name = db;
+              ensurePermissions."DATABASE \"${db}\"" = "ALL";
+            }
+          ];
+        };
+      };
+    };
+}
diff --git a/modules/nixos/hydra.nix b/modules/nixos/hydra.nix
new file mode 100644
index 0000000..590fecb
--- /dev/null
+++ b/modules/nixos/hydra.nix
@@ -0,0 +1,57 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.hydra;
+in {
+  options.nixfiles.modules.hydra = {
+    enable = mkEnableOption "Hydra";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "hydra.${config.networking.domain}";
+    };
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 7754;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      nginx = {
+        enable = true;
+        upstreams.hydra.servers."127.0.0.1:${toString cfg.port}" = {};
+        virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://hydra";
+      };
+      postgresql.enable = true;
+    };
+
+    services = let
+      db = "hydra";
+    in {
+      hydra = {
+        enable = true;
+        listenHost = "127.0.0.1";
+        inherit (cfg) port;
+        dbi = "dbi:Pg:dbname=${db};user=${db}";
+        hydraURL = cfg.domain;
+      };
+
+      postgresql = {
+        ensureDatabases = [db];
+        ensureUsers = [
+          {
+            name = db;
+            ensurePermissions."DATABASE \"${db}\"" = "ALL";
+          }
+        ];
+      };
+    };
+  };
+}
diff --git a/modules/nixos/ipfs.nix b/modules/nixos/ipfs.nix
new file mode 100644
index 0000000..0ec64e5
--- /dev/null
+++ b/modules/nixos/ipfs.nix
@@ -0,0 +1,167 @@
+{
+  config,
+  lib,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.ipfs;
+
+  swarmDefaultPort = 4001;
+  apiDefaultPort = 5001;
+  gatewayDefaultPort = 6001;
+in {
+  options.nixfiles.modules.ipfs = {
+    enable = mkEnableOption "IPFS daemon";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "ipfs.${config.networking.fqdn}";
+    };
+
+    swarmPort = mkOption {
+      description = "Swarm port.";
+      type = with types; port;
+      default =
+        if this.isHeadless
+        then swarmDefaultPort + 990
+        else swarmDefaultPort;
+    };
+
+    apiPort = mkOption {
+      description = "API port.";
+      type = with types; port;
+      default =
+        if this.isHeadless
+        then apiDefaultPort + 990
+        else apiDefaultPort;
+    };
+
+    gatewayPort = mkOption {
+      description = "Gateway port.";
+      type = with types; port;
+      default =
+        if this.isHeadless
+        then gatewayDefaultPort + 990
+        else gatewayDefaultPort;
+    };
+  };
+
+  config = mkIf cfg.enable (mkMerge [
+    {
+      services.ipfs = {
+        enable = true;
+
+        user = my.username;
+        inherit (config.my) group;
+
+        dataDir = "${config.dirs.data}/ipfs";
+
+        swarmAddress = let
+          port = toString cfg.swarmPort;
+        in
+          if this.isHeadless
+          then [
+            "/ip4/127.0.0.1/tcp/${port}"
+            "/ip4/127.0.0.1/udp/${port}/quic"
+          ]
+          else [
+            "/ip4/0.0.0.0/tcp/${port}"
+            "/ip6/::/tcp/${port}"
+            "/ip4/0.0.0.0/udp/${port}/quic"
+            "/ip6/::/udp/${port}/quic"
+          ];
+        apiAddress = "/ip4/127.0.0.1/tcp/${toString cfg.apiPort}";
+        gatewayAddress = "/ip4/127.0.0.1/tcp/${toString cfg.gatewayPort}";
+
+        autoMigrate = true;
+        autoMount = true;
+        emptyRepo = true;
+        enableGC = true;
+
+        extraConfig = mkMerge [
+          (let
+            filterAddresses =
+              [
+                "/ip4/100.64.0.0/ipcidr/10"
+                "/ip4/169.254.0.0/ipcidr/16"
+                "/ip4/172.16.0.0/ipcidr/12"
+                "/ip4/192.0.0.0/ipcidr/24"
+                "/ip4/192.0.2.0/ipcidr/24"
+                "/ip4/192.168.0.0/ipcidr/16"
+                "/ip4/198.18.0.0/ipcidr/15"
+                "/ip4/198.51.100.0/ipcidr/24"
+                "/ip4/203.0.113.0/ipcidr/24"
+                "/ip4/240.0.0.0/ipcidr/4"
+                "/ip6/100::/ipcidr/64"
+                "/ip6/2001:2::/ipcidr/48"
+                "/ip6/2001:db8::/ipcidr/32"
+                "/ip6/fe80::/ipcidr/10"
+              ]
+              ++ optionals (!hasAttr "wireguard" this) [
+                "/ip4/10.0.0.0/ipcidr/8"
+                "/ip6/fc00::/ipcidr/7"
+              ];
+          in {
+            Addresses = with config.services.ipfs; {
+              # https://github.com/NixOS/nixpkgs/pull/165259
+              # I think this shit broke inheritance... Gotta test more and make
+              # a PR I guess.
+              API = apiAddress;
+              Gateway = gatewayAddress;
+              Swarm = swarmAddress;
+
+              NoAnnounce = filterAddresses;
+            };
+            Swarm.AddrFilters = filterAddresses;
+            API.HTTPHeaders.Access-Control-Allow-Methods = ["GET" "POST" "PUT"];
+          })
+          (mkIf this.isHeadful {
+            API.HTTPHeaders.Access-Control-Allow-Origin = ["*"];
+          })
+          (mkIf this.isHeadless {
+            API.HTTPHeaders.Access-Control-Allow-Origin = ["https://${cfg.domain}" "https://api.${cfg.domain}"];
+          })
+        ];
+      };
+
+      networking.firewall = rec {
+        allowedTCPPorts = [swarmDefaultPort];
+        allowedUDPPorts = allowedTCPPorts;
+      };
+    }
+    (mkIf this.isHeadless {
+      nixfiles.modules.nginx = {
+        enable = true;
+        upstreams = {
+          ipfs_gateway.servers."127.0.0.1:${toString cfg.gatewayPort}" = {};
+          ipfs_swarm.servers."127.0.0.1:${toString cfg.swarmPort}" = {};
+          ipfs_api.servers."127.0.0.1:${toString cfg.apiPort}" = {};
+        };
+        virtualHosts = {
+          ${cfg.domain}.locations."/".proxyPass = "http://ipfs_gateway";
+          "swarm.${cfg.domain}" = {
+            serverName = cfg.domain;
+            listen = [
+              {
+                addr = "0.0.0.0";
+                port = swarmDefaultPort;
+              }
+              {
+                addr = "[::0]";
+                port = swarmDefaultPort;
+              }
+            ];
+            locations."/".proxyPass = "http://ipfs_swarm";
+          };
+          "api.${cfg.domain}" = {
+            # TODO Redirect "/" to "/webui" but keep other endpoints.
+            locations."/".proxyPass = "http://ipfs_api";
+            extraConfig = nginxInternalOnly;
+          };
+        };
+      };
+    })
+  ]);
+}
diff --git a/modules/nixos/kde.nix b/modules/nixos/kde.nix
new file mode 100644
index 0000000..a430294
--- /dev/null
+++ b/modules/nixos/kde.nix
@@ -0,0 +1,49 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.kde;
+in {
+  options.nixfiles.modules.kde.enable = mkEnableOption "KDE Plasma";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      gnupg.pinentry = "qt";
+      sound.enable = true;
+      x11.enable = true;
+    };
+
+    hm = {
+      home.sessionVariables.GTK_USE_PORTAL = 1;
+
+      programs.firefox.profiles.default.settings = {
+        "widget.use-xdg-desktop-portal.file-picker" = 1;
+        "widget.use-xdg-desktop-portal.mime-handler" = 1;
+      };
+    };
+
+    services.xserver = {
+      desktopManager.plasma5 = {
+        enable = true;
+        excludePackages = with pkgs.plasma5Packages; [
+          elisa
+          gwenview
+          khelpcenter
+          okular
+          print-manager
+        ];
+      };
+      displayManager = {
+        sddm.enable = true;
+
+        # NOTE https://github.com/NixOS/nixpkgs/pull/199881
+        setupCommands = lib.mkForce "";
+      };
+    };
+
+    environment.systemPackages = with pkgs; [pinentry-qt];
+  };
+}
diff --git a/modules/nixos/libvirtd.nix b/modules/nixos/libvirtd.nix
new file mode 100644
index 0000000..ae8b336
--- /dev/null
+++ b/modules/nixos/libvirtd.nix
@@ -0,0 +1,44 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.libvirtd;
+in {
+  options.nixfiles.modules.libvirtd.enable = mkEnableOption "libvirtd";
+
+  config = mkIf cfg.enable {
+    virtualisation.libvirtd = {
+      enable = true;
+
+      onBoot = "ignore";
+      onShutdown = "shutdown";
+
+      extraConfig = ''
+        log_level = 4
+        log_outputs = "4:stderr"
+      '';
+
+      qemu = {
+        package = pkgs.qemu_kvm;
+        runAsRoot = false;
+
+        ovmf = {
+          enable = true;
+          packages = [pkgs.OVMFFull.fd];
+        };
+
+        swtpm = {
+          enable = false; # Is this required for Windows 11?
+          package = pkgs.swtpm-tpm2;
+        };
+      };
+    };
+
+    environment.systemPackages = with pkgs; [virt-manager qemu-utils];
+
+    my.extraGroups = ["libvirtd"];
+  };
+}
diff --git a/modules/nixos/lidarr.nix b/modules/nixos/lidarr.nix
new file mode 100644
index 0000000..f73f917
--- /dev/null
+++ b/modules/nixos/lidarr.nix
@@ -0,0 +1,28 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.lidarr;
+in {
+  options.nixfiles.modules.lidarr = {
+    enable = mkEnableOption "Lidarr";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "lidarr.${config.networking.fqdn}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.nginx = {
+      enable = true;
+      upstreams.lidarr.servers."127.0.0.1:8686" = {};
+      virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://lidarr";
+    };
+
+    services.lidarr.enable = true;
+  };
+}
diff --git a/modules/nixos/loki.nix b/modules/nixos/loki.nix
new file mode 100644
index 0000000..1582164
--- /dev/null
+++ b/modules/nixos/loki.nix
@@ -0,0 +1,102 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.loki;
+in {
+  options.nixfiles.modules.loki = {
+    enable = mkEnableOption "Loki";
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 30171;
+    };
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "loki.${config.networking.domain}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.nginx = with cfg; {
+      enable = true;
+      upstreams.loki.servers."127.0.0.1:${toString cfg.port}" = {};
+      virtualHosts.${domain} = {
+        locations."/".proxyPass = "http://loki";
+        extraConfig = nginxInternalOnly;
+      };
+    };
+
+    services.loki = {
+      enable = true;
+
+      configuration = rec {
+        auth_enabled = false;
+
+        server = rec {
+          http_listen_address = "127.0.0.1";
+          http_listen_port = cfg.port;
+
+          grpc_listen_address = "127.0.0.1";
+          grpc_listen_port = http_listen_port + 1;
+
+          log_level = "warn";
+        };
+
+        common = rec {
+          path_prefix = "/var/lib/loki";
+          storage.filesystem = {
+            chunks_directory = "${path_prefix}/chunker";
+            rules_directory = "${path_prefix}/ruler";
+          };
+          replication_factor = 1;
+          instance_interface_names = ["lo"];
+          ring = {
+            instance_addr = "127.0.0.1";
+            kvstore.store = "inmemory";
+          };
+        };
+
+        ruler = {
+          rule_path = "${common.path_prefix}/ruler";
+          storage = {
+            type = "local";
+            local.directory =
+              pkgs.writeTextDir "ruler/ruler.yml"
+              (generators.toJSON {} {groups = [{name = "default";}];});
+          };
+        };
+
+        schema_config.configs = [
+          {
+            from = "2020-01-01";
+            store = "boltdb-shipper";
+            object_store = "filesystem";
+            schema = "v11";
+            index = {
+              prefix = "index_";
+              period = "24h";
+            };
+            chunks = {
+              prefix = "chunks_";
+              period = "24h";
+            };
+          }
+        ];
+
+        analytics.reporting_enabled = false;
+      };
+    };
+
+    systemd.tmpfiles.rules = [
+      "d /var/lib/loki 0700 loki loki - -"
+      "d /var/lib/loki/ruler 0700 loki loki - -"
+    ];
+  };
+}
diff --git a/modules/nixos/lxc.nix b/modules/nixos/lxc.nix
new file mode 100644
index 0000000..4f7805f
--- /dev/null
+++ b/modules/nixos/lxc.nix
@@ -0,0 +1,16 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.lxc;
+in {
+  options.nixfiles.modules.lxc.enable =
+    mkEnableOption "LXC/LXD";
+
+  config = mkIf cfg.enable {
+    virtualisation.lxd.enable = true;
+    my.extraGroups = "lxd";
+  };
+}
diff --git a/modules/nixos/matrix/default.nix b/modules/nixos/matrix/default.nix
new file mode 100644
index 0000000..bd221c4
--- /dev/null
+++ b/modules/nixos/matrix/default.nix
@@ -0,0 +1 @@
+_: {imports = [./dendrite.nix ./element.nix ./synapse.nix];}
diff --git a/modules/nixos/matrix/dendrite.nix b/modules/nixos/matrix/dendrite.nix
new file mode 100644
index 0000000..0fad5f2
--- /dev/null
+++ b/modules/nixos/matrix/dendrite.nix
@@ -0,0 +1,157 @@
+{
+  config,
+  lib,
+  inputs,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.matrix.dendrite;
+in {
+  options.nixfiles.modules.matrix.dendrite = {
+    enable = mkEnableOption "Dendrite Matrix server";
+
+    domain = mkOption {
+      type = types.str;
+      default = config.networking.domain;
+      description = "Domain name sans protocol scheme.";
+    };
+  };
+
+  config = let
+    db = "dendrite";
+  in
+    mkIf cfg.enable {
+      secrets.dendrite-private-key = {
+        file = "${inputs.self}/secrets/dendrite-private-key";
+        mode = "0444"; # The user is dynamic so the file must be world-readable.
+      };
+      secrets.dendrite-environment-file = {
+        file = "${inputs.self}/secrets/dendrite-environment-file";
+        mode = "0444"; # The user is dynamic so the file must be world-readable.
+      };
+
+      nixfiles.modules = {
+        nginx = {
+          enable = true;
+          upstreams.dendrite.servers."127.0.0.1:${toString config.services.dendrite.httpPort}" = {};
+          virtualHosts.${cfg.domain}.locations = {
+            "/_matrix".proxyPass = "http://dendrite";
+            "= /.well-known/matrix/server" = {
+              extraConfig = ''
+                add_header Content-Type application/json;
+              '';
+              return = "200 '${
+                generators.toJSON {} {"m.server" = "${cfg.domain}:443";}
+              }'";
+            };
+            "= /.well-known/matrix/client" = {
+              extraConfig = ''
+                add_header Content-Type application/json;
+                add_header Access-Control-Allow-Origin *;
+              '';
+              return = "200 '${
+                generators.toJSON {} {
+                  "m.homeserver".base_url = "https://${cfg.domain}";
+                }
+              }'";
+            };
+          };
+        };
+        postgresql = {
+          enable = true;
+          extraPostStart = [
+            ''
+              $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"'
+            ''
+          ];
+        };
+      };
+
+      services = {
+        dendrite = {
+          enable = true;
+          httpPort = 8008;
+          environmentFile = config.secrets.dendrite-environment-file.path;
+          settings = {
+            version = 2;
+            global = {
+              server_name = cfg.domain;
+              private_key = config.secrets.dendrite-private-key.path;
+              database = {
+                connection_string = "postgresql://${db}@/${db}?host=/run/postgresql";
+                max_open_conns = 64;
+                max_idle_connections = 8;
+              };
+              cache = {
+                max_size_estimated = "1gb";
+                max_age = "1h";
+              };
+              trusted_third_party_id_servers = [
+                "matrix.org"
+                "nixos.org"
+                "vector.im"
+              ];
+              presence = {
+                enable_inbound = false;
+                enable_outbound = false;
+              };
+            };
+            client_api = {
+              registration_disabled = true;
+              guests_disabled = true;
+              registration_shared_secret = "$REGISTRATION_SHARED_SECRET";
+            };
+            media_api = {
+              max_file_size_bytes = 0;
+              dynamic_thumbnails = true;
+              max_thumbnail_generators = 8;
+              thumbnail_sizes = [
+                {
+                  width = 32;
+                  height = 32;
+                  method = "crop";
+                }
+                {
+                  width = 96;
+                  height = 96;
+                  method = "crop";
+                }
+                {
+                  width = 640;
+                  height = 480;
+                  method = "scale";
+                }
+              ];
+            };
+            logging = [
+              {
+                type = "std";
+                level = "warn";
+              }
+            ];
+          };
+        };
+
+        postgresql = {
+          ensureDatabases = [db];
+          ensureUsers = [
+            {
+              name = db;
+              ensurePermissions."DATABASE \"${db}\"" = "ALL";
+            }
+          ];
+        };
+      };
+
+      systemd.services.dendrite.serviceConfig.ExecStart =
+        mkForce
+        (concatStringsSep " " [
+          "${pkgs.dendrite}/bin/dendrite-monolith-server"
+          "--config /run/dendrite/dendrite.yaml"
+          "--http-bind-address 127.0.0.1:${
+            toString config.services.dendrite.httpPort
+          }"
+        ]);
+    };
+}
diff --git a/modules/nixos/matrix/element.nix b/modules/nixos/matrix/element.nix
new file mode 100644
index 0000000..3d47800
--- /dev/null
+++ b/modules/nixos/matrix/element.nix
@@ -0,0 +1,59 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.matrix.element;
+in {
+  options.nixfiles.modules.matrix.element = {
+    enable = mkEnableOption "Element, a Matrix web interface";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; nullOr str;
+      default = "element.${config.networking.domain}";
+    };
+
+    homeserver = mkOption {
+      description = "Default Matrix homeserver.";
+      type = with types; str;
+      default = my.domain.azahi;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = with config.nixfiles.modules.matrix;
+          (synapse.enable || dendrite.enable) && !(!synapse.enable && !dendrite.enable);
+        message = "Synapse or Dendrite must be enabled";
+      }
+    ];
+
+    nixfiles.modules.nginx = with cfg; {
+      enable = true;
+      virtualHosts.${domain}.locations."/".root = pkgs.element-web.override {
+        conf = {
+          default_server_config."m.homeserver" = {
+            base_url = "https://${homeserver}";
+            server_name = homeserver;
+          };
+          disable_custom_urls = true;
+          disable_guests = true;
+          disable_login_language_selector = true;
+          disable_3pid_login = true;
+          brand = homeserver;
+          branding.authFooterLinks = [
+            {
+              text = "Hosted on NixOS";
+              url = "https://nixos.org";
+            }
+          ];
+          default_theme = "dark";
+        };
+      };
+    };
+  };
+}
diff --git a/modules/nixos/matrix/synapse.nix b/modules/nixos/matrix/synapse.nix
new file mode 100644
index 0000000..6ff5e0d
--- /dev/null
+++ b/modules/nixos/matrix/synapse.nix
@@ -0,0 +1,93 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.matrix.synapse;
+in {
+  options.nixfiles.modules.matrix.synapse = {
+    enable = mkEnableOption "Synapse Matrix server";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = config.networking.domain;
+    };
+  };
+
+  config = let
+    bind_address = "127.0.0.1";
+    port = 8448;
+  in
+    mkIf cfg.enable {
+      nixfiles.modules = {
+        nginx = {
+          enable = true;
+          upstreams.synapse.servers."${bind_address}:${toString port}" = {};
+          virtualHosts.${cfg.domain}.locations = {
+            "~ ^(/_matrix|/_synapse/client)".proxyPass = "http://synapse";
+            "= /.well-known/matrix/server" = {
+              extraConfig = ''
+                add_header Content-Type application/json;
+              '';
+              return = "200 '${
+                generators.toJSON {} {"m.server" = "${cfg.domain}:443";}
+              }'";
+            };
+            "= /.well-known/matrix/client" = {
+              extraConfig = ''
+                add_header Content-Type application/json;
+                add_header Access-Control-Allow-Origin *;
+              '';
+              return = "200 '${
+                generators.toJSON {} {
+                  "m.homeserver".base_url = "https://${cfg.domain}";
+                }
+              }'";
+            };
+          };
+        };
+        postgresql.enable = true;
+      };
+
+      services = let
+        db = "synapse";
+      in {
+        matrix-synapse = {
+          enable = true;
+          server_name = config.networking.domain;
+
+          database_type = "psycopg2";
+          database_name = db;
+          database_user = db;
+
+          listeners = [
+            {
+              inherit bind_address port;
+              type = "http";
+              tls = false;
+              x_forwarded = true;
+              resources = [
+                {
+                  names = ["client" "federation"];
+                  compress = false;
+                }
+              ];
+            }
+          ];
+        };
+
+        postgresql = {
+          ensureDatabases = [db];
+          ensureUsers = [
+            {
+              name = db;
+              ensurePermissions."DATABASE \"${db}\"" = "ALL";
+            }
+          ];
+        };
+      };
+    };
+}
diff --git a/modules/nixos/monitoring/dashboards/endlessh.json b/modules/nixos/monitoring/dashboards/endlessh.json
new file mode 100644
index 0000000..0b47ee2
--- /dev/null
+++ b/modules/nixos/monitoring/dashboards/endlessh.json
@@ -0,0 +1,1457 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "datasource",
+          "uid": "grafana"
+        },
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "target": {
+          "limit": 100,
+          "matchAny": false,
+          "tags": [],
+          "type": "dashboard"
+        },
+        "type": "dashboard"
+      }
+    ]
+  },
+  "description": "",
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "gnetId": 15156,
+  "graphTooltip": 0,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "datasource": {
+        "type": "datasource",
+        "uid": "-- Dashboard --"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 0,
+        "y": 0
+      },
+      "id": 36,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "/^Total number connections that endlessh trapped$/",
+          "values": false
+        },
+        "text": {},
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "datasource",
+            "uid": "-- Dashboard --"
+          },
+          "panelId": 49,
+          "refId": "A"
+        }
+      ],
+      "title": "Connections",
+      "transformations": [
+        {
+          "id": "filterByRefId",
+          "options": {
+            "include": "Seen"
+          }
+        },
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Value #Seen"
+              }
+            ],
+            "match": "any",
+            "type": "include"
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Value #Seen": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "ip": {
+                "aggregations": [],
+                "operation": "groupby"
+              }
+            }
+          }
+        },
+        {
+          "id": "reduce",
+          "options": {
+            "includeTimeField": false,
+            "labelsToFields": false,
+            "mode": "reduceFields",
+            "reducers": [
+              "sum"
+            ]
+          }
+        },
+        {
+          "id": "organize",
+          "options": {
+            "excludeByName": {},
+            "indexByName": {},
+            "renameByName": {
+              "Value #Seen (sum)": "Total number connections that endlessh trapped"
+            }
+          }
+        }
+      ],
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "datasource",
+        "uid": "-- Dashboard --"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          },
+          "unit": "s"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 4,
+        "y": 0
+      },
+      "id": 42,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "/^Time spent on endlessh$/",
+          "values": false
+        },
+        "text": {},
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "datasource",
+            "uid": "-- Dashboard --"
+          },
+          "panelId": 49,
+          "refId": "A"
+        }
+      ],
+      "title": "Trapped Time",
+      "transformations": [
+        {
+          "id": "filterByRefId",
+          "options": {
+            "include": "Trapped"
+          }
+        },
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Value #Trapped"
+              }
+            ],
+            "match": "all",
+            "type": "include"
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Value #Trapped": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "ip": {
+                "aggregations": [],
+                "operation": "groupby"
+              }
+            }
+          }
+        },
+        {
+          "id": "reduce",
+          "options": {
+            "includeTimeField": false,
+            "labelsToFields": false,
+            "mode": "reduceFields",
+            "reducers": [
+              "sum"
+            ]
+          }
+        },
+        {
+          "id": "organize",
+          "options": {
+            "excludeByName": {},
+            "indexByName": {},
+            "renameByName": {
+              "Total": "Seconds spent on endlessh",
+              "Value #Trapped (sum)": "Time spent on endlessh"
+            }
+          }
+        }
+      ],
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 8,
+        "y": 0
+      },
+      "id": 18,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "text": {},
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "exemplar": true,
+          "expr": "sum(increase(endlessh_sent_bytes_total{instance=~\"$host\",job=~\"$job\"}[$__range]))",
+          "hide": false,
+          "interval": "",
+          "legendFormat": "Bytes sent by endlessh",
+          "refId": "sent_bytes"
+        }
+      ],
+      "title": "Sent Bytes",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "datasource",
+        "uid": "-- Dashboard --"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 12,
+        "y": 0
+      },
+      "id": 38,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "/^Unique IPs connected$/",
+          "values": false
+        },
+        "text": {},
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "datasource",
+            "uid": "-- Dashboard --"
+          },
+          "panelId": 49,
+          "refId": "A"
+        }
+      ],
+      "title": "Unique IPs",
+      "transformations": [
+        {
+          "id": "filterByRefId",
+          "options": {
+            "include": "Seen"
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Value #Seen": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "ip": {
+                "aggregations": [],
+                "operation": "groupby"
+              }
+            }
+          }
+        },
+        {
+          "id": "reduce",
+          "options": {
+            "includeTimeField": false,
+            "labelsToFields": false,
+            "mode": "reduceFields",
+            "reducers": [
+              "count"
+            ]
+          }
+        },
+        {
+          "id": "organize",
+          "options": {
+            "excludeByName": {},
+            "indexByName": {},
+            "renameByName": {
+              "Value #Seen (sum)": "Unique IPs connected",
+              "ip": ""
+            }
+          }
+        }
+      ],
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "datasource",
+        "uid": "-- Dashboard --"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "links": [
+            {
+              "targetBlank": true,
+              "title": "whois",
+              "url": "https://search.arin.net/rdap/?query=${__value.text}"
+            }
+          ],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 16,
+        "y": 0
+      },
+      "id": 45,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "/^Client IP of the latest connection$/",
+          "values": false
+        },
+        "text": {},
+        "textMode": "value"
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "datasource",
+            "uid": "-- Dashboard --"
+          },
+          "panelId": 49,
+          "refId": "A"
+        }
+      ],
+      "title": "Latest Connection",
+      "transformations": [
+        {
+          "id": "filterByRefId",
+          "options": {
+            "include": "Seen"
+          }
+        },
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Value #Seen"
+              }
+            ],
+            "match": "any",
+            "type": "include"
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Time": {
+                "aggregations": [
+                  "lastNotNull"
+                ],
+                "operation": "aggregate"
+              },
+              "Value #Seen": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "ip": {
+                "aggregations": [],
+                "operation": "groupby"
+              }
+            }
+          }
+        },
+        {
+          "id": "sortBy",
+          "options": {
+            "fields": {},
+            "sort": [
+              {
+                "field": "Time (lastNotNull)"
+              }
+            ]
+          }
+        },
+        {
+          "id": "organize",
+          "options": {
+            "excludeByName": {
+              "instance": true,
+              "job": true
+            },
+            "indexByName": {},
+            "renameByName": {
+              "Time (lastNotNull)": "Time",
+              "Value #Seen (sum)": "Count",
+              "ip": "Client IP of the latest connection"
+            }
+          }
+        }
+      ],
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "#EAB839",
+                "value": 10
+              },
+              {
+                "color": "red",
+                "value": 20
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 0
+      },
+      "id": 20,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "text": {},
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "exemplar": true,
+          "expr": "sum((endlessh_client_open_count_total{instance=~\"$host\",job=~\"$job\"}) - (endlessh_client_closed_count_total{instance=~\"$host\",job=~\"$job\"} offset $__interval  or endlessh_client_open_count_total{instance=~\"$host\",job=~\"$job\"} * 0))",
+          "instant": false,
+          "interval": "",
+          "legendFormat": "Open Connections",
+          "refId": "current_open"
+        }
+      ],
+      "title": "Current Connections",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "datasource",
+        "uid": "-- Dashboard --"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "stepAfter",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "min": -0.01,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 12,
+        "x": 0,
+        "y": 3
+      },
+      "id": 30,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": false
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "datasource",
+            "uid": "-- Dashboard --"
+          },
+          "panelId": 20,
+          "refId": "A"
+        }
+      ],
+      "title": "Concurrent Connections",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "datasource",
+        "uid": "-- Dashboard --"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            }
+          },
+          "mappings": []
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 3
+      },
+      "id": 32,
+      "options": {
+        "displayLabels": [],
+        "legend": {
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true,
+          "values": [
+            "value",
+            "percent"
+          ]
+        },
+        "pieType": "pie",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "datasource",
+            "uid": "-- Dashboard --"
+          },
+          "panelId": 49,
+          "refId": "A"
+        }
+      ],
+      "title": "Connections by country",
+      "transformations": [
+        {
+          "id": "filterByRefId",
+          "options": {
+            "include": "Seen"
+          }
+        },
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Value #Seen"
+              }
+            ],
+            "match": "any",
+            "type": "include"
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Value #Seen": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "country": {
+                "aggregations": [
+                  "last"
+                ],
+                "operation": "aggregate"
+              },
+              "ip": {
+                "aggregations": [],
+                "operation": "groupby"
+              }
+            }
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Value #Seen (sum)": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "country (last)": {
+                "aggregations": [],
+                "operation": "groupby"
+              }
+            }
+          }
+        },
+        {
+          "id": "rowsToFields",
+          "options": {}
+        }
+      ],
+      "type": "piechart"
+    },
+    {
+      "datasource": {
+        "type": "datasource",
+        "uid": "-- Dashboard --"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "continuous-GrYlRd"
+          },
+          "custom": {
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "#96D98D",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 12,
+        "w": 12,
+        "x": 0,
+        "y": 7
+      },
+      "id": 48,
+      "links": [],
+      "options": {
+        "basemap": {
+          "config": {},
+          "name": "Layer 0",
+          "type": "default"
+        },
+        "controls": {
+          "mouseWheelZoom": false,
+          "showAttribution": false,
+          "showDebug": false,
+          "showScale": false,
+          "showZoom": true
+        },
+        "layers": [
+          {
+            "config": {
+              "color": {
+                "field": "Connections",
+                "fixed": "dark-green"
+              },
+              "fillOpacity": 0.4,
+              "shape": "circle",
+              "showLegend": false,
+              "size": {
+                "field": "Connections",
+                "fixed": 5,
+                "max": 10,
+                "min": 2
+              },
+              "style": {
+                "color": {
+                  "field": "Connections",
+                  "fixed": "dark-green"
+                },
+                "size": {
+                  "field": "Connections",
+                  "fixed": 5,
+                  "max": 9,
+                  "min": 2
+                },
+                "text": {
+                  "field": "location (lastNotNull) (lastNotNull)",
+                  "fixed": "",
+                  "mode": "fixed"
+                }
+              }
+            },
+            "location": {
+              "geohash": "Geohash",
+              "mode": "geohash"
+            },
+            "name": "Layer 1",
+            "type": "markers"
+          }
+        ],
+        "tooltip": {
+          "mode": "details"
+        },
+        "view": {
+          "id": "zero",
+          "lat": 0,
+          "lon": 0,
+          "zoom": 1
+        }
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "datasource",
+            "uid": "-- Dashboard --"
+          },
+          "panelId": 49,
+          "refId": "A"
+        }
+      ],
+      "title": "Locations",
+      "transformations": [
+        {
+          "id": "filterByRefId",
+          "options": {
+            "include": "Seen"
+          }
+        },
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Value #Seen"
+              }
+            ],
+            "match": "any",
+            "type": "include"
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Value #Seen": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "geohash": {
+                "aggregations": [
+                  "lastNotNull"
+                ],
+                "operation": "groupby"
+              },
+              "location": {
+                "aggregations": [
+                  "lastNotNull"
+                ],
+                "operation": "aggregate"
+              }
+            }
+          }
+        },
+        {
+          "id": "organize",
+          "options": {
+            "excludeByName": {},
+            "indexByName": {
+              "Value #geo (lastNotNull) (sum)": 2,
+              "geohash (lastNotNull)": 0,
+              "location (lastNotNull) (lastNotNull)": 1
+            },
+            "renameByName": {
+              "Value #Seen (sum)": "Connections",
+              "geohash": "Geohash",
+              "location (lastNotNull)": "Location"
+            }
+          }
+        }
+      ],
+      "type": "geomap"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "custom": {
+            "align": "auto",
+            "displayMode": "auto",
+            "filterable": true,
+            "inspect": false,
+            "minWidth": 50
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Last Connection"
+            },
+            "properties": [
+              {
+                "id": "custom.minWidth",
+                "value": 150
+              },
+              {
+                "id": "unit",
+                "value": "dateTimeAsIso"
+              },
+              {
+                "id": "custom.align",
+                "value": "left"
+              }
+            ]
+          },
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "IP"
+            },
+            "properties": [
+              {
+                "id": "links",
+                "value": [
+                  {
+                    "targetBlank": true,
+                    "title": "search ARIN",
+                    "url": "https://search.arin.net/rdap/?query=${__data.fields.IP}"
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Trapped Time"
+            },
+            "properties": [
+              {
+                "id": "unit",
+                "value": "s"
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 11
+      },
+      "id": 49,
+      "options": {
+        "footer": {
+          "fields": "",
+          "reducer": [
+            "sum"
+          ],
+          "show": false
+        },
+        "frameIndex": 0,
+        "showHeader": true,
+        "sortBy": [
+          {
+            "desc": true,
+            "displayName": "Last Connection"
+          }
+        ]
+      },
+      "pluginVersion": "9.1.2",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "exemplar": true,
+          "expr": "(endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"} - endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"} offset $__interval) > 0 or (endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"}!=0 unless endlessh_client_open_count{instance=~\"$host\",job=~\"$job\"} offset $__interval)",
+          "format": "table",
+          "hide": false,
+          "interval": "",
+          "legendFormat": "Seen {{ip}}",
+          "refId": "Seen"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "exemplar": true,
+          "expr": "(endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"} - endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"} offset $__interval) > 0 or (endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"}!=0 unless endlessh_client_trapped_time_seconds{instance=~\"$host\",job=~\"$job\"} offset $__interval)",
+          "format": "table",
+          "hide": false,
+          "interval": "",
+          "legendFormat": "Trapped {{ip}}",
+          "refId": "Trapped"
+        }
+      ],
+      "title": "Clients",
+      "transformations": [
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Value #Seen"
+              },
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Value #Trapped"
+              }
+            ],
+            "match": "any",
+            "type": "include"
+          }
+        },
+        {
+          "id": "merge",
+          "options": {}
+        },
+        {
+          "id": "calculateField",
+          "options": {
+            "alias": "Seen",
+            "mode": "reduceRow",
+            "reduce": {
+              "include": [
+                "Value #Seen"
+              ],
+              "reducer": "sum"
+            }
+          }
+        },
+        {
+          "id": "calculateField",
+          "options": {
+            "alias": "Trapped",
+            "mode": "reduceRow",
+            "reduce": {
+              "include": [
+                "Value #Trapped"
+              ],
+              "reducer": "sum"
+            }
+          }
+        },
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Seen"
+              },
+              {
+                "config": {
+                  "id": "greaterOrEqual",
+                  "options": {
+                    "value": 0
+                  }
+                },
+                "fieldName": "Trapped"
+              }
+            ],
+            "match": "all",
+            "type": "include"
+          }
+        },
+        {
+          "id": "groupBy",
+          "options": {
+            "fields": {
+              "Seen": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "Time": {
+                "aggregations": [
+                  "max"
+                ],
+                "operation": "aggregate"
+              },
+              "Trapped": {
+                "aggregations": [
+                  "sum"
+                ],
+                "operation": "aggregate"
+              },
+              "country": {
+                "aggregations": [
+                  "lastNotNull"
+                ],
+                "operation": "aggregate"
+              },
+              "ip": {
+                "aggregations": [],
+                "operation": "groupby"
+              }
+            }
+          }
+        },
+        {
+          "id": "filterByValue",
+          "options": {
+            "filters": [
+              {
+                "config": {
+                  "id": "isNull",
+                  "options": {}
+                },
+                "fieldName": "ip"
+              }
+            ],
+            "match": "any",
+            "type": "exclude"
+          }
+        },
+        {
+          "id": "organize",
+          "options": {
+            "excludeByName": {},
+            "indexByName": {
+              "Seen (sum)": 3,
+              "Time (max)": 0,
+              "Trapped (sum)": 4,
+              "country (lastNotNull)": 2,
+              "ip": 1
+            },
+            "renameByName": {
+              "Seen (sum)": "Connections",
+              "Time (max)": "Last Connection",
+              "Trapped (sum)": "Trapped Time",
+              "country (lastNotNull)": "Country",
+              "ip": "IP"
+            }
+          }
+        }
+      ],
+      "type": "table"
+    }
+  ],
+  "refresh": "",
+  "schemaVersion": 37,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "current": {
+          "selected": false,
+          "text": "default",
+          "value": "default"
+        },
+        "hide": 0,
+        "includeAll": false,
+        "label": "datasource",
+        "multi": false,
+        "name": "DS_PROMETHEUS",
+        "options": [],
+        "query": "prometheus",
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "type": "datasource"
+      },
+      {
+        "allValue": ".*",
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "${DS_PROMETHEUS}"
+        },
+        "definition": "label_values(endlessh_client_open_count_total, job)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "Job",
+        "multi": true,
+        "name": "job",
+        "options": [],
+        "query": {
+          "query": "label_values(endlessh_client_open_count_total, job)",
+          "refId": "StandardVariableQuery"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "type": "query"
+      },
+      {
+        "allValue": ".*",
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "${DS_PROMETHEUS}"
+        },
+        "definition": "label_values(endlessh_client_open_count_total{job=~\"$job\"}, instance)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "Host",
+        "multi": true,
+        "name": "host",
+        "options": [],
+        "query": {
+          "query": "label_values(endlessh_client_open_count_total{job=~\"$job\"}, instance)",
+          "refId": "StandardVariableQuery"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "type": "query"
+      }
+    ]
+  },
+  "time": {
+    "from": "now-24h",
+    "to": "now"
+  },
+  "timepicker": {},
+  "timezone": "",
+  "title": "Endlessh",
+  "uid": "ATIxYkO7k",
+  "version": 1,
+  "weekStart": ""
+}
\ No newline at end of file
diff --git a/modules/nixos/monitoring/dashboards/nginx.json b/modules/nixos/monitoring/dashboards/nginx.json
new file mode 100644
index 0000000..b2cc499
--- /dev/null
+++ b/modules/nixos/monitoring/dashboards/nginx.json
@@ -0,0 +1,567 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "datasource",
+          "uid": "grafana"
+        },
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "target": {
+          "limit": 100,
+          "matchAny": false,
+          "tags": [],
+          "type": "dashboard"
+        },
+        "type": "dashboard"
+      }
+    ]
+  },
+  "description": "",
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "graphTooltip": 0,
+  "id": 31,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 0
+      },
+      "id": 4,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Status",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "0": {
+                  "text": "Down"
+                },
+                "1": {
+                  "text": "Up"
+                }
+              },
+              "type": "value"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "#E02F44",
+                "value": null
+              },
+              {
+                "color": "#FF9830",
+                "value": 1
+              },
+              {
+                "color": "#299c46",
+                "value": 1
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 12,
+        "x": 0,
+        "y": 1
+      },
+      "id": 8,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "background",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "repeat": "instance",
+      "repeatDirection": "h",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "nginx_up{instance=~\"$instance\"}",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "NGINX Status for $instance",
+      "type": "stat"
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 4
+      },
+      "id": 6,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Metrics",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "Connections (rate)",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "decimals": 1,
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 10,
+        "w": 12,
+        "x": 0,
+        "y": 5
+      },
+      "id": 10,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "irate(nginx_connections_accepted{instance=~\"$instance\"}[$__range])",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}} accepted",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "irate(nginx_connections_handled{instance=~\"$instance\"}[$__range])",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}} handled",
+          "refId": "B"
+        }
+      ],
+      "title": "Processed connections",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "Connections",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "decimals": 0,
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 10,
+        "w": 12,
+        "x": 12,
+        "y": 5
+      },
+      "id": 12,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "nginx_connections_active{instance=~\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}} active",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "nginx_connections_reading{instance=~\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}} reading",
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "nginx_connections_waiting{instance=~\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}} waiting",
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "nginx_connections_writing{instance=~\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}} writing",
+          "refId": "D"
+        }
+      ],
+      "title": "Active Connections",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 24,
+        "x": 0,
+        "y": 15
+      },
+      "id": 15,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "irate(nginx_http_requests_total{instance=~\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}} total requests",
+          "refId": "A"
+        }
+      ],
+      "title": "Total requests",
+      "type": "timeseries"
+    }
+  ],
+  "refresh": "5s",
+  "schemaVersion": 37,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "current": {
+          "selected": false,
+          "text": "default",
+          "value": "default"
+        },
+        "hide": 0,
+        "includeAll": false,
+        "label": "datasource",
+        "multi": false,
+        "name": "DS_PROMETHEUS",
+        "options": [],
+        "query": "prometheus",
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "type": "datasource"
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": {
+          "uid": "${DS_PROMETHEUS}"
+        },
+        "definition": "label_values(nginx_up, instance)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "",
+        "multi": true,
+        "name": "instance",
+        "options": [],
+        "query": {
+          "query": "label_values(nginx_up, instance)",
+          "refId": "StandardVariableQuery"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 0,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+  },
+  "timezone": "",
+  "title": "NGINX",
+  "uid": "Yoeroupho",
+  "version": 1,
+  "weekStart": ""
+}
diff --git a/modules/nixos/monitoring/dashboards/postgresql.json b/modules/nixos/monitoring/dashboards/postgresql.json
new file mode 100644
index 0000000..4e533f7
--- /dev/null
+++ b/modules/nixos/monitoring/dashboards/postgresql.json
@@ -0,0 +1,3086 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "datasource",
+          "uid": "grafana"
+        },
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "target": {
+          "limit": 100,
+          "matchAny": false,
+          "tags": [],
+          "type": "dashboard"
+        },
+        "type": "dashboard"
+      }
+    ]
+  },
+  "description": "",
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "graphTooltip": 0,
+  "id": 29,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 0
+      },
+      "id": 34,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "General Counters, CPU, Memory and File Descriptor Stats",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 2,
+        "w": 4,
+        "x": 0,
+        "y": 1
+      },
+      "id": 36,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["mean"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "name"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "pg_static{instance=\"$instance\"}",
+          "format": "time_series",
+          "instant": true,
+          "intervalFactor": 1,
+          "legendFormat": "{{short_version}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Version",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "start time of the process",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "dateTimeFromNow"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 2,
+        "w": 4,
+        "x": 4,
+        "y": 1
+      },
+      "id": 28,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["mean"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "pg_postmaster_start_time_seconds{instance=\"$instance\"} * 1000",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Start Time",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "decbytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 2,
+        "w": 4,
+        "x": 8,
+        "y": 1
+      },
+      "id": 10,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "SUM(pg_stat_database_tup_fetched{datname=~\"$datname\", instance=~\"$instance\"})",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "refId": "A",
+          "step": 4
+        }
+      ],
+      "title": "Current fetch data",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "decbytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 2,
+        "w": 4,
+        "x": 12,
+        "y": 1
+      },
+      "id": 11,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "SUM(pg_stat_database_tup_inserted{datname=~\"$datname\", instance=~\"$instance\"})",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "range": true,
+          "refId": "A",
+          "step": 4
+        }
+      ],
+      "title": "Current insert data",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "decbytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 2,
+        "w": 4,
+        "x": 16,
+        "y": 1
+      },
+      "id": 12,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "SUM(pg_stat_database_tup_updated{datname=~\"$datname\", instance=~\"$instance\"})",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "refId": "A",
+          "step": 4
+        }
+      ],
+      "title": "Current update data",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 2,
+        "w": 4,
+        "x": 20,
+        "y": 1
+      },
+      "id": 38,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["mean"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "pg_settings_max_connections{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Max Connections",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Average user and system CPU time spent in seconds.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "s"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 3
+      },
+      "id": 22,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "max", "min"],
+          "displayMode": "table",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "avg(rate(process_cpu_seconds_total{instance=\"$instance\"}[$__range]) * 1000)",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "CPU Time",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Average CPU Usage",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Virtual and Resident memory size in bytes, averages over 5 min interval",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "decbytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 3
+      },
+      "id": 24,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "max", "min"],
+          "displayMode": "table",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "avg(rate(process_resident_memory_bytes{instance=\"$instance\"}[$__range]))",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "Resident Mem",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "avg(rate(process_virtual_memory_bytes{instance=\"$instance\"}[$__range]))",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "Virtual Mem",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Average Memory Usage",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Number of open file descriptors",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 16,
+        "y": 3
+      },
+      "id": 26,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "max", "min"],
+          "displayMode": "table",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "process_open_fds{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "Open FD",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Open File Descriptors",
+      "type": "timeseries"
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 10
+      },
+      "id": 32,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Settings",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 0,
+        "y": 11
+      },
+      "id": 40,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_shared_buffers_bytes{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Shared Buffers",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 3,
+        "y": 11
+      },
+      "id": 42,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_effective_cache_size_bytes{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Effective Cache",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 6,
+        "y": 11
+      },
+      "id": 44,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_maintenance_work_mem_bytes{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Maintenance Work Mem",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 9,
+        "y": 11
+      },
+      "id": 46,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_work_mem_bytes{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "",
+          "refId": "A"
+        }
+      ],
+      "title": "Work Mem",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "decimals": 1,
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 12,
+        "y": 11
+      },
+      "id": 48,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_max_wal_size_bytes{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Max WAL Size",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 15,
+        "y": 11
+      },
+      "id": 50,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_random_page_cost{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Random Page Cost",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 2,
+        "x": 18,
+        "y": 11
+      },
+      "id": 52,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_seq_page_cost",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Seq Page Cost",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 2,
+        "x": 20,
+        "y": 11
+      },
+      "id": 54,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["mean"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_max_worker_processes{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Max Worker Processes",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 2,
+        "x": 22,
+        "y": 11
+      },
+      "id": 56,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_settings_max_parallel_workers{instance=\"$instance\"}",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "refId": "A"
+        }
+      ],
+      "title": "Max Parallel Workers",
+      "type": "stat"
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 14
+      },
+      "id": 30,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Database Stats",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineStyle": {
+              "fill": "solid"
+            },
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": true,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "decimals": 0,
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 15
+      },
+      "id": 1,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "max"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "pg_stat_activity_count{datname=~\"$datname\", instance=~\"$instance\", state=\"active\"} != 0",
+          "format": "time_series",
+          "interval": "",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}}, s: {{state}}",
+          "range": true,
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Active sessions",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 15
+      },
+      "id": 60,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "irate(pg_stat_database_xact_commit{instance=\"$instance\", datname=~\"$datname\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{datname}} commits",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "irate(pg_stat_database_xact_rollback{instance=\"$instance\", datname=~\"$datname\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{datname}} rollbacks",
+          "refId": "B"
+        }
+      ],
+      "title": "Transactions",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 16,
+        "y": 15
+      },
+      "id": 8,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_stat_database_tup_updated{datname=~\"$datname\", instance=~\"$instance\"} != 0",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}}",
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Update data",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 22
+      },
+      "id": 5,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_stat_database_tup_fetched{datname=~\"$datname\", instance=~\"$instance\"} != 0",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}}",
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Fetch data",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 22
+      },
+      "id": 6,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_stat_database_tup_inserted{datname=~\"$datname\", instance=~\"$instance\"} != 0",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}}",
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Insert data",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "decimals": 0,
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 16,
+        "y": 22
+      },
+      "id": 3,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_locks_count{datname=~\"$datname\", instance=~\"$instance\", mode=~\"$mode\"} != 0",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}},{{mode}}",
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Lock tables",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 29
+      },
+      "id": 14,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_stat_database_tup_returned{datname=~\"$datname\", instance=~\"$instance\"} != 0",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}}",
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Return data",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 29
+      },
+      "id": 4,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["lastNotNull", "max"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_stat_activity_count{datname=~\"$datname\", instance=~\"$instance\", state=~\"idle|idle in transaction|idle in transaction (aborted)\"}",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}}, s: {{state}}",
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Idle sessions",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 16,
+        "y": 29
+      },
+      "id": 7,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_stat_database_tup_deleted{datname=~\"$datname\", instance=~\"$instance\"} != 0",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "legendFormat": "{{datname}}",
+          "refId": "A",
+          "step": 2
+        }
+      ],
+      "title": "Delete data",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "decimals": 4,
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "percentunit"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 36
+      },
+      "id": 62,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "pg_stat_database_blks_hit{instance=\"$instance\", datname=~\"$datname\"} / (pg_stat_database_blks_read{instance=\"$instance\", datname=~\"$datname\"} + pg_stat_database_blks_hit{instance=\"$instance\", datname=~\"$datname\"})",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{ datname }}",
+          "refId": "A"
+        }
+      ],
+      "title": "Cache Hit Rate",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 8,
+        "y": 36
+      },
+      "id": 64,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "max", "min"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_bgwriter_buffers_backend_total{instance=\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "buffers_backend",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_bgwriter_buffers_alloc_total{instance=\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "buffers_alloc",
+          "range": true,
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_bgwriter_buffers_backend_fsync_total{instance=\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "backend_fsync",
+          "range": true,
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_bgwriter_buffers_checkpoint_total{instance=\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "buffers_checkpoint",
+          "range": true,
+          "refId": "D"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_bgwriter_buffers_clean_total{instance=\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "buffers_clean",
+          "range": true,
+          "refId": "E"
+        }
+      ],
+      "title": "Buffers (bgwriter)",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 16,
+        "y": 36
+      },
+      "id": 66,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "irate(pg_stat_database_conflicts{instance=\"$instance\", datname=~\"$datname\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{datname}} conflicts",
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "irate(pg_stat_database_deadlocks{instance=\"$instance\", datname=~\"$datname\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{datname}} deadlocks",
+          "refId": "A"
+        }
+      ],
+      "title": "Conflicts/Deadlocks",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total amount of data written to temporary files by queries in this database. All temporary files are counted, regardless of why the temporary file was created, and regardless of the log_temp_files setting.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 0,
+        "y": 43
+      },
+      "id": 68,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "sum"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_database_temp_bytes{instance=\"$instance\", datname=~\"$datname\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{datname}}",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Temp File (Bytes)",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "ms"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 16,
+        "x": 8,
+        "y": 43
+      },
+      "id": 70,
+      "links": [],
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull", "max", "min"],
+          "displayMode": "table",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_bgwriter_checkpoint_write_time_total{instance=\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "write_time - Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk.",
+          "range": true,
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "irate(pg_stat_bgwriter_checkpoint_sync_time_total{instance=\"$instance\"}[$__range])",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "sync_time - Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk.",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Checkpoint Stats",
+      "type": "timeseries"
+    }
+  ],
+  "refresh": "5s",
+  "schemaVersion": 37,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "current": {
+          "selected": false,
+          "text": "Prometheus",
+          "value": "Prometheus"
+        },
+        "hide": 0,
+        "includeAll": false,
+        "label": "datasource",
+        "multi": false,
+        "name": "DS_PROMETHEUS",
+        "options": [],
+        "query": "prometheus",
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "type": "datasource"
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "manwe:9187",
+          "value": "manwe:9187"
+        },
+        "datasource": {
+          "uid": "${DS_PROMETHEUS}"
+        },
+        "definition": "query_result(pg_up)",
+        "hide": 0,
+        "includeAll": false,
+        "label": "Instance",
+        "multi": false,
+        "name": "instance",
+        "options": [],
+        "query": {
+          "query": "query_result(pg_up)",
+          "refId": "StandardVariableQuery"
+        },
+        "refresh": 1,
+        "regex": "/.*instance=\"([^\"]+).*/",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": {
+          "uid": "${DS_PROMETHEUS}"
+        },
+        "definition": "label_values(datname)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "Database",
+        "multi": true,
+        "name": "datname",
+        "options": [],
+        "query": {
+          "query": "label_values(datname)",
+          "refId": "StandardVariableQuery"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 1,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "All",
+          "value": "$__all"
+        },
+        "datasource": {
+          "uid": "${DS_PROMETHEUS}"
+        },
+        "definition": "label_values({mode=~\"accessexclusivelock|accesssharelock|exclusivelock|rowexclusivelock|rowsharelock|sharelock|sharerowexclusivelock|shareupdateexclusivelock\"}, mode)",
+        "hide": 0,
+        "includeAll": true,
+        "label": "Lock table",
+        "multi": true,
+        "name": "mode",
+        "options": [],
+        "query": {
+          "query": "label_values({mode=~\"accessexclusivelock|accesssharelock|exclusivelock|rowexclusivelock|rowsharelock|sharelock|sharerowexclusivelock|shareupdateexclusivelock\"}, mode)",
+          "refId": "StandardVariableQuery"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 0,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+  },
+  "timezone": "",
+  "title": "PostgreSQL",
+  "uid": "fiuGhahwi",
+  "version": 1,
+  "weekStart": ""
+}
diff --git a/modules/nixos/monitoring/dashboards/unbound.json b/modules/nixos/monitoring/dashboards/unbound.json
new file mode 100644
index 0000000..8a0d503
--- /dev/null
+++ b/modules/nixos/monitoring/dashboards/unbound.json
@@ -0,0 +1,2991 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "datasource",
+          "uid": "grafana"
+        },
+        "enable": true,
+        "hide": true,
+        "iconColor": "rgba(0, 211, 255, 1)",
+        "name": "Annotations & Alerts",
+        "target": {
+          "limit": 100,
+          "matchAny": false,
+          "tags": [],
+          "type": "dashboard"
+        },
+        "type": "dashboard"
+      }
+    ]
+  },
+  "description": "",
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "gnetId": 11705,
+  "graphTooltip": 0,
+  "id": 39,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries received at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 0,
+        "y": 0
+      },
+      "id": 24,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(increase(unbound_queries_total{instance=\"$instance\"}[$__range]))",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 4,
+        "y": 0
+      },
+      "id": 26,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "sum(rate(unbound_queries_total{instance=\"$instance\"}[$__range]))",
+          "refId": "A"
+        }
+      ],
+      "title": "Queries per second",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were refused or dropped because they failed the access control settings at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 8,
+        "y": 0
+      },
+      "id": 8,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "increase(unbound_unwanted_queries_total{instance=\"$instance\"}[$__range])",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Unwanted queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of replies that were unwanted or unsolicited  at the selected time range.\n\nA sharp increase in unwanted traffic indicates a possible spoof run in progress.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 12,
+        "y": 0
+      },
+      "id": 49,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_unwanted_replies_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "Unwanted replies",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Max memory in use by caches at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 16,
+        "y": 0
+      },
+      "id": 48,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(rate(unbound_memory_caches_bytes{instance=\"$instance\"}[$__range]))",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Memory cache size",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "The number of threads created to serve clients",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 0
+      },
+      "id": 2,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "unbound_num_threads{instance=\"$instance\"}",
+          "format": "time_series",
+          "instant": false,
+          "refId": "A"
+        }
+      ],
+      "title": "Threads",
+      "type": "stat"
+    },
+    {
+      "cards": {
+        "cardPadding": 0
+      },
+      "color": {
+        "cardColor": "#3274D9",
+        "colorScale": "sqrt",
+        "colorScheme": "interpolateSpectral",
+        "exponent": 0.5,
+        "mode": "opacity"
+      },
+      "dataFormat": "timeseries",
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Query response time in seconds",
+      "fieldConfig": {
+        "defaults": {
+          "custom": {
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "scaleDistribution": {
+              "type": "linear"
+            }
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 16,
+        "x": 0,
+        "y": 3
+      },
+      "heatmap": {},
+      "hideZeroBuckets": false,
+      "highlightCards": true,
+      "id": 46,
+      "legend": {
+        "show": false
+      },
+      "options": {
+        "calculate": true,
+        "calculation": {},
+        "cellGap": 1,
+        "cellValues": {},
+        "color": {
+          "exponent": 0.5,
+          "fill": "#3274D9",
+          "mode": "opacity",
+          "scale": "exponential",
+          "scheme": "Oranges",
+          "steps": 128
+        },
+        "exemplars": {
+          "color": "rgba(255,0,255,0.7)"
+        },
+        "filterValues": {
+          "le": 1e-9
+        },
+        "legend": {
+          "show": false
+        },
+        "rowsFrame": {
+          "layout": "ge"
+        },
+        "showValue": "never",
+        "tooltip": {
+          "show": true,
+          "yHistogram": true
+        },
+        "yAxis": {
+          "axisPlacement": "left",
+          "decimals": 0,
+          "min": "0",
+          "reverse": false,
+          "unit": "s"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "reverseYBuckets": false,
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "rate(unbound_response_time_seconds_bucket{instance=\"$instance\", le=~\"0.001024|0.002048|0.004096|0.008192|0.016384|0.032768|0.065536|0.131072|0.262144|0.524288|1|2|4|8|16|32|64\"}[$__range])",
+          "format": "heatmap",
+          "instant": false,
+          "intervalFactor": 1,
+          "legendFormat": "{{le}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Response time",
+      "tooltip": {
+        "show": true,
+        "showHistogram": true
+      },
+      "type": "heatmap",
+      "xAxis": {
+        "show": true
+      },
+      "yAxis": {
+        "decimals": 0,
+        "format": "s",
+        "logBase": 1,
+        "min": "0",
+        "show": true
+      },
+      "yBucketBound": "upper"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            }
+          },
+          "mappings": []
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 8,
+        "x": 16,
+        "y": 3
+      },
+      "id": 37,
+      "links": [],
+      "maxDataPoints": 3,
+      "options": {
+        "displayLabels": ["value"],
+        "legend": {
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "pieType": "pie",
+        "reduceOptions": {
+          "calcs": ["lastNotNull"],
+          "fields": "",
+          "values": false
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "8.3.6",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(increase(unbound_cache_hits_total{instance=\"$instance\"}[$__range]))",
+          "legendFormat": "Hits",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "exemplar": true,
+          "expr": "sum(increase(unbound_cache_misses_total{instance=\"$instance\"}[$__range]))",
+          "format": "time_series",
+          "interval": "",
+          "legendFormat": "Misses",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Cache hit/miss ratio",
+      "type": "piechart"
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 10
+      },
+      "id": 12,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Queries",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were made using TCP towards the Unbound server at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 0,
+        "y": 11
+      },
+      "id": 16,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_query_tcp_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "Incoming TCP queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were made using TCP towards the Unbound server  at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 5,
+        "y": 11
+      },
+      "id": 22,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_query_tcp_out_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "Outgoing TCP queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that had been rate limited at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 10,
+        "y": 11
+      },
+      "id": 50,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_query_ratelimited_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "Rate limited queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were made using TLS towards the Unbound server at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 14,
+        "y": 11
+      },
+      "id": 18,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_query_tls_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "Incoming TLS queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were made using TLS resumption at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 19,
+        "y": 11
+      },
+      "id": 19,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_query_tls_resume_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "TLS resumption queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were made using IPv6 towards the Unbound server at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 0,
+        "y": 14
+      },
+      "id": 20,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "increase(unbound_query_ipv6_total{instance=\"$instance\"}[$__range])",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "IPv6 queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total amount of answers that were secure (AD) at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 5,
+        "y": 14
+      },
+      "id": 51,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_answers_secure_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "Secure answers",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total amount of answers that were bogus (withheld as SERVFAIL) at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 10,
+        "y": 14
+      },
+      "id": 52,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "increase(unbound_answers_bogus_total{instance=\"$instance\"}[$__range])",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Bogus answers",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that had an EDNS OPT record present at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 14,
+        "y": 14
+      },
+      "id": 17,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_query_edns_present_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "EDNS OPT queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that had an EDNS OPT record with the DO (DNSSEC OK) bit set present at the selected time range.",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "fixedColor": "rgb(31, 120, 193)",
+            "mode": "fixed"
+          },
+          "mappings": [
+            {
+              "options": {
+                "match": "null",
+                "result": {
+                  "text": "N/A"
+                }
+              },
+              "type": "special"
+            }
+          ],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 5,
+        "x": 19,
+        "y": 14
+      },
+      "id": 21,
+      "links": [],
+      "maxDataPoints": 100,
+      "options": {
+        "colorMode": "none",
+        "graphMode": "area",
+        "justifyMode": "auto",
+        "orientation": "horizontal",
+        "reduceOptions": {
+          "calcs": ["sum"],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "increase(unbound_query_edns_DO_total{instance=\"$instance\"}[$__range])",
+          "refId": "A"
+        }
+      ],
+      "title": "EDNS OPT + DNSSEC OK queries",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries with a given query type",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 17
+      },
+      "id": 35,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_query_types_total{instance=\"$instance\"}[$__range])",
+          "legendFormat": "{{type}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Total queries by type",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of answers by rcode",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "NXDOMAIN"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "dark-orange",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          },
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "SERVFAIL"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "dark-red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 17
+      },
+      "id": 40,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_answer_rcodes_total{instance=\"$instance\"}[$__range])",
+          "legendFormat": "{{rcode}}",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_answers_secure_total{instance=\"$instance\"}[$__range])",
+          "legendFormat": "Secure (AD)",
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_answers_bogus_total{instance=\"$instance\"}[$__range])",
+          "legendFormat": "Bogus (SERVFAIL)",
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_rrset_bogus_total{instance=\"$instance\"}[$__range])",
+          "legendFormat": "RRset bogus",
+          "refId": "D"
+        }
+      ],
+      "title": "Total answers by rcode",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries with a given query class",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 25
+      },
+      "id": 38,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_query_classes_total{instance=\"$instance\"}[$__range])",
+          "legendFormat": "{{class}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Total queries by class",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries with a given query opcode",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 25
+      },
+      "id": 39,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_query_opcodes_total{instance=\"$instance\"}[$__range])",
+          "legendFormat": "{{opcode}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Total queries by opcode",
+      "type": "timeseries"
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 33
+      },
+      "id": 54,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Request list",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Current size of the request list, including internally generated queries",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 24,
+        "x": 0,
+        "y": 34
+      },
+      "id": 55,
+      "options": {
+        "legend": {
+          "calcs": ["lastNotNull"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "sum(rate(unbound_request_list_current_all{instance=\"$instance\"}[$__range]))",
+          "legendFormat": "all",
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "sum(rate(unbound_request_list_current_user{instance=\"$instance\"}[$__range]))",
+          "legendFormat": "user",
+          "refId": "B"
+        }
+      ],
+      "title": "Request list size",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Number of queries that were dropped because the request list was full",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 42
+      },
+      "id": 56,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(rate(unbound_request_list_exceeded_total{instance=\"$instance\"}[$__range]))",
+          "legendFormat": "",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Total exceeded queries",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of requests in the request list that were overwritten by newer entries",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 42
+      },
+      "id": 57,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(rate(unbound_request_list_overwritten_total{instance=\"$instance\"}[$__range]))",
+          "legendFormat": "",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Total overwritten queries",
+      "type": "timeseries"
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 50
+      },
+      "id": 30,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "DNSCrypt",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were encrypted and successfully decapsulated by dnscrypt",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 51
+      },
+      "id": 28,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": false
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(rate(unbound_dnscrypt_valid_queries_total{instance=\"$instance\"}[$__range]))",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Total successful queries",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries that were requesting dnscrypt certificates",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 51
+      },
+      "id": 31,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": false
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "sum(irate(unbound_dnscrypt_cert_queries_total{instance=\"$instance\"}[$__range]))",
+          "refId": "A"
+        }
+      ],
+      "title": "Total certificate requesting queries",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of queries received on dnscrypt port that were cleartext and not a request for certificates",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 59
+      },
+      "id": 32,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": false
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "sum(irate(unbound_dnscrypt_cleartext_queries_total{instance=\"$instance\"}[$__range]))",
+          "refId": "A"
+        }
+      ],
+      "title": "Total received cleartext queries",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Total number of requests that were neither cleartext, not valid dnscrypt messages",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 59
+      },
+      "id": 33,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": false
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "sum(irate(unbound_dnscrypt_malformed_queries_total{instance=\"$instance\"}[$__range]))",
+          "refId": "A"
+        }
+      ],
+      "title": "Total malformed queries",
+      "type": "timeseries"
+    },
+    {
+      "collapsed": false,
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 67
+      },
+      "id": 43,
+      "panels": [],
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "refId": "A"
+        }
+      ],
+      "title": "Cache",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Hits: total number of queries that were successfully answered using a cache lookup.\n\nMisses: total number of cache queries that needed recursive processing",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "min": 0,
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "ops"
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "hits"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "#73BF69",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          },
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "misses"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "#F2495C",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 24,
+        "x": 0,
+        "y": 68
+      },
+      "id": 60,
+      "options": {
+        "legend": {
+          "calcs": ["mean", "lastNotNull"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "editorMode": "code",
+          "expr": "sum(irate(unbound_cache_hits_total{instance=\"$instance\"}[$__range]))",
+          "legendFormat": "hits",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "sum(irate(unbound_cache_misses_total{instance=\"$instance\"}[$__range]))",
+          "legendFormat": "misses",
+          "refId": "B"
+        }
+      ],
+      "title": "Cache hits / misses",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "Memory in bytes by caches",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 75
+      },
+      "id": 6,
+      "options": {
+        "legend": {
+          "calcs": ["lastNotNull"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "rate(unbound_memory_caches_bytes{instance=\"$instance\"}[$__range])",
+          "legendFormat": "{{cache}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Cache size",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "description": "The number of cached entries",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": 0,
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "never",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "links": [],
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "short"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 75
+      },
+      "id": 41,
+      "options": {
+        "legend": {
+          "calcs": ["lastNotNull"],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "multi",
+          "sort": "none"
+        }
+      },
+      "pluginVersion": "9.1.0",
+      "targets": [
+        {
+          "datasource": {
+            "uid": "${DS_PROMETHEUS}"
+          },
+          "expr": "unbound_cache_count_total{instance=\"$instance\"}",
+          "legendFormat": "{{type}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Cached entries",
+      "type": "timeseries"
+    }
+  ],
+  "refresh": "5s",
+  "schemaVersion": 37,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "current": {
+          "selected": false,
+          "text": "manwe:9167",
+          "value": "manwe:9167"
+        },
+        "datasource": {
+          "uid": "${DS_PROMETHEUS}"
+        },
+        "definition": "label_values(unbound_up, instance)",
+        "hide": 0,
+        "includeAll": false,
+        "multi": false,
+        "name": "instance",
+        "options": [],
+        "query": {
+          "query": "label_values(unbound_up, instance)",
+          "refId": "Prometheus-instance-Variable-Query"
+        },
+        "refresh": 2,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 0,
+        "tagValuesQuery": "",
+        "tagsQuery": "",
+        "type": "query",
+        "useTags": false
+      },
+      {
+        "current": {
+          "selected": false,
+          "text": "Prometheus",
+          "value": "Prometheus"
+        },
+        "hide": 0,
+        "includeAll": false,
+        "label": "datasource",
+        "multi": false,
+        "name": "DS_PROMETHEUS",
+        "options": [],
+        "query": "prometheus",
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "type": "datasource"
+      }
+    ]
+  },
+  "time": {
+    "from": "now-1h",
+    "to": "now"
+  },
+  "timepicker": {
+    "refresh_intervals": [
+      "5s",
+      "10s",
+      "30s",
+      "1m",
+      "5m",
+      "15m",
+      "30m",
+      "1h",
+      "2h",
+      "1d"
+    ],
+    "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
+  },
+  "timezone": "",
+  "title": "Unbound",
+  "uid": "Eighooghi",
+  "version": 1,
+  "weekStart": ""
+}
diff --git a/modules/nixos/monitoring/default.nix b/modules/nixos/monitoring/default.nix
new file mode 100644
index 0000000..4ff4c50
--- /dev/null
+++ b/modules/nixos/monitoring/default.nix
@@ -0,0 +1,176 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.monitoring;
+in {
+  options.nixfiles.modules.monitoring.enable = mkEnableOption ''
+    a custom monitoring stack bas on the Grafana Labs toolkit
+  '';
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      grafana.enable = true;
+      loki.enable = true;
+      prometheus.enable = true;
+      alertmanager.enable = true;
+    };
+
+    services = {
+      grafana.provision = {
+        enable = true;
+
+        # https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources
+        datasources.settings.datasources = with config.nixfiles.modules; [
+          {
+            name = "Prometheus";
+            type = "prometheus";
+            access = "proxy";
+            url = "https://${prometheus.domain}";
+            isDefault = true;
+          }
+          {
+            name = "Loki";
+            type = "loki";
+            access = "proxy";
+            url = "https://${loki.domain}";
+          }
+        ];
+
+        # https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards
+        dashboards.settings.providers = [
+          # System dashboard is imported manually from here[1]. Too bad
+          # provisioned dashboards cannot properly integrate dynamic datasources
+          # yet.
+          #
+          # [1]: https://grafana.com/grafana/dashboards/1860-node-exporter-full
+          {
+            name = "endlessh";
+            options.path = ./dashboards/endlessh.json;
+          }
+          {
+            name = "unbound";
+            options.path = ./dashboards/unbound.json;
+          }
+          {
+            name = "nginx";
+            options.path = ./dashboards/nginx.json;
+          }
+          {
+            name = "postgersql";
+            options.path = ./dashboards/postgresql.json;
+          }
+        ];
+      };
+
+      loki.configuration.ruler.alertmanager_url = "https://${config.nixfiles.modules.alertmanager.domain}";
+
+      prometheus = {
+        # It would be nice if these could be generated dynamically. That would
+        # require a complete rework of how configurations are defined, though.
+        scrapeConfigs = let
+          mkTargets = hosts: port: map (host: "${host.hostname}:${toString port}") hosts;
+        in
+          with my.configurations;
+          with config.services.prometheus.exporters; [
+            {
+              job_name = "endlessh-go";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets
+                    [
+                      manwe
+                      varda
+                      yavanna
+                    ]
+                    config.services.endlessh-go.prometheus.port;
+                }
+              ];
+            }
+            {
+              job_name = "nginx";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets
+                    [
+                      manwe
+                      varda
+                      yavanna
+                    ]
+                    nginx.port;
+                }
+              ];
+            }
+            {
+              job_name = "node";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets
+                    [
+                      manwe
+                      varda
+                      yavanna
+                    ]
+                    node.port;
+                }
+              ];
+            }
+            {
+              job_name = "postgres";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets
+                    [
+                      manwe
+                    ]
+                    postgres.port;
+                }
+              ];
+            }
+            {
+              job_name = "unbound";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets
+                    [
+                      manwe
+                    ]
+                    unbound.port;
+                }
+              ];
+            }
+            {
+              job_name = "wireguard";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets
+                    [
+                      manwe
+                    ]
+                    wireguard.port;
+                }
+              ];
+            }
+          ];
+
+        alertmanagers = [
+          {
+            scheme = "https";
+            static_configs = [
+              {targets = [config.nixfiles.modules.alertmanager.domain];}
+            ];
+          }
+        ];
+      };
+    };
+  };
+}
diff --git a/modules/nixos/nextcloud.nix b/modules/nixos/nextcloud.nix
new file mode 100644
index 0000000..69bea8a
--- /dev/null
+++ b/modules/nixos/nextcloud.nix
@@ -0,0 +1,133 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.nextcloud;
+in {
+  options.nixfiles.modules.nextcloud = {
+    enable = mkEnableOption "Nextcloud";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "nextcloud.${config.networking.domain}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      nginx = {
+        enable = true;
+        virtualHosts.${cfg.domain} = {};
+      };
+      postgresql.enable = true;
+    };
+
+    services = let
+      db = "nextcloud";
+    in {
+      nextcloud = mkMerge [
+        {
+          enable = true;
+          package = pkgs.nextcloud23;
+
+          hostName = cfg.domain;
+
+          appstoreEnable = false;
+
+          config = {
+            adminpassFile = null; # This needs to be set as secret.
+
+            dbtype = "pgsql";
+            dbhost = "/run/postgresql";
+            dbuser = db;
+            dbname = db;
+
+            defaultPhoneRegion = "RU";
+          };
+
+          extraApps = let
+            mkNextcloudApp = {
+              name,
+              version,
+              hash,
+            }:
+              pkgs.fetchNextcloudApp {
+                inherit name version hash;
+                url = "https://github.com/nextcloud/${name}/archive/refs/tags/v${version}.tar.gz";
+              };
+          in {
+            contacts = mkNextcloudApp {
+              name = "contacts";
+              version = "4.0.1";
+              sha256 = "sha256-dXKsG8KmlUojeY5dUn/XsMD3KaSh4QcZFOGDdcqlSvE=";
+            };
+            calendar = mkNextcloudApp {
+              name = "calendar";
+              version = "3.0.5";
+              sha256 = "sha256-aKUKm7fWJQxOWwma56Tv+GGIo+p0n30Nhoyt4XoxsjI=";
+            };
+            files_rightclick = mkNextcloudApp {
+              name = "files_rightclick";
+              version = "23.0.1";
+              sha256 = "sha256-VYODzkvvGrtpyRoug/8UPKhAgfCx1ltP1JdGPiB/lts=";
+            };
+            unsplash = mkNextcloudApp {
+              name = "unsplash";
+              version = "1.2.4";
+              sha256 = "sha256-KGSkBOrNu0nK0YvAPYaxEL/kZNoJQD1oBV2aUBxh6cI=";
+            };
+            previewgenerator = mkNextcloudApp {
+              name = "previewgenerator";
+              version = "3.4.1";
+              sha256 = "sha256-IUdj0xWt5zHxQoiMv1bYyYTzekuOFrsRIe530QOwC/w=";
+            };
+            bruteforcesettings = mkNextcloudApp {
+              name = "bruteforcesettings";
+              version = "2.3.0";
+              sha256 = "sha256-J7ujmiPaw8GI7vDfVPXEum2XAMWvahciP8C6iXgckdE=";
+            };
+          };
+        }
+        (mkIf config.nixfiles.modules.acme.enable {
+          https = true;
+          config.overwriteProtocol = "https";
+        })
+      ];
+
+      postgresql = {
+        ensureDatabases = [db];
+        ensureUsers = [
+          {
+            name = db;
+            ensurePermissions."DATABASE \"${db}\"" = "ALL";
+          }
+        ];
+      };
+    };
+
+    systemd = {
+      services = {
+        nextcloud-setup.after = ["network-online.target" "postgresql.service"];
+
+        nextcloud-preview-generate-cron.serviceConfig = {
+          Type = "oneshot";
+          User = "nextcloud";
+          ExecStart = "${config.services.nextcloud.occ}/bin/nextcloud-occ preview:pre-generate";
+        };
+      };
+
+      timers.nextcloud-preview-generate = {
+        wantedBy = ["timers.target"];
+        timerConfig = {
+          OnBootSec = "15m";
+          OnUnitActiveSec = "15m";
+          Unit = "nextcloud-preview-generate-cron.service";
+        };
+      };
+    };
+  };
+}
diff --git a/modules/nixos/nginx.nix b/modules/nixos/nginx.nix
new file mode 100644
index 0000000..b8ab24d
--- /dev/null
+++ b/modules/nixos/nginx.nix
@@ -0,0 +1,99 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.nginx;
+in {
+  options.nixfiles.modules.nginx = {
+    enable = mkEnableOption "Nginx";
+
+    upstreams = mkOption {
+      description = "Defines a group of servers to use as proxy target.";
+      type = with types; anything;
+      default = null;
+    };
+
+    virtualHosts = mkOption {
+      description = "Attrset of virtual hosts.";
+      type = with types; anything;
+      default = null;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services = {
+      nginx = {
+        enable = true;
+        enableReload = true;
+
+        package = pkgs.nginxMainline;
+
+        statusPage = true;
+
+        serverTokens = false;
+
+        recommendedGzipSettings = true;
+        recommendedOptimisation = true;
+        recommendedProxySettings = true;
+        recommendedTlsSettings = true;
+
+        commonHttpConfig = concatStrings [
+          ''
+            add_header X-Robots-Tag "noindex, nofollow, noarchive, nosnippet";
+          ''
+          (optionalString (hasAttr "wireguard" this)
+            (with config.nixfiles.modules.wireguard; ''
+              geo $internal {
+                default 0;
+                127.0.0.1/32 1;
+                ::1/128 1;
+                ${ipv4.subnet} 1;
+                ${ipv6.subnet} 1;
+              }
+            ''))
+        ];
+
+        inherit (cfg) upstreams;
+
+        virtualHosts =
+          {
+            default = {
+              default = true;
+              rejectSSL = true;
+              locations."/".return = "444";
+            };
+          }
+          // (mkIf (cfg.virtualHosts != null) (mapAttrs (_: attr:
+            mkMerge [
+              attr
+              (mkIf config.nixfiles.modules.acme.enable {
+                enableACME = true;
+                forceSSL = true;
+              })
+            ])
+          cfg.virtualHosts));
+      };
+
+      fail2ban.jails = {
+        nginx-http-auth = ''
+          enabled = true
+        '';
+        nginx-botsearch = ''
+          enabled = true
+        '';
+      };
+
+      prometheus.exporters.nginx = {
+        enable = true;
+        listenAddress = mkDefault this.wireguard.ipv4.address;
+        port = mkDefault 9113;
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = [80 443];
+  };
+}
diff --git a/modules/nixos/node-exporter.nix b/modules/nixos/node-exporter.nix
new file mode 100644
index 0000000..43f48f6
--- /dev/null
+++ b/modules/nixos/node-exporter.nix
@@ -0,0 +1,34 @@
+{
+  config,
+  lib,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.node-exporter;
+in {
+  options.nixfiles.modules.node-exporter.enable = mkEnableOption "Prometheus Node Exporter";
+
+  config = mkIf cfg.enable {
+    services.prometheus.exporters.node = {
+      enable = true;
+      listenAddress = mkDefault this.wireguard.ipv4.address;
+      port = 9100;
+      enabledCollectors = [
+        "buddyinfo"
+        "cgroups"
+        "ethtool"
+        "interrupts"
+        "ksmd"
+        "lnstat"
+        "logind"
+        "mountstats"
+        "network_route"
+        "processes"
+        "qdisc"
+        "systemd"
+        "zoneinfo"
+      ];
+    };
+  };
+}
diff --git a/modules/nixos/nsd.nix b/modules/nixos/nsd.nix
new file mode 100644
index 0000000..0dade8f
--- /dev/null
+++ b/modules/nixos/nsd.nix
@@ -0,0 +1,174 @@
+{
+  config,
+  inputs,
+  lib,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.nsd;
+in {
+  options.nixfiles.modules.nsd = {
+    enable = mkEnableOption "NSD";
+
+    fqdn = mkOption {
+      description = "FQDN of this nameserver.";
+      type = with types; str;
+      default = "ns.${config.networking.domain}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services = {
+      nsd = {
+        enable = true;
+        interfaces = with this; [ipv4.address ipv6.address];
+        ipTransparent = true;
+        ratelimit.enable = true;
+
+        zones = let
+          dns = inputs.dns-nix.lib;
+        in
+          with dns.combinators; let
+            ips = hostname:
+              with my.configurations.${hostname}; {
+                A = [(a ipv4.address)];
+                AAAA = [(aaaa ipv6.address)];
+              };
+
+            mkEmailEntries = {
+              domain ? my.domain.shire,
+              dkimKey ? null,
+            }: {
+              MX = [(mx.mx 10 "${domain}.")];
+              TXT = [(spf.strict ["a" "mx"])];
+              DMARC = [
+                {
+                  p = "quarantine";
+                  sp = "quarantine";
+                  rua = ["mailto:admin+rua@${domain}"];
+                  ruf = ["mailto:admin+ruf@${domain}"];
+                }
+              ];
+              DKIM = optional (dkimKey != null) {
+                selector = "mail";
+                p = dkimKey;
+              };
+            };
+
+            mkZone = {
+              domain,
+              sldIps ? (ips "manwe"),
+              extra ? {},
+            }: {
+              ${domain}.data = dns.toString domain (mkMerge [
+                {
+                  TTL = 60 * 60;
+
+                  SOA = {
+                    nameServer = "${cfg.fqdn}.";
+                    adminEmail = "admin+dns@${my.domain.shire}";
+                    serial = 2022091601; # Don't forget to bump the revision!
+                  };
+
+                  NS = with my.domain; [
+                    "ns1.${shire}"
+                    # "ns2.${shire}"
+                  ];
+
+                  CAA = letsEncrypt "admin+caa@${my.domain.shire}";
+                }
+                sldIps
+                extra
+              ]);
+            };
+
+            # https://ariadne.id/
+            # https://docs.keyoxide.org/service-providers/dns/
+            ariadneIdProof.TXT = ["openpgp4fpr:${my.pgp.fingerprint}"];
+          in
+            mkMerge [
+              (mkZone {
+                domain = my.domain.shire;
+                extra = mkMerge [
+                  (mkEmailEntries {
+                    dkimKey = "@DKIM_KEY@";
+                  })
+                  {
+                    subdomains = rec {
+                      manwe = ips "manwe";
+                      "*.manwe" = manwe;
+                      varda = ips "varda";
+                      "*.varda" = varda;
+                      yavanna = ips "yavanna";
+                      "*.yavanna" = yavanna;
+
+                      ns1 = manwe;
+                      # ns2 = varda;
+
+                      alertmanager = manwe;
+                      bitwarden = manwe;
+                      git = manwe;
+                      gotify = manwe;
+                      grafana = manwe;
+                      loki = manwe;
+                      prometheus = manwe;
+                      radicale = manwe;
+                      rss-bridge = manwe;
+                      vaultwarden = manwe;
+
+                      minecraft = varda;
+
+                      flood = yavanna;
+                    };
+                  }
+                ];
+              })
+              (mkZone {
+                domain = my.domain.azahi;
+                extra = mkMerge [
+                  (mkEmailEntries {
+                    dkimKey = "@DKIM_KEY@";
+                  })
+                  ariadneIdProof
+                  {
+                    subdomains.git = ips "manwe";
+                  }
+                ];
+              })
+              (mkZone {
+                domain = my.domain.gondor;
+                extra = mkMerge [
+                  (mkEmailEntries {
+                    dkimKey = "@DKIM_KEY@";
+                  })
+                  {
+                    subdomains.frodo = ips "manwe" // ariadneIdProof;
+                  }
+                ];
+              })
+              (mkZone {
+                domain = my.domain.rohan;
+                extra = mkMerge [
+                  (mkEmailEntries {
+                    dkimKey = "@DKIM_KEY@";
+                  })
+                  {
+                    subdomains.frodo = ips "manwe" // ariadneIdProof;
+                  }
+                ];
+              })
+            ];
+      };
+
+      fail2ban.jails.nsd = ''
+        enabled = true
+      '';
+    };
+
+    networking.firewall = rec {
+      allowedTCPPorts = [53];
+      allowedUDPPorts = allowedTCPPorts;
+    };
+  };
+}
diff --git a/modules/nixos/openssh.nix b/modules/nixos/openssh.nix
new file mode 100644
index 0000000..00d2852
--- /dev/null
+++ b/modules/nixos/openssh.nix
@@ -0,0 +1,34 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.openssh;
+in {
+  options.nixfiles.modules.openssh.server.enable =
+    mkEnableOption "OpenSSH server";
+
+  config = mkIf cfg.server.enable {
+    programs.mosh.enable = true;
+
+    services = let
+      port = 22022; # Port 22 should be occupied by a tarpit.
+    in {
+      openssh = {
+        enable = true;
+        ports = [port];
+        logLevel = "VERBOSE"; # Required by fail2ban.
+        permitRootLogin = "no";
+        passwordAuthentication = false;
+      };
+
+      fail2ban.jails.sshd = ''
+        enabled = true
+        mode = aggressive
+        port = ${toString port}
+      '';
+    };
+  };
+}
diff --git a/modules/nixos/podman.nix b/modules/nixos/podman.nix
new file mode 100644
index 0000000..1c5378b
--- /dev/null
+++ b/modules/nixos/podman.nix
@@ -0,0 +1,41 @@
+{
+  config,
+  lib,
+  inputs,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.podman;
+in {
+  options.nixfiles.modules.podman.enable = mkEnableOption "Podman";
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !config.nixfiles.modules.docker.enable;
+        message = "Pick only one!";
+      }
+    ];
+
+    secrets.containers-auth = {
+      file = "${inputs.self}/secrets/containers-auth";
+      path = "${config.dirs.config}/containers/auth.json";
+      owner = my.username;
+      inherit (config.my) group;
+    };
+
+    virtualisation.podman.enable = true;
+
+    environment.systemPackages = with pkgs; [podman-compose];
+
+    my.extraGroups = ["podman"];
+
+    hm.programs.bash = {
+      shellAliases.p = "${pkgs.podman}/bin/podman";
+      initExtra = mkAfter ''
+        _complete_alias p __start_podman podman
+      '';
+    };
+  };
+}
diff --git a/modules/nixos/postgresql.nix b/modules/nixos/postgresql.nix
new file mode 100644
index 0000000..df05e7e
--- /dev/null
+++ b/modules/nixos/postgresql.nix
@@ -0,0 +1,87 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.postgresql;
+in {
+  options.nixfiles.modules.postgresql = {
+    enable = mkEnableOption "PostgreSQL";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.postgresql_15;
+      description = "PostgreSQL package to use.";
+    };
+
+    extraPostStart = mkOption {
+      type = with types; listOf str;
+      default = [];
+      description = ''
+        Additional post-startup commands.
+
+        This could be used to provide a crude interface to grant permissions and
+        such.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = any (x: x == "en_GB.UTF-8/UTF-8") config.i18n.supportedLocales;
+        message = "The locale must be available";
+      }
+    ];
+
+    services = {
+      postgresql = {
+        enable = true;
+
+        inherit (cfg) package;
+
+        initdbArgs = [
+          "--encoding=UTF8"
+          "--locale-provider=icu"
+          "--icu-locale=en_GB@collation=posix"
+          "--locale=en_GB.UTF-8"
+          "--lc-collate=C"
+          "--lc-ctype=C"
+        ];
+
+        authentication = ''
+          local all all trust
+        '';
+      };
+
+      prometheus.exporters.postgres = {
+        enable = true;
+        listenAddress = mkDefault this.wireguard.ipv4.address;
+        port = mkDefault 9187;
+      };
+    };
+
+    systemd.services.postgresql.postStart =
+      optionalString (cfg.extraPostStart != [])
+      concatStringsSep "\n"
+      cfg.extraPostStart;
+
+    environment.sessionVariables.PSQLRC = toString (pkgs.writeText "psqlrc" ''
+      \set QUIET 1
+
+      \timing
+      \x auto
+      \pset null '[NULL]'
+      \set PROMPT1 '%[%033[1m%]%M %n@%/%R%[%033[0m%]% λ '
+      \set PROMPT2 '    … > '
+      \set VERBOSITY verbose
+      \set HISTCONTROL ignoredups
+      \set HISTFILE /dev/null
+
+      \unset QUIET
+    '');
+  };
+}
diff --git a/modules/nixos/profiles/default.nix b/modules/nixos/profiles/default.nix
new file mode 100644
index 0000000..d5ab838
--- /dev/null
+++ b/modules/nixos/profiles/default.nix
@@ -0,0 +1,33 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.default;
+in {
+  imports = [
+    ./dev
+    ./headful.nix
+    ./headless.nix
+  ];
+
+  config = mkIf cfg.enable {
+    programs.less = {
+      enable = true;
+      envVariables.LESSHISTFILE = "-";
+    };
+
+    environment.systemPackages = with pkgs; [
+      cryptsetup
+      lshw
+      lsof
+      pciutils
+      psmisc
+      usbutils
+      util-linux
+    ];
+  };
+}
diff --git a/modules/nixos/profiles/dev/containers.nix b/modules/nixos/profiles/dev/containers.nix
new file mode 100644
index 0000000..195b892
--- /dev/null
+++ b/modules/nixos/profiles/dev/containers.nix
@@ -0,0 +1,27 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.dev.containers;
+in {
+  config = mkIf cfg.enable {
+    nixfiles.modules.podman.enable = true;
+
+    hm = {
+      home = {
+        sessionVariables.MINIKUBE_HOME = "${config.dirs.config}/minikube";
+
+        packages = with pkgs; [buildah];
+      };
+
+      xdg.dataFile."minikube/config/config.json".text = generators.toJSON {} {
+        config.Rootless = true;
+        driver = "podman";
+        container-runtime = "cri-o";
+      };
+    };
+  };
+}
diff --git a/modules/nixos/profiles/dev/default.nix b/modules/nixos/profiles/dev/default.nix
new file mode 100644
index 0000000..83d41c0
--- /dev/null
+++ b/modules/nixos/profiles/dev/default.nix
@@ -0,0 +1,19 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.dev.default;
+in {
+  config = mkIf cfg.enable {
+    hm.home.language = {
+      collate = "C";
+      messages = "C";
+    };
+
+    my.extraGroups = ["kvm"];
+  };
+}
diff --git a/modules/nixos/profiles/headful.nix b/modules/nixos/profiles/headful.nix
new file mode 100644
index 0000000..01c442e
--- /dev/null
+++ b/modules/nixos/profiles/headful.nix
@@ -0,0 +1,88 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.headful;
+in {
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      chromium.enable = true;
+      firefox.enable = true;
+      sound.enable = true;
+      x11.enable = true;
+
+      dwm.enable = mkDefault false;
+      kde.enable = mkDefault true;
+      xmonad.enable = mkDefault false;
+    };
+
+    hm = {
+      home.packages = with pkgs; [
+        calibre
+        imv
+        neochat
+        tdesktop
+        tor-browser
+      ];
+
+      programs.bash.shellAliases.open = "${pkgs.xdg-utils}/bin/xdg-open";
+    };
+
+    boot = {
+      # Pretty much placebo but has some nice patches for `-march=native`
+      # optimisations, P-State Zen4 support and Fsync for Wine.
+      kernelPackages = mkDefault pkgs.linuxPackages_xanmod_latest;
+
+      # There are (arguably) not a lot of reasons to keep mitigations enabled
+      # for on machine that is not web-facing. First of all, to completely
+      # mitigate any possible Spectre holes one would need to disable
+      # Hyperthreading altogether which will essentially put one's computer into
+      # the stone age by not being able to to effectively utilise multi-core its
+      # multicore capabilities. Secondly, by enabling mitigations, we introduce
+      # a plethora of performace overheads[1], which, albeit small, but still
+      # contribute to the overall speed of things. This is however still poses a
+      # security risk, which I am willing to take.
+      #
+      # [1]: https://www.phoronix.com/scan.php?page=article&item=spectre-meltdown-2&num=11
+      kernelParams = ["mitigations=off"];
+
+      loader = {
+        efi.canTouchEfiVariables = true;
+
+        systemd-boot = {
+          enable = true;
+          configurationLimit = 10;
+        };
+      };
+    };
+
+    hardware.opengl = {
+      enable = true;
+      driSupport = true;
+    };
+
+    programs = {
+      iftop.enable = true;
+      mtr.enable = true;
+      traceroute.enable = true;
+    };
+
+    services = {
+      # https://github.com/NixOS/nixpkgs/issues/135888
+      upower.enable = true;
+
+      psd.enable = true;
+    };
+
+    environment.systemPackages = with pkgs; [
+      ethtool
+      nethogs
+    ];
+
+    my.extraGroups = ["audio" "video" "input"];
+  };
+}
diff --git a/modules/nixos/profiles/headless.nix b/modules/nixos/profiles/headless.nix
new file mode 100644
index 0000000..9faf531
--- /dev/null
+++ b/modules/nixos/profiles/headless.nix
@@ -0,0 +1,42 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.profiles.headless;
+in {
+  config = mkIf cfg.enable {
+    nixfiles.modules = {
+      openssh.server.enable = true;
+      endlessh-go.enable = true;
+
+      fail2ban.enable = true;
+
+      node-exporter.enable = true;
+      promtail.enable = true;
+    };
+
+    # Pin version to prevent any surprises.
+    boot.kernelPackages = pkgs.linuxPackages_5_15_hardened;
+
+    nix = {
+      gc = {
+        automatic = true;
+        dates = "weekly";
+        options = "--delete-older-than 30d";
+      };
+
+      optimise = {
+        automatic = true;
+        dates = ["daily"];
+      };
+    };
+
+    services.udisks2.enable = false;
+
+    xdg.sounds.enable = false;
+  };
+}
diff --git a/modules/nixos/prometheus.nix b/modules/nixos/prometheus.nix
new file mode 100644
index 0000000..a75c151
--- /dev/null
+++ b/modules/nixos/prometheus.nix
@@ -0,0 +1,49 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.prometheus;
+in {
+  options.nixfiles.modules.prometheus = {
+    enable = mkEnableOption "Prometheus";
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 30111;
+    };
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "prometheus.${config.networking.domain}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.nginx = with cfg; {
+      enable = true;
+      upstreams.prometheus.servers."127.0.0.1:${toString cfg.port}" = {};
+      virtualHosts.${domain} = {
+        locations."/".proxyPass = "http://prometheus";
+        extraConfig = nginxInternalOnly;
+      };
+    };
+
+    services.prometheus = with cfg; {
+      enable = true;
+
+      listenAddress = "127.0.0.1";
+      inherit port;
+
+      extraFlags = [
+        "--web.external-url=https://${domain}"
+        "--storage.tsdb.retention.size=50GB"
+        "--storage.tsdb.retention.time=1y"
+        "--storage.tsdb.wal-compression"
+      ];
+    };
+  };
+}
diff --git a/modules/nixos/promtail.nix b/modules/nixos/promtail.nix
new file mode 100644
index 0000000..552df82
--- /dev/null
+++ b/modules/nixos/promtail.nix
@@ -0,0 +1,53 @@
+{
+  config,
+  lib,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.promtail;
+in {
+  options.nixfiles.modules.promtail = {
+    enable = mkEnableOption "Promtail";
+
+    loki = {
+      url = mkOption {
+        description = "Address of a listening Loki service.";
+        type = with types; str;
+        default = "https://${config.nixfiles.modules.loki.domain}";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.promtail = {
+      enable = true;
+
+      configuration = {
+        server = rec {
+          http_listen_address = this.wireguard.ipv4.address;
+          http_listen_port = 30181;
+
+          grpc_listen_address = this.wireguard.ipv4.address;
+          grpc_listen_port = http_listen_port + 1;
+
+          log_level = "warn";
+        };
+
+        clients = [{url = "${cfg.loki.url}/loki/api/v1/push";}];
+
+        positions.filename = "/tmp/positions.yaml";
+
+        scrape_configs = [
+          {
+            job_name = "journal";
+            journal = {
+              max_age = "24h";
+              labels.job = "systemd-journal";
+            };
+          }
+        ];
+      };
+    };
+  };
+}
diff --git a/modules/nixos/psd.nix b/modules/nixos/psd.nix
new file mode 100644
index 0000000..77d3c66
--- /dev/null
+++ b/modules/nixos/psd.nix
@@ -0,0 +1,60 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.psd;
+in {
+  options.nixfiles.modules.psd.enable =
+    mkEnableOption "Profile Sync Daemon";
+
+  config = mkIf cfg.enable {
+    hm = {
+      home.packages = with pkgs; [profile-sync-daemon];
+
+      xdg.configFile."psd/psd.conf".text = ''
+        USE_OVERLAYFS="yes"
+      '';
+    };
+
+    systemd.user = {
+      services = {
+        psd = {
+          unitConfig = {
+            Description = "Profile-sync-daemon";
+            Wants = ["psd-resync.service"];
+            RequiresMountsFor = "/home/";
+            After = ["local-fs.target"];
+          };
+          serviceConfig = {
+            RemainAfterExit = true;
+            ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon startup";
+            ExecStop = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon unsync";
+          };
+          wantedBy = ["graphical.target"];
+        };
+
+        psd-resync = {
+          unitConfig = {
+            Description = "Profile-sync-daemon resync";
+            After = ["psd.service"];
+            Wants = ["psd-resync.timer"];
+            BindsTo = ["psd.service"];
+          };
+          serviceConfig.ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon resync";
+          wantedBy = ["graphical.target"];
+        };
+      };
+
+      timers.psd-resync = {
+        unitConfig = {
+          Description = "Profile-sync-daemon resync timer";
+          BindsTo = ["psd.service"];
+        };
+        timerConfig.OnUnitActiveSec = "1h";
+      };
+    };
+  };
+}
diff --git a/modules/nixos/radarr.nix b/modules/nixos/radarr.nix
new file mode 100644
index 0000000..0abfdf2
--- /dev/null
+++ b/modules/nixos/radarr.nix
@@ -0,0 +1,28 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.radarr;
+in {
+  options.nixfiles.modules.radarr = {
+    enable = mkEnableOption "Radarr";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "radarr.${config.networking.fqdn}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.nginx = {
+      enable = true;
+      upstreams.radarr.servers."127.0.0.1:7878" = {};
+      virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://radarr";
+    };
+
+    services.radarr.enable = true;
+  };
+}
diff --git a/modules/nixos/radicale.nix b/modules/nixos/radicale.nix
new file mode 100644
index 0000000..c903d39
--- /dev/null
+++ b/modules/nixos/radicale.nix
@@ -0,0 +1,52 @@
+{
+  config,
+  inputs,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.radicale;
+in {
+  options.nixfiles.modules.radicale = {
+    enable = mkEnableOption "Radicale";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "radicale.${config.networking.domain}";
+    };
+  };
+
+  config = let
+    port = 5232;
+  in
+    mkIf cfg.enable {
+      secrets.radicale-htpasswd = {
+        file = "${inputs.self}/secrets/radicale-htpasswd";
+        owner = "radicale";
+        group = "radicale";
+      };
+
+      nixfiles.modules.nginx = {
+        enable = true;
+        upstreams.radicale.servers."127.0.0.1:${toString port}" = {};
+        virtualHosts.${cfg.domain} = {
+          locations."/".proxyPass = "http://radicale";
+          extraConfig = nginxInternalOnly;
+        };
+      };
+
+      services.radicale = {
+        enable = true;
+        settings = {
+          server.hosts = ["127.0.0.1:${toString port}"];
+          web.type = "none";
+          auth = {
+            type = "htpasswd";
+            htpasswd_filename = config.secrets.radicale-htpasswd.path;
+            htpasswd_encryption = "bcrypt";
+          };
+        };
+      };
+    };
+}
diff --git a/modules/nixos/rss-bridge.nix b/modules/nixos/rss-bridge.nix
new file mode 100644
index 0000000..fef1070
--- /dev/null
+++ b/modules/nixos/rss-bridge.nix
@@ -0,0 +1,31 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.rss-bridge;
+in {
+  options.nixfiles.modules.rss-bridge = {
+    enable = mkEnableOption "RSS-Bridge";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "rss-bridge.${config.networking.domain}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.nginx = {
+      enable = true;
+      virtualHosts.${cfg.domain}.extraConfig = nginxInternalOnly;
+    };
+
+    services.rss-bridge = {
+      enable = true;
+      virtualHost = cfg.domain;
+      whitelist = ["*"];
+    };
+  };
+}
diff --git a/modules/nixos/rtorrent.nix b/modules/nixos/rtorrent.nix
new file mode 100644
index 0000000..4014a3b
--- /dev/null
+++ b/modules/nixos/rtorrent.nix
@@ -0,0 +1,297 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.rtorrent;
+in {
+  options.nixfiles.modules.rtorrent = {
+    enable = mkEnableOption "rTorrent";
+
+    flood = {
+      enable = mkEnableOption "Flood" // {default = cfg.enable;};
+
+      domain = mkOption {
+        description = "Domain name sans protocol scheme.";
+        type = with types; str;
+        default = "flood.${config.networking.domain}";
+      };
+    };
+  };
+
+  config = let
+    user = "rtorrent";
+    group = "rtorrent";
+    baseDir = "/var/lib/rtorrent";
+    rpcSocket = "${baseDir}/rpc.socket";
+  in
+    mkIf cfg.enable (mkMerge [
+      (let
+        port = 50000;
+      in {
+        systemd = {
+          services.rtorrent = {
+            description = "rTorrent";
+            after = ["network.target" "local-fs.target"];
+            serviceConfig = let
+              leechDir = "${baseDir}/leech";
+              seedDir = "${baseDir}/seed";
+              sessionDir = "${baseDir}/session";
+              logDir = "${baseDir}/log";
+              configFile = let
+                moveCompleted = let
+                  pkg = pkgs.writeShellApplication {
+                    name = "move-completed";
+                    runtimeInputs = with pkgs; [
+                      coreutils-full
+                      gnused
+                      findutils
+                    ];
+                    text = ''
+                      set -x
+
+                      leech_path="$1"
+                      seed_path="$2"
+                      # seed_path="$(echo "$2" | sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf '%b')"
+
+                      mkdir -pv "$seed_path"
+                      mv -fv "$leech_path" "$seed_path"
+                    '';
+                  };
+                in "${pkg}/bin/move-completed";
+              in
+                pkgs.writeText "rtorrent.rc" ''
+                  method.insert = cfg.leech,     private|const|string, (cat, "${leechDir}")
+                  method.insert = cfg.seed,      private|const|string, (cat, "${seedDir}")
+                  method.insert = cfg.session,   private|const|string, (cat, "${sessionDir}")
+                  method.insert = cfg.log,       private|const|string, (cat, "${logDir}")
+                  method.insert = cfg.rpcsocket, private|const|string, (cat, "${rpcSocket}")
+
+                  directory.default.set = (cat, (cfg.leech))
+                  session.path.set = (cat, (cfg.session))
+
+                  network.port_range.set = ${toString port}-${toString port}
+                  network.port_random.set = no
+
+                  dht.mode.set = disable
+                  protocol.pex.set = no
+
+                  trackers.use_udp.set = no
+
+                  protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
+
+                  pieces.memory.max.set = ${toString (pow 2 11)}M
+                  pieces.preload.type.set = 2
+
+                  network.xmlrpc.size_limit.set = ${toString (pow 2 17)}
+
+                  network.max_open_files.set   = ${toString (pow 2 10)}
+                  network.max_open_sockets.set = ${toString (pow 2 10)}
+
+                  network.http.max_open.set = ${toString (pow 2 8)}
+
+                  throttle.global_down.max_rate.set_kb = 0
+                  throttle.global_up.max_rate.set_kb   = 0
+
+                  encoding.add = UTF-8
+                  system.umask.set = 0027
+                  system.cwd.set = (directory.default)
+
+                  network.scgi.open_local = (cat, (cfg.rpcsocket))
+
+                  method.insert = d.move_completed, simple, "\
+                    d.directory.set=$argument.1=;\
+                    execute=${moveCompleted}, $argument.0=, $argument.1=;\
+                    d.save_full_session=\
+                  "
+                  method.insert = d.leech_path, simple, "\
+                    if=(d.is_multi_file),\
+                    (cat, (d.directory), /),\
+                    (cat, (d.directory), /, (d.name))\
+                  "
+                  method.insert = d.seed_path, simple, "\
+                    cat=$cfg.seed=, /, $d.custom1=\
+                  "
+                  method.set_key = event.download.finished, move_complete, "\
+                    d.move_completed=$d.leech_path=, $d.seed_path=\
+                  "
+
+                  log.open_file = "log", (cat, (cfg.log), "/", "default.log")
+                  log.add_output = "info", "log"
+                  log.execute = (cat, (cfg.log), "/", "execute.log")
+                '';
+            in {
+              Restart = "on-failure";
+              RestartSec = 3;
+
+              KillMode = "process";
+              KillSignal = "SIGHUP";
+
+              User = user;
+              Group = group;
+
+              ExecStartPre = concatStringsSep " " [
+                "${pkgs.coreutils-full}/bin/mkdir -p"
+                leechDir
+                seedDir
+                sessionDir
+                logDir
+              ];
+              ExecStart = concatStringsSep " " [
+                "${pkgs.rtorrent}/bin/rtorrent"
+                "-n"
+                "-o system.daemon.set=true"
+                "-o network.bind_address.set=0.0.0.0"
+                "-o import=${configFile}"
+              ];
+              ExecStop = concatStringsSep " " [
+                "${pkgs.coreutils-full}/bin/rm -rf"
+                rpcSocket
+              ];
+
+              RuntimeDirectory = "rtorrent";
+              RuntimeDirectoryMode = 0750;
+              UMask = 0027;
+              AmbientCapabilities = [""];
+              CapabilityBoundingSet = [""];
+              LockPersonality = true;
+              MemoryDenyWriteExecute = true;
+              NoNewPrivileges = true;
+              PrivateDevices = true;
+              PrivateTmp = true;
+              PrivateUsers = true;
+              ProtectClock = true;
+              ProtectControlGroups = true;
+              ProtectHome = true;
+              ProtectHostname = true;
+              ProtectKernelLogs = true;
+              ProtectKernelModules = true;
+              ProtectKernelTunables = true;
+              ProcSubset = "pid";
+              RemoveIPC = true;
+              RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
+              RestrictNamespaces = true;
+              RestrictRealtime = true;
+              RestrictSUIDSGID = true;
+              SystemCallArchitectures = "native";
+              SystemCallFilter = ["@system-service" "~@resources" "~@privileged"];
+            };
+            wantedBy = ["multi-user.target"];
+          };
+
+          tmpfiles.rules = ["d '${baseDir}' 0750 ${user} ${group} -"];
+        };
+
+        users = {
+          users.${user} = {
+            inherit group;
+            shell = pkgs.bashInteractive;
+            home = baseDir;
+            description = "rTorrent";
+            isSystemUser = true;
+          };
+          groups.${group} = {};
+        };
+        my.extraGroups = [group];
+
+        networking.firewall.allowedTCPPorts = [port];
+
+        boot.kernel.sysctl = {
+          "net.core.rmem_max" = mkOverride 500 (pow 2 24);
+          "net.core.wmem_max" = mkOverride 500 (pow 2 24);
+          "net.ipv4.tcp_fin_timeout" = mkOverride 500 30;
+          "net.ipv4.tcp_rmem" = mkOverride 500 (mkTcpMem 12 23 24);
+          "net.ipv4.tcp_slow_start_after_idle" = 0;
+          "net.ipv4.tcp_tw_recycle" = mkOverride 500 1;
+          "net.ipv4.tcp_tw_reuse" = mkOverride 500 1;
+          "net.ipv4.tcp_wmem" = mkOverride 500 (mkTcpMem 12 23 24);
+        };
+      })
+      (let
+        port = 50001;
+        pkg = pkgs.nodePackages.flood;
+      in
+        mkIf cfg.flood.enable {
+          nixfiles.modules.nginx = {
+            enable = true;
+            upstreams.flood.servers."127.0.0.1:${toString port}" = {};
+            virtualHosts.${cfg.flood.domain} = {
+              root = "${pkg}/lib/node_modules/flood/dist/assets";
+              locations = {
+                "/".tryFiles = "$uri /index.html";
+                "/api" = {
+                  proxyPass = "http://flood";
+                  extraConfig = ''
+                    proxy_buffering off;
+                    proxy_cache off;
+                  '';
+                };
+              };
+              extraConfig = nginxInternalOnly;
+            };
+          };
+
+          systemd.services.flood = {
+            description = "Flood";
+            after = ["network.target" "rtorrent.service"];
+            path = with pkgs; [mediainfo];
+            serviceConfig = {
+              Restart = "on-failure";
+              RestartSec = 3;
+
+              User = user;
+              Group = group;
+
+              ExecStart = concatStringsSep " " [
+                "${pkg}/bin/flood"
+                "--allowedpath=${baseDir}"
+                "--baseuri=/"
+                "--rundir=${baseDir}/flood"
+                "--host=127.0.0.1"
+                "--port=${toString port}"
+                "--rtsocket=${rpcSocket}"
+                "--ssl=false"
+                "--auth=none"
+              ];
+
+              RuntimeDirectory = "rtorrent";
+              RuntimeDirectoryMode = 0750;
+              UMask = 0027;
+              AmbientCapabilities = [""];
+              CapabilityBoundingSet = [""];
+              LockPersonality = true;
+              NoNewPrivileges = true;
+              PrivateDevices = true;
+              PrivateTmp = true;
+              PrivateUsers = true;
+              ProtectClock = true;
+              ProtectControlGroups = true;
+              ProtectHome = true;
+              ProtectHostname = true;
+              ProtectKernelLogs = true;
+              ProtectKernelModules = true;
+              ProtectKernelTunables = true;
+              ProcSubset = "pid";
+              ProtectProc = "invisible";
+              RemoveIPC = true;
+              RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
+              RestrictNamespaces = true;
+              RestrictRealtime = true;
+              RestrictSUIDSGID = true;
+              SystemCallArchitectures = "native";
+              SystemCallFilter = [
+                "~@cpu-emulation"
+                "~@debug"
+                "~@mount"
+                "~@obsolete"
+                "~@privileged"
+                "~@resources"
+              ];
+            };
+            wantedBy = ["multi-user.target"];
+          };
+        })
+    ]);
+}
diff --git a/modules/nixos/searx.nix b/modules/nixos/searx.nix
new file mode 100644
index 0000000..9462d5d
--- /dev/null
+++ b/modules/nixos/searx.nix
@@ -0,0 +1,78 @@
+{
+  config,
+  inputs,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.searx;
+in {
+  options.nixfiles.modules.searx = {
+    enable = mkEnableOption "SearX";
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 61001;
+    };
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; nullOr str;
+      default = "searx.${config.networking.domain}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    secrets.searx-environment = {
+      file = "${inputs.self}/secrets/searx-environment";
+      owner = "searx";
+      group = "searx";
+    };
+
+    nixfiles.modules.nginx = {
+      enable = true;
+      upstreams.searx.servers."127.0.0.1:${toString cfg.port}" = {};
+      virtualHosts.${cfg.domain} = {
+        locations."/".proxyPass = "http://searx";
+        extraConfig = nginxInternalOnly;
+      };
+    };
+
+    services = {
+      searx = {
+        enable = true;
+
+        settings = {
+          general = {
+            instance_name = cfg.domain;
+            contact_url = "mailto:admin+searx@${config.networking.domain}";
+            git_url = false;
+            git_branch = false;
+            docs_url = false;
+            wiki_url = false;
+            twitter_url = false;
+          };
+          server = {
+            bind_address = "127.0.0.1";
+            inherit (cfg) port;
+            secret_key = "@SEARX_SECRET_KEY@";
+            base_url = false;
+            image_proxy = false;
+            default_http_headers = {
+              Referrer-Policy = "no-referrer";
+              X-Content-Type-Options = "nosniff";
+              X-Download-Options = "noopen";
+              X-Robots-Tag = "noindex, nofollow, nosnippet, noarchive";
+            };
+          };
+          search = {
+            safe_search = 0;
+            autocomplete = "";
+          };
+        };
+        environmentFile = config.secrets.searx-environment.path;
+      };
+    };
+  };
+}
diff --git a/modules/nixos/shadowsocks.nix b/modules/nixos/shadowsocks.nix
new file mode 100644
index 0000000..b59359c
--- /dev/null
+++ b/modules/nixos/shadowsocks.nix
@@ -0,0 +1,116 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.shadowsocks;
+in {
+  options.nixfiles.modules.shadowsocks = {
+    enable = mkEnableOption "Shadowsocks";
+
+    port = mkOption {
+      type = with types; port;
+      default = 8388;
+      description = "Port.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    secrets.shadowsocks-password.file = "${inputs.self}/secrets/shadowsocks-password";
+
+    services = {
+      shadowsocks = {
+        enable = true;
+        passwordFile = config.secrets.shadowsocks-password.path;
+        localAddress = ["0.0.0.0"];
+        mode = "tcp_only";
+      };
+
+      fail2ban.jails.shadowsocks-libev = ''
+        enabled = true
+        filter = shadowsocks-libev
+        port = ${toString cfg.port}
+      '';
+    };
+
+    systemd.services.shadowsocks-libev.path = with pkgs;
+      mkForce [
+        (writeShellApplication {
+          name = "ss-server";
+          runtimeInputs = [shadowsocks-libev];
+          text = let
+            # https://github.com/shadowsocks/shadowsocks-libev/blob/master/acl/server_block_local.acl
+            aclFile = writeText "outbound_block_list.acl" ''
+              [outbound_block_list]
+              0.0.0.0/8
+              10.0.0.0/8
+              100.64.0.0/10
+              127.0.0.0/8
+              169.254.0.0/16
+              172.16.0.0/12
+              192.0.0.0/24
+              192.0.2.0/24
+              192.88.99.0/24
+              192.168.0.0/16
+              198.18.0.0/15
+              198.51.100.0/24
+              203.0.113.0/24
+              224.0.0.0/4
+              240.0.0.0/4
+              255.255.255.255/32
+              ::1/128
+              ::ffff:127.0.0.1/104
+              fc00::/7
+              fe80::/10
+            '';
+          in ''
+            ss-server --acl ${aclFile} "$@"
+          '';
+        })
+        coreutils-full
+        jq
+      ];
+
+    environment.etc = mkIf config.nixfiles.modules.fail2ban.enable {
+      "fail2ban/filter.d/shadowsocks-libev.conf".text = ''
+        [Definition]
+        failregex = ^.*failed to handshake with <ADDR>: authentication error$
+        ignoreregex =
+        journalmatch = _SYSTEMD_UNIT=shadowsocks-libev.service
+      '';
+    };
+
+    networking.firewall = {
+      allowedTCPPorts = [cfg.port];
+      extraCommands = ''
+        iptables -A nixos-fw -p tcp --syn --dport ${
+          toString cfg.port
+        } -m connlimit --connlimit-above 32 -j nixos-fw-refuse
+      '';
+    };
+
+    boot.kernel.sysctl = {
+      "net.core.rmem_max" = mkOverride 100 (pow 2 26);
+      "net.core.wmem_max" = mkOverride 100 (pow 2 26);
+      "net.core.netdev_max_backlog" = pow 2 18;
+      "net.core.somaxconn" = pow 2 12;
+      "net.ipv4.tcp_syncookies" = 1;
+      "net.ipv4.tcp_tw_reuse" = mkOverride 100 1;
+      "net.ipv4.tcp_tw_recycle" = mkOverride 100 0;
+      "net.ipv4.tcp_fin_timeout" = mkOverride 100 30;
+      "net.ipv4.tcp_keepalive_time" = 60 * 20;
+      "net.ipv4.ip_local_port_range" = "10000 65000";
+      "net.ipv4.tcp_max_syn_backlog" = pow 2 13;
+      "net.ipv4.tcp_max_tw_buckets" = pow 2 12;
+      "net.ipv4.tcp_fastopen" = 3;
+      "net.ipv4.tcp_mem" = mkOverride 100 (mkTcpMem 15 16 17);
+      "net.ipv4.tcp_rmem" = mkOverride 100 (mkTcpMem 12 16 26);
+      "net.ipv4.tcp_wmem" = mkOverride 100 (mkTcpMem 12 16 26);
+      "net.ipv4.tcp_mtu_probing" = 1;
+      "net.ipv4.tcp_congestion_control" = "hybla";
+    };
+  };
+}
diff --git a/modules/nixos/soju.nix b/modules/nixos/soju.nix
new file mode 100644
index 0000000..14faf00
--- /dev/null
+++ b/modules/nixos/soju.nix
@@ -0,0 +1,117 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.soju;
+in {
+  options.nixfiles.modules.soju = {
+    enable = mkEnableOption "soju";
+
+    protocol = mkOption {
+      description = "Port.";
+      type = with types; enum ["ircs" "irc+insecure"];
+      default = "irc+insecure";
+    };
+
+    address = mkOption {
+      description = "Address.";
+      type = with types; str;
+      default = this.wireguard.ipv4.address;
+    };
+
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 6667;
+    };
+
+    domain = mkOption {
+      description = "Domain.";
+      type = with types; str;
+      default = config.networking.fqdn;
+    };
+  };
+
+  config = let
+    db = "soju";
+  in
+    mkIf cfg.enable {
+      nixfiles.modules.postgresql = {
+        enable = true;
+        extraPostStart = [
+          ''
+            $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"'
+          ''
+        ];
+      };
+
+      services.postgresql = {
+        ensureDatabases = [db];
+        ensureUsers = [
+          {
+            name = db;
+            ensurePermissions."DATABASE \"${db}\"" = "ALL";
+          }
+        ];
+      };
+
+      systemd.services.soju = {
+        description = "soju IRC bouncer";
+        wantedBy = ["multi-user.target"];
+        after = ["network-online.target" "postgresql.service"];
+        serviceConfig = {
+          ExecStart = let
+            # https://soju.im/doc/soju.1.html
+            configFile = pkgs.writeText "soju.conf" ''
+              listen ${cfg.protocol}://${cfg.address}:${toString cfg.port}
+              db postgres ${
+                concatStringsSep " " [
+                  "host=/run/postgresql"
+                  "user=${db}"
+                  "dbname=${db}"
+                  "sslmode=disable"
+                ]
+              }
+              hostname ${cfg.domain}
+              title ${cfg.domain}
+            '';
+          in
+            concatStringsSep " " [
+              "${pkgs.soju}/bin/soju"
+              "-config ${configFile}"
+            ];
+          DynamicUser = true;
+          AmbientCapabilities = [""];
+          CapabilityBoundingSet = [""];
+          UMask = "0077";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectSystem = "strict";
+          ProtectProc = "invisible";
+          ProcSubset = "pid";
+          RemoveIPC = true;
+          RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = ["@system-service" "~@privileged"];
+        };
+      };
+    };
+}
diff --git a/modules/nixos/solaar.nix b/modules/nixos/solaar.nix
new file mode 100644
index 0000000..ceff23d
--- /dev/null
+++ b/modules/nixos/solaar.nix
@@ -0,0 +1,54 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.solaar;
+in {
+  options.nixfiles.modules.solaar = {
+    enable = mkEnableOption "Solaar";
+  };
+
+  config = mkIf cfg.enable {
+    hm = {
+      home.packages = with pkgs; [solaar];
+
+      systemd.user.services.solaar = {
+        Unit = {
+          Description = "Device manager for Logitech devices";
+          After = ["graphical-session-pre.target"];
+          PartOf = ["graphical-session.target"];
+        };
+        Service = {
+          # The dirtiest hack I've ever implemented... I should be ashamed of
+          # it. Regardless, that shit still doesn't work because each reconnect,
+          # /dev/hidraw* is recreated and has default permissions which breaks
+          # Solaar. Fuck this shit.
+          ExecStartPre = let
+            pkg = pkgs.writeShellApplication {
+              name = "solaar-pre";
+              text = ''
+                for i in /dev/hidraw*; do
+                  if [ -c "$i" ]; then
+                    sudo chown root:input "$i"
+                    sudo chmod 0660 "$i"
+                  fi
+                done
+              '';
+            };
+          in "${pkg}/bin/solaar-pre";
+          ExecStart = "${pkgs.solaar}/bin/solaar --window=hide";
+        };
+        Install.WantedBy = ["graphical-session.target"];
+      };
+    };
+
+    boot.kernelModules = ["hid_logitech_dj" "hid_logitech_hidpp"];
+
+    hardware.uinput.enable = true;
+
+    my.extraGroups = ["uinput" "input"];
+  };
+}
diff --git a/modules/nixos/sonarr.nix b/modules/nixos/sonarr.nix
new file mode 100644
index 0000000..8c79175
--- /dev/null
+++ b/modules/nixos/sonarr.nix
@@ -0,0 +1,28 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.sonarr;
+in {
+  options.nixfiles.modules.sonarr = {
+    enable = mkEnableOption "Sonarr";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "sonarr.${config.networking.fqdn}";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.nginx = {
+      enable = true;
+      upstreams.sonarr.servers."127.0.0.1:8989" = {};
+      virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://sonarr";
+    };
+
+    services.sonarr.enable = true;
+  };
+}
diff --git a/modules/nixos/sound.nix b/modules/nixos/sound.nix
new file mode 100644
index 0000000..ae35e44
--- /dev/null
+++ b/modules/nixos/sound.nix
@@ -0,0 +1,21 @@
+{
+  config,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.sound;
+in {
+  options.nixfiles.modules.sound.enable =
+    mkEnableOption "sound support";
+
+  config = mkIf cfg.enable {
+    services.pipewire = {
+      enable = true;
+
+      alsa.enable = false;
+      jack.enable = false;
+      pulse.enable = true;
+    };
+  };
+}
diff --git a/modules/nixos/syncthing.nix b/modules/nixos/syncthing.nix
new file mode 100644
index 0000000..b690ab4
--- /dev/null
+++ b/modules/nixos/syncthing.nix
@@ -0,0 +1,145 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.syncthing;
+in {
+  options.nixfiles.modules.syncthing = {
+    enable = mkEnableOption "Syncthing";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "syncthing.${config.networking.fqdn}";
+    };
+  };
+
+  config = mkIf cfg.enable (mkMerge [
+    {
+      secrets = {
+        "syncthing-cert-${this.hostname}" = with config.services.syncthing; {
+          file = "${inputs.self}/secrets/syncthing-cert-${this.hostname}";
+          owner = user;
+          inherit group;
+        };
+
+        "syncthing-key-${this.hostname}" = with config.services.syncthing; {
+          file = "${inputs.self}/secrets/syncthing-key-${this.hostname}";
+          owner = user;
+          inherit group;
+        };
+      };
+
+      services.syncthing = {
+        enable = true;
+
+        user = my.username;
+        inherit (config.my) group;
+
+        dataDir = config.my.home;
+
+        guiAddress = "127.0.0.1:8384";
+
+        cert = config.secrets."syncthing-cert-${this.hostname}".path;
+        key = config.secrets."syncthing-key-${this.hostname}".path;
+
+        overrideDevices = true;
+        devices = mapAttrs (name: attr:
+          mkIf (attr.syncthing.id != null && hasAttr "wireguard" attr) {
+            inherit (attr.syncthing) id;
+            addresses = ["tcp://${name}.${config.networking.domain}:22000"];
+            introducer = this.isHeadless;
+          })
+        my.configurations;
+
+        overrideFolders = true;
+        folders = let
+          filterDevices = f:
+            attrNames (filterAttrs (_: attr:
+              (attr.hostname != this.hostname)
+              && (attr.syncthing.id != null)
+              && f attr)
+            my.configurations);
+          all = filterDevices (_: true);
+          notHeadless = filterDevices (attr: !attr.isHeadless);
+          notOther = filterDevices (attr: !attr.isOther);
+
+          simple = {
+            type = "simple";
+            params.keep = "5";
+          };
+          trashcan = {
+            type = "trashcan";
+            params.cleanoutDays = "7";
+          };
+        in
+          with config.hm.xdg.userDirs; {
+            share = {
+              path = publicShare;
+              devices = notHeadless;
+              versioning = trashcan;
+            };
+            pass = {
+              path = config.hm.programs.password-store.settings.PASSWORD_STORE_DIR;
+              devices = notOther;
+              versioning = trashcan;
+            };
+            org = {
+              path = "${documents}/org";
+              devices = all;
+              versioning = simple;
+            };
+            roam = {
+              path = "${documents}/roam";
+              devices = notOther;
+              versioning = simple;
+            };
+            elfeed = {
+              path = "${config.my.home}/.elfeed";
+              devices = notOther;
+              versioning = trashcan;
+            };
+            books = {
+              path = "${documents}/books";
+              devices = notOther;
+              versioning = trashcan;
+            };
+          };
+
+        extraOptions = {
+          gui = {
+            insecureAdminAccess = true;
+            insecureSkipHostcheck = this.isHeadless;
+          };
+          options = {
+            autoUpgradeIntervalH = 0;
+            crashReportingEnabled = false;
+            globalAnnounceEnabled = false;
+            relaysEnabled = false;
+            setLowPriority = this.isHeadless;
+            stunKeepaliveMinS = 0;
+            stunKeepaliveStartS = 0;
+            urAccepted = -1;
+          };
+        };
+      };
+
+      systemd.services.syncthing.environment.STNODEFAULTFOLDER = "yes";
+    }
+    (mkIf this.isHeadless {
+      nixfiles.modules.nginx = {
+        enable = true;
+        upstreams.syncthing.servers.${config.services.syncthing.guiAddress} = {};
+        virtualHosts.${cfg.domain} = {
+          locations."/".proxyPass = "http://syncthing";
+          extraConfig = nginxInternalOnly;
+        };
+      };
+    })
+  ]);
+}
diff --git a/modules/nixos/throttled.nix b/modules/nixos/throttled.nix
new file mode 100644
index 0000000..f182ee1
--- /dev/null
+++ b/modules/nixos/throttled.nix
@@ -0,0 +1,119 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.throttled;
+in {
+  options.nixfiles.modules.throttled.enable = mkEnableOption "Throttled";
+
+  config = mkIf cfg.enable {
+    # Disable the module we are trying to "override".
+    services.throttled.enable = mkForce false;
+
+    environment.etc."throttled.conf".text = ''
+      [GENERAL]
+      # Enable or disable the script execution
+      Enabled: True
+      # SYSFS path for checking if the system is running on AC power
+      Sysfs_Power_Path: /sys/class/power_supply/AC*/online
+      # Auto reload config on changes
+      Autoreload: True
+
+      ## Settings to apply while connected to Battery power
+      [BATTERY]
+      # Update the registers every this many seconds
+      Update_Rate_s: 30
+      # Max package power for time window #1
+      PL1_Tdp_W: 29
+      # Time window #1 duration
+      PL1_Duration_s: 28
+      # Max package power for time window #2
+      PL2_Tdp_W: 44
+      # Time window #2 duration
+      PL2_Duration_S: 0.002
+      # Max allowed temperature before throttling
+      Trip_Temp_C: 85
+      # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL)
+      cTDP: 0
+      # Disable BDPROCHOT (EXPERIMENTAL)
+      Disable_BDPROCHOT: False
+
+      ## Settings to apply while connected to AC power
+      [AC]
+      # Update the registers every this many seconds
+      Update_Rate_s: 5
+      # Max package power for time window #1
+      PL1_Tdp_W: 44
+      # Time window #1 duration
+      PL1_Duration_s: 28
+      # Max package power for time window #2
+      PL2_Tdp_W: 44
+      # Time window #2 duration
+      PL2_Duration_S: 0.002
+      # Max allowed temperature before throttling
+      Trip_Temp_C: 95
+      # Set HWP energy performance hints to 'performance' on high load (EXPERIMENTAL)
+      # Uncomment only if you really want to use it
+      # HWP_Mode: False
+      # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL)
+      cTDP: 0
+      # Disable BDPROCHOT (EXPERIMENTAL)
+      Disable_BDPROCHOT: False
+
+      # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)!
+      [UNDERVOLT.BATTERY]
+      # CPU core voltage offset (mV)
+      CORE: 0
+      # Integrated GPU voltage offset (mV)
+      GPU: 0
+      # CPU cache voltage offset (mV)
+      CACHE: 0
+      # System Agent voltage offset (mV)
+      UNCORE: 0
+      # Analog I/O voltage offset (mV)
+      ANALOGIO: 0
+
+      # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)!
+      [UNDERVOLT.AC]
+      # CPU core voltage offset (mV)
+      CORE: 0
+      # Integrated GPU voltage offset (mV)
+      GPU: 0
+      # CPU cache voltage offset (mV)
+      CACHE: 0
+      # System Agent voltage offset (mV)
+      UNCORE: 0
+      # Analog I/O voltage offset (mV)
+      ANALOGIO: 0
+
+      # [ICCMAX.AC]
+      # # CPU core max current (A)
+      # CORE:
+      # # Integrated GPU max current (A)
+      # GPU:
+      # # CPU cache max current (A)
+      # CACHE:
+
+      # [ICCMAX.BATTERY]
+      # # CPU core max current (A)
+      # CORE:
+      # # Integrated GPU max current (A)
+      # GPU:
+      # # CPU cache max current (A)
+      # CACHE:
+    '';
+
+    systemd.services.throttled = {
+      description = "Stop Intel throttling";
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.throttled}/opt/throttled/throttled.py";
+      };
+      environment.PYTHONUNBUFFERED = "1";
+      wantedBy = ["multi-user.target"];
+    };
+  };
+}
diff --git a/modules/nixos/unbound.nix b/modules/nixos/unbound.nix
new file mode 100644
index 0000000..8c40291
--- /dev/null
+++ b/modules/nixos/unbound.nix
@@ -0,0 +1,197 @@
+{
+  config,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.unbound;
+in {
+  options.nixfiles.modules.unbound = {
+    enable = mkEnableOption "Unbound";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = config.networking.domain;
+    };
+  };
+
+  config = let
+    adblock-conf = "${config.services.unbound.stateDir}/adblock.conf";
+  in
+    mkIf cfg.enable {
+      services = {
+        unbound = {
+          enable = true;
+
+          package = pkgs.unbound-with-systemd.override {
+            withRedis = true;
+            withTFO = true;
+          };
+
+          settings = {
+            server = {
+              interface = with this.wireguard; [
+                "127.0.0.1"
+                "::1"
+                ipv4.address
+                ipv6.address
+              ];
+
+              local-zone =
+                concatLists
+                (mapAttrsToList (h: _: [''"${h}.${cfg.domain}" redirect''])
+                  my.configurations);
+              local-data = concatLists (mapAttrsToList (hostname: let
+                domain = "${hostname}.${cfg.domain}";
+              in
+                attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard;
+                  [
+                    ''"${domain} 604800 IN A ${ipv4.address}"''
+                    ''"${domain} 604800 IN AAAA ${ipv6.address}"''
+                    ''"${domain}. A ${ipv4.address}"''
+                    ''"${domain}. AAAA ${ipv6.address}"''
+                  ]
+                  ++ concatMap (domain: [
+                    ''"${domain}. A ${ipv4.address}"''
+                    ''"${domain}. AAAA ${ipv6.address}"''
+                  ])
+                  attr.domains)))
+              my.configurations);
+              local-data-ptr = concatLists (mapAttrsToList (hostname: let
+                domain = "${hostname}.${cfg.domain}";
+              in
+                attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard;
+                  [
+                    ''"${ipv4.address} ${domain}"''
+                    ''"${ipv6.address} ${domain}"''
+                  ]
+                  ++ concatMap (domain: [
+                    ''"${ipv4.address} ${domain}"''
+                    ''"${ipv6.address} ${domain}"''
+                  ])
+                  attr.domains)))
+              my.configurations);
+
+              access-control = with config.nixfiles.modules.wireguard; [
+                "0.0.0.0/0 refuse"
+                "::/0 refuse"
+                "127.0.0.0/8 allow"
+                "::1/128 allow"
+                "${ipv4.subnet} allow"
+                "${ipv6.subnet} allow"
+              ];
+
+              private-domain = cfg.domain;
+              private-address = with config.nixfiles.modules.wireguard; [
+                ipv4.subnet
+                ipv6.subnet
+              ];
+
+              domain-insecure = cfg.domain;
+
+              prefetch = true;
+              prefetch-key = true;
+
+              hide-identity = true;
+              hide-version = true;
+
+              extended-statistics = true;
+
+              include = ''"${adblock-conf}"'';
+            };
+
+            forward-zone = [
+              {
+                name = ".";
+                forward-tls-upstream = true;
+                forward-addr = let
+                  mkDnsOverTls = ips: auth: map (ip: concatStrings [ip "@" auth]) ips;
+                in
+                  mkDnsOverTls dns.const.quad9.default "853#dns.quad9.net";
+              }
+            ];
+
+            cachedb = with config.services.redis.servers.unbound; {
+              backend = "redis";
+              redis-server-host = bind;
+              redis-server-port = port;
+            };
+          };
+
+          localControlSocketPath = "/run/unbound/unbound.socket";
+        };
+
+        redis = {
+          servers.unbound = {
+            enable = true;
+            bind = "127.0.0.1";
+            port = 6379;
+          };
+          vmOverCommit = mkForce true;
+        };
+
+        prometheus.exporters = {
+          unbound = {
+            enable = true;
+            listenAddress = mkDefault this.wireguard.ipv4.address;
+            port = 9167;
+            fetchType = "uds";
+            controlInterface = config.services.unbound.localControlSocketPath;
+            inherit (config.services.unbound) group user;
+          };
+
+          redis = {
+            enable = true;
+            listenAddress = mkDefault this.wireguard.ipv4.address;
+            port = mkDefault 9121;
+            extraFlags = with config.services.redis.servers.unbound; [
+              "--redis.addr=redis://${bind}:${toString port}"
+              "--redis.user=${user}"
+            ];
+          };
+        };
+      };
+
+      systemd = {
+        services = {
+          unbound.after = ["unbound-adblock-update.service"];
+
+          unbound-adblock-update = {
+            serviceConfig = with config.services.unbound; {
+              Type = "oneshot";
+              User = user;
+              Group = group;
+              ExecStart = let
+                pkg = with pkgs;
+                  writeShellApplication {
+                    name = "unbound-adblock-update";
+                    runtimeInputs = [curl package];
+                    text = ''
+                      curl \
+                        "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext" \
+                        >${adblock-conf}
+
+                      if [[ -f "${localControlSocketPath}" ]]; then
+                         unbound-control reload
+                      fi
+                    '';
+                  };
+              in "${pkg}/bin/unbound-adblock-update";
+            };
+          };
+        };
+
+        timers.unbound-adblock-update = {
+          requires = ["network-online.target"];
+          timerConfig = {
+            OnUnitActiveSec = "1d";
+            Unit = "unbound-adblock-update.service";
+          };
+          wantedBy = ["timers.target"];
+        };
+      };
+    };
+}
diff --git a/modules/nixos/vaultwarden.nix b/modules/nixos/vaultwarden.nix
new file mode 100644
index 0000000..7d51667
--- /dev/null
+++ b/modules/nixos/vaultwarden.nix
@@ -0,0 +1,134 @@
+{
+  config,
+  inputs,
+  lib,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.vaultwarden;
+in {
+  options.nixfiles.modules.vaultwarden = {
+    enable = mkEnableOption "Vaultwarden";
+
+    domain = mkOption {
+      description = "Domain name sans protocol scheme.";
+      type = with types; str;
+      default = "vaultwarden.${config.networking.domain}";
+    };
+  };
+
+  config = let
+    db = "vaultwarden";
+  in
+    mkIf cfg.enable {
+      secrets.vaultwarden-environment = {
+        file = "${inputs.self}/secrets/vaultwarden-environment";
+        owner = "vaultwarden";
+        group = "vaultwarden";
+      };
+
+      nixfiles.modules = {
+        nginx = {
+          enable = true;
+          upstreams = with config.services.vaultwarden.config; {
+            vaultwarden_rocket.servers."${ROCKET_ADDRESS}:${toString ROCKET_PORT}" = {};
+            vaultwarden_websocket.servers."${WEBSOCKET_ADDRESS}:${toString WEBSOCKET_PORT}" = {};
+          };
+          virtualHosts.${cfg.domain} = {
+            locations."/" = {
+              proxyPass = "http://vaultwarden_rocket";
+              proxyWebsockets = true;
+            };
+            locations."/notifications/hub" = {
+              proxyPass = "http://vaultwarden_websocket";
+              proxyWebsockets = true;
+            };
+            locations."/notifications/hub/negotiate" = {
+              proxyPass = "http://vaultwarden_rocket";
+              proxyWebsockets = true;
+            };
+          };
+        };
+        postgresql = {
+          enable = true;
+          extraPostStart = [
+            ''
+              $PSQL "${db}" -tAc 'GRANT ALL ON SCHEMA "public" TO "${db}"'
+            ''
+          ];
+        };
+      };
+
+      services = {
+        vaultwarden = {
+          enable = true;
+          config = {
+            TZ = config.time.timeZone;
+
+            WEB_VAULT_ENABLED = true;
+
+            DOMAIN = optionalString (cfg.domain != null) "http://${cfg.domain}";
+
+            SIGNUPS_ALLOWED = false;
+            INVITATIONS_ALLOWED = false;
+
+            ORG_CREATION_USERS = "none";
+
+            PASSWORD_HINTS_ALLOWED = false;
+            SHOW_PASSWORD_HINT = false;
+
+            ROCKET_ADDRESS = "127.0.0.1";
+            ROCKET_PORT = 8812;
+
+            WEBSOCKET_ENABLED = true;
+            WEBSOCKET_ADDRESS = "127.0.0.1";
+            WEBSOCKET_PORT = 8813;
+
+            LOG_LEVEL = "error";
+
+            DATABASE_URL = "postgresql://${db}@/${db}";
+          };
+          dbBackend = "postgresql";
+          environmentFile = config.secrets.vaultwarden-environment.path;
+        };
+
+        postgresql = {
+          ensureDatabases = [db];
+          ensureUsers = [
+            {
+              name = db;
+              ensurePermissions."DATABASE \"${db}\"" = "ALL";
+            }
+          ];
+        };
+
+        fail2ban.jails = mkIf config.nixfiles.modules.fail2ban.enable {
+          vaultwarden = ''
+            enabled = true
+            filter = vaultwarden
+            port = http,https
+          '';
+          vaultwarden-admin = ''
+            enabled = true
+            filter = vaultwarden-admin
+            port = http,https
+          '';
+        };
+      };
+
+      environment.etc = mkIf config.nixfiles.modules.fail2ban.enable {
+        "fail2ban/filter.d/vaultwarden.conf".text = ''
+          [Definition]
+          failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
+          ignoreregex =
+          journalmatch = _SYSTEMD_UNIT=vaultwarden.service
+        '';
+        "fail2ban/filter.d/vaultwarden-admin.conf".text = ''
+          [Definition]
+          failregex = ^.*Invalid admin token\. IP: <ADDR>.*$
+          ignoreregex =
+          journalmatch = _SYSTEMD_UNIT=vaultwarden.service
+        '';
+      };
+    };
+}
diff --git a/modules/nixos/wireguard.nix b/modules/nixos/wireguard.nix
new file mode 100644
index 0000000..d05c6ae
--- /dev/null
+++ b/modules/nixos/wireguard.nix
@@ -0,0 +1,198 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.wireguard;
+in {
+  options.nixfiles.modules.wireguard = {
+    client = {
+      enable = mkEnableOption "WireGuard client";
+
+      enableTrafficRouting = mkOption {
+        description = "Whether to enable traffic routing through the sever.";
+        type = with types; bool;
+        default = !this.isHeadless;
+      };
+    };
+
+    server = {
+      enable = mkEnableOption "WireGuard server";
+
+      ipv4.address = mkOption {
+        description = "IPv4 address to bind to.";
+        type = with types; str;
+        default = my.configurations.manwe.wireguard.ipv4.address;
+      };
+
+      ipv6.address = mkOption {
+        description = "IPv4 address to bind to.";
+        type = with types; str;
+        default = my.configurations.manwe.wireguard.ipv6.address;
+      };
+
+      address = mkOption {
+        description = "Endpoint address to use";
+        type = with types; str;
+        default = my.configurations.manwe.ipv4.address;
+      };
+
+      port = mkOption {
+        description = "Endpoint port to use.";
+        type = with types; int;
+        default = 6969;
+      };
+
+      publicKey = mkOption {
+        description = "Server's public key.";
+        type = with types; str;
+        default = my.configurations.manwe.wireguard.publicKey;
+      };
+
+      peers = mkOption {
+        description = "List of peers.";
+        type = with types; listOf attrs;
+        default = mapAttrsToList (_: attr:
+          with attr; {
+            inherit (wireguard) publicKey;
+            allowedIPs = with wireguard; [
+              "${ipv4.address}/32"
+              "${ipv6.address}/128"
+            ];
+          }) (filterAttrs (_: attr:
+          attr.hostname != this.hostname && hasAttr "wireguard" attr)
+        my.configurations);
+      };
+    };
+
+    interface = mkOption {
+      description = "Name of the interface to use WireGuard with.";
+      type = with types; str;
+      default = "wg69";
+    };
+
+    ipv4.subnet = mkOption {
+      description = "CIDR notation for the IPv4 subnet to use over WireGuard.";
+      type = with types; str;
+      default = "10.69.0.0/16";
+    };
+
+    ipv6.subnet = mkOption {
+      description = "CIDR notation for the IPv6 subnet to use over WireGuard.";
+      type = with types; str;
+      default = "fd69::/16";
+    };
+  };
+
+  config =
+    {
+      assertions = [
+        {
+          assertion = config.security.sudo.enable;
+          message = "Sudo is not enabled.";
+        }
+        {
+          assertion = any (x: x == "wheel") config.my.extraGroups;
+          message = ''User is not in the "wheel" group.'';
+        }
+      ];
+    }
+    // mkMerge [
+      (mkIf (cfg.client.enable || cfg.server.enable) {
+        secrets."wireguard-private-key-${this.hostname}".file = "${inputs.self}/secrets/wireguard-private-key-${this.hostname}";
+
+        networking.firewall.trustedInterfaces = [cfg.interface];
+      })
+      (mkIf cfg.client.enable {
+        networking.wg-quick.interfaces.${cfg.interface} = mkMerge [
+          (with this.wireguard; {
+            privateKeyFile = config.secrets."wireguard-private-key-${this.hostname}".path;
+            address = ["${ipv4.address}/16" "${ipv6.address}/16"];
+          })
+          (with cfg.server; {
+            peers = [
+              {
+                inherit publicKey;
+                endpoint = "${address}:${toString port}";
+                allowedIPs =
+                  if cfg.client.enableTrafficRouting
+                  then [
+                    "0.0.0.0/0"
+                    "::/0"
+                  ]
+                  else [
+                    cfg.ipv4.subnet
+                    cfg.ipv6.subnet
+                  ];
+                persistentKeepalive = 25;
+              }
+            ];
+            dns = [
+              ipv4.address
+              ipv6.address
+            ]; # This assumes that the host has Unbound running.
+          })
+        ];
+
+        environment.systemPackages = with pkgs; [
+          (writeShellApplication {
+            name = "wg-toggle";
+            runtimeInputs = [iproute2 jq wireguard-tools];
+            text = ''
+              ip46() {
+                sudo ip -4 "$@"
+                sudo ip -6 "$@"
+              }
+
+              fwmark=$(sudo wg show ${cfg.interface} fwmark) || exit
+              if ip -j rule list lookup "$fwmark" | jq -e 'length > 0' >/dev/null; then
+                  ip46 rule del lookup main suppress_prefixlength 0
+                  ip46 rule del lookup "$fwmark"
+              else
+                  ip46 rule add not fwmark "$fwmark" lookup "$fwmark"
+                  ip46 rule add lookup main suppress_prefixlength 0
+              fi
+            '';
+          })
+        ];
+      })
+      (mkIf cfg.server.enable {
+        networking = {
+          wireguard = {
+            enable = true;
+            interfaces.${cfg.interface} = with cfg.server; {
+              privateKeyFile = config.secrets."wireguard-private-key-${this.hostname}".path;
+              ips = ["${ipv4.address}/16" "${ipv6.address}/16"];
+              listenPort = port;
+              inherit peers;
+              allowedIPsAsRoutes = false;
+            };
+          };
+
+          nat = {
+            enable = true;
+            enableIPv6 = true;
+
+            externalInterface = mkDefault "eth0";
+
+            internalInterfaces = [cfg.interface];
+            internalIPs = [cfg.ipv4.subnet];
+            internalIPv6s = [cfg.ipv6.subnet];
+          };
+
+          firewall.allowedUDPPorts = [cfg.server.port];
+        };
+
+        services.prometheus.exporters.wireguard = {
+          enable = true;
+          listenAddress = mkDefault this.wireguard.ipv4.address;
+          withRemoteIp = true;
+          port = 9586;
+        };
+      })
+    ];
+}
diff --git a/modules/nixos/x11.nix b/modules/nixos/x11.nix
new file mode 100644
index 0000000..cd8dfbe
--- /dev/null
+++ b/modules/nixos/x11.nix
@@ -0,0 +1,92 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.x11;
+in {
+  options.nixfiles.modules.x11.enable = mkEnableOption "X11";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.fonts.enable = true;
+
+    hm = {
+      home.sessionVariables = with config.dirs; {
+        XCOMPOSEFILE = "${cache}/XComposeFile";
+        XCOMPOSECACHE = "${cache}/XComposeCache";
+      };
+
+      xsession.scriptPath = ".xinitrc";
+
+      xresources.properties =
+        (let
+          font = with config.fontScheme.monospaceFont; "${family}:style=${style}:size=${toString size}";
+        in {
+          "*.font" = font;
+
+          "Xft.antialias" = 1;
+          "Xft.autohint" = 0;
+          "Xft.dpi" = 96;
+          "Xft.hinting" = 1;
+          "Xft.hintstyle" = "hintslight";
+          "Xft.lcdfilter" = "lcddefault";
+          "Xft.rgba" = "rgb";
+        })
+        // (with config.colourScheme; {
+          "*.color0" = black;
+          "*.color8" = brightBlack;
+          "*.color1" = red;
+          "*.color9" = brightRed;
+          "*.color2" = green;
+          "*.color10" = brightGreen;
+          "*.color3" = yellow;
+          "*.color11" = brightYellow;
+          "*.color4" = blue;
+          "*.color12" = brightBlue;
+          "*.color5" = magenta;
+          "*.color13" = brightMagenta;
+          "*.color6" = cyan;
+          "*.color14" = brightCyan;
+          "*.color7" = white;
+          "*.color15" = brightWhite;
+
+          "*.background" = background;
+          "*.foreground" = foreground;
+        });
+    };
+
+    services.xserver = {
+      enable = true;
+
+      tty = mkDefault 1;
+
+      autoRepeatDelay = 200;
+      autoRepeatInterval = 25;
+
+      libinput.enable = true;
+
+      monitorSection = ''
+        Option "DPMS" "false"
+      '';
+
+      serverFlagsSection = ''
+        Option "BlankTime" "0"
+        Option "OffTime" "0"
+        Option "StandbyTime" "0"
+        Option "SuspendTime" "0"
+      '';
+
+      inputClassSections = [
+        ''
+          Identifier "Mouse"
+          MatchIsPointer "yes"
+          Option "AccelerationNumerator" "2"
+          Option "AccelerationDenominator" "1"
+          Option "AccelerationThreshold" "4"
+        ''
+      ];
+    };
+  };
+}
diff --git a/modules/nixos/xmonad.nix b/modules/nixos/xmonad.nix
new file mode 100644
index 0000000..2cc7ad6
--- /dev/null
+++ b/modules/nixos/xmonad.nix
@@ -0,0 +1,28 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.xmonad;
+in {
+  options.nixfiles.modules.xmonad.enable = mkEnableOption "XMonad";
+
+  config = mkIf cfg.enable {
+    nixfiles.modules.x11.enable = true;
+
+    hm = {
+      xsession = {
+        enable = true;
+
+        scriptPath = ".xinitrc";
+
+        windowManager.command = "${pkgs.xmonad-ng}/bin/xmonad-ng";
+      };
+    };
+
+    services.xserver.displayManager.startx.enable = true;
+  };
+}

Consider giving Nix/NixOS a try! <3