summaryrefslogtreecommitdiff
path: root/modules/nixos
diff options
context:
space:
mode:
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;
+ };
+}