about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAzat Bahawi <azat@bahawi.net>2023-04-01 04:39:59 +0300
committerAzat Bahawi <azat@bahawi.net>2023-04-01 04:39:59 +0300
commitf4145939712b0046e5d57906d4b157b8a150614d (patch)
tree3c8d24db6410692e0fa59570ff698d446ae5a96e
parent2023-03-23 (diff)
2023-04-01
-rw-r--r--flake.lock48
-rw-r--r--flake.nix14
-rw-r--r--modules/common/emacs/default.nix298
-rw-r--r--modules/common/qutebrowser.nix4
-rw-r--r--modules/nixos/firefox/userContent.css9
-rw-r--r--modules/nixos/monitoring/dashboards/ntfy.json2362
-rw-r--r--modules/nixos/monitoring/default.nix47
-rw-r--r--modules/nixos/ntfy.nix31
-rw-r--r--modules/nixos/postgresql.nix5
-rw-r--r--modules/nixos/promtail.nix24
-rw-r--r--modules/nixos/soju.nix15
-rw-r--r--nixosConfigurations/eonwe/default.nix30
12 files changed, 2685 insertions, 202 deletions
diff --git a/flake.lock b/flake.lock
index 8b7ec9c..f14ca60 100644
--- a/flake.lock
+++ b/flake.lock
@@ -240,11 +240,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1679265143,
-        "narHash": "sha256-5RDMW+O4owjdPz7t4K4YxH2fOHCNOcyVmSiKRUikiv0=",
+        "lastModified": 1680114304,
+        "narHash": "sha256-XymtLu8G2nzenjDUWI7XV2MMHztvPkEZUFpwmZFcxVM=",
         "owner": "nix-community",
         "repo": "home-manager",
-        "rev": "1b8bf5c3270386a1b6850bd77d79dbdbaf0d7a7c",
+        "rev": "0e065e1b6f0776ebbacea9dcbc977af7bc9eddc0",
         "type": "github"
       },
       "original": {
@@ -296,11 +296,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1679276580,
-        "narHash": "sha256-3+YDy2BQuIWauD7oHZZDU0uRC2c0cOWdyRZW3ss76VY=",
+        "lastModified": 1680140339,
+        "narHash": "sha256-P5rexqb5iOiRQFfqJj8YItgG0B5rKncEZJHJPRcTYQI=",
         "owner": "Infinidoge",
         "repo": "nix-minecraft",
-        "rev": "710b88f976c522deb1306b0310429b4bef3cff39",
+        "rev": "9c6b1d8bab44b77bc19f201c472b73a2ce4f7dad",
         "type": "github"
       },
       "original": {
@@ -312,11 +312,11 @@
     },
     "nixos-hardware": {
       "locked": {
-        "lastModified": 1679224149,
-        "narHash": "sha256-TSY37Zv0icF/aijR3/KWGLVBlnKKHlG9QTj7vHbF/UU=",
+        "lastModified": 1680070330,
+        "narHash": "sha256-aoT2YZCd9LEtiEULFLIF0ykKydgE72X8gw/k9/pRS5I=",
         "owner": "NixOS",
         "repo": "nixos-hardware",
-        "rev": "a4bc66709604ab78abc575b60baa6d23ae027a59",
+        "rev": "a6aa8174fa61e55bd7e62d35464d3092aefe0421",
         "type": "github"
       },
       "original": {
@@ -328,11 +328,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1679319606,
-        "narHash": "sha256-wyEMIZB6BnsmJWInEgDZu66hXVMGJEZFl5uDsn27f9M=",
+        "lastModified": 1680121753,
+        "narHash": "sha256-rB3ZnNodA4o8Rq0sKZ0t/YdY/MRon4V+bW1b20adgr4=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "8bc6945b1224a1cfa679d6801580b1054dba1a5c",
+        "rev": "e779e17ebdf3907ce08c75c7c1b2dc8a1c5038c9",
         "type": "github"
       },
       "original": {
@@ -344,11 +344,11 @@
     },
     "nixpkgs-master": {
       "locked": {
-        "lastModified": 1679348130,
-        "narHash": "sha256-i16avZCx5Wf2ntiGltopk37+xX2hM1BHf+/iuOFp0hw=",
+        "lastModified": 1680186142,
+        "narHash": "sha256-Ypjc8vzstTbxJhSkaNQcRUXuE0NeNKqBHMaYr2F887Y=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "862746a32f1921a5928062e4b3c4feb055c440be",
+        "rev": "f13bb7efd2b6eefb3bb8b62b3159f888f0264985",
         "type": "github"
       },
       "original": {
@@ -360,11 +360,11 @@
     },
     "nixpkgs-stable": {
       "locked": {
-        "lastModified": 1679338459,
-        "narHash": "sha256-3iRGwvIh8h9HrqgYb5KTgheysn6ZvUfttmluW0AKcXs=",
+        "lastModified": 1680183455,
+        "narHash": "sha256-lhlRyEZE1CZ2B5h4goEbS3Env7G6ORojj0/+AAGOooM=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "448db6f9987e95a5fc4abc022be005ec8355b452",
+        "rev": "646930ff24273c6d91fb3ea37531261553043b50",
         "type": "github"
       },
       "original": {
@@ -410,11 +410,11 @@
     },
     "nur": {
       "locked": {
-        "lastModified": 1679341557,
-        "narHash": "sha256-cMj6zzH0cUdd2nMLvplQDO5cfViZB33WApPHtKVDHXI=",
+        "lastModified": 1680183987,
+        "narHash": "sha256-ww5/H0ZmV8ztnrDaL7GX2zLW82yURgrTebGEd4nTx6Y=",
         "owner": "nix-community",
         "repo": "NUR",
-        "rev": "d7a59b9f787b1ded42c76969ae3c720024687a24",
+        "rev": "fa953d3867d6f133eb4d72234d8dd0fa68d007e0",
         "type": "github"
       },
       "original": {
@@ -466,11 +466,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1678976941,
-        "narHash": "sha256-skNr08frCwN9NO+7I77MjOHHAw+L410/37JknNld+W4=",
+        "lastModified": 1680170909,
+        "narHash": "sha256-FtKU/edv1jFRr/KwUxWTYWXEyj9g8GBrHntC2o8oFI8=",
         "owner": "cachix",
         "repo": "pre-commit-hooks.nix",
-        "rev": "32b1dbedfd77892a6e375737ef04d8efba634e9e",
+        "rev": "29dbe1efaa91c3a415d8b45d62d48325a4748816",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index ae5d91a..9af2e05 100644
--- a/flake.nix
+++ b/flake.nix
@@ -136,15 +136,6 @@
       };
     };
 
-    # TODO Integrate into Altertmanager.
-    # alertmanager-gotify = {
-    #   type = "git";
-    #   url = "https://git.mbosch.me/ma27/alertmanager-gotify";
-    #   inputs = {
-    #     flake-utils.follows = "flake-utils";
-    #     nixpkgs.follows = "nixpkgs";
-    #   };
-    # };
     alertmanager-ntfy = {
       type = "github";
       owner = "pinpox";
@@ -275,7 +266,10 @@
           '';
 
         checks.preCommit = pre-commit-hooks.lib.${system}.run {
-          src = ./.;
+          src = builtins.path {
+            name = "nixfiles";
+            path = ./.;
+          };
           hooks = {
             alejandra.enable = true;
             deadnix.enable = true;
diff --git a/modules/common/emacs/default.nix b/modules/common/emacs/default.nix
index 3be560e..5499d48 100644
--- a/modules/common/emacs/default.nix
+++ b/modules/common/emacs/default.nix
@@ -25,136 +25,165 @@ in {
     };
 
     hm = {
-      xdg.configFile = {
+      xdg.configFile = mapAttrs (_: value:
+        value
+        // {
+          onChange = with config.hm.programs; ''
+            export EMACSDIR="''${XDG_CONFIG_HOME:-$HOME/.config}/emacs"
+            export DOOMDIR="''${XDG_CONFIG_HOME:-$HOME/.config}/doom"
+
+            if [[ ! -d "$EMACSDIR/.git" ]]; then
+              ${git.package}/bin/git clone --depth=1 --branch=master \
+                "https://github.com/doomemacs/doomemacs" "$EMACSDIR"
+            fi
+
+            if [[ ! -d "$DOOMDIR" ]]; then
+              mkdir -p "$DOOMDIR/snippets"
+            fi
+
+            if [[ -x "$EMACSDIR/bin/doom" ]]; then
+              if [[ ! -d "$EMACSDIR/.local" ]]; then
+                PATH="''${PATH:-/bin:/usr/bin:/usr/local/bin}:${emacs.package}/bin:${git.package}/bin" \
+                  "$EMACSDIR/bin/doom" install --force --verbose
+              fi
+
+              PATH="''${PATH:-/bin:/usr/bin:/usr/local/bin}:${emacs.package}/bin:${git.package}/bin" \
+                "$EMACSDIR/bin/doom" sync -e -p --force --verbose
+            fi
+          '';
+        }) {
         "doom/init.el".source = ./doom/init.el;
         "doom/packages.el".source = ./doom/packages.el;
         "doom/config.el" = {
-          text = concatStringsSep "\n" [
-            (let
-              # NOTE gopls will require the "go" executable which must be provided
-              # by the project's flake/shell.
-              extraBins = with pkgs;
-                [
-                  (aspellWithDicts (p: with p; [en ru])) # :checkers (spell +aspell)
-                  (python3.withPackages (p:
-                    with p; [
-                      black # :lang python :editor format
-                      isort # :lang python
-                      pyflakes # :lang python
-                      python-lsp-server # :lang (python +lsp)
-                    ]))
-                  asmfmt # :editor format
-                  bash-language-server # :lang (sh +lsp)
-                  clang-tools # :lang (cc +lsp) :editor format
-                  cmake # :term vterm
-                  cmake-format # :lang cc :editor format
-                  cmigemo # :lang japanese
-                  css-language-server # :lang (web +lsp)
-                  dhall-language-server # :lang (dhall +lsp)
-                  dockerfile-language-server # :tools (docker +lsp)
-                  editorconfig # :tools editorconfig
-                  fd # doom!
-                  gcc # :tools magit :term vterm
-                  gnuplot # :lang (org +gnuplot)
-                  gnutls # doom!
-                  go-language-server # :lang (go +lsp)
-                  gomodifytags # :lang go
-                  gore # :lang go
-                  gotests # :lang go
-                  gotools # :lang go
-                  graphviz # :lang (org +roam2) :lang plantuml
-                  html-language-server # :lang (web +lsp)
-                  html-tidy # :lang web
-                  jre # :lang plantuml
-                  json-language-server # :lang (json +lsp)
-                  libtool # :term vterm
-                  nix-language-server # :lang (nix +lsp)
-                  nodePackages.eslint # :lang (json +lsp)
-                  nodePackages.js-beautify # :lang web
-                  nodePackages.prettier # :editor format
-                  nodePackages.stylelint # :lang web
-                  nodejs # :tools debugger
-                  pandoc # :lang org markdown latex
-                  perl # term vterm
-                  pinentry-emacs # doom!
-                  pre-commit # :tools magit
-                  ripgrep # doom!
-                  rust-analyzer # :lang (rust +lsp)
-                  rustfmt # :lang rust
-                  shellcheck # :lang sh
-                  shfmt # :lang sh :editor format
-                  sqlite # :lang (org +roam2) :tools lookup
-                  texlab # lang (tex +lsp)
-                  texlive.combined.scheme-full # :lang org tex
-                  unzip # :tools debugger
-                  wordnet # :tools (lookup +dictionary +offline)
-                  yaml-language-server # :lang (yaml +lsp)
-                  zls # :lang (zig +lsp)
-                  zstd # :emacs undo
-                ]
-                ++ (
-                  #
-                  # GDB doesn't support[1] Apple Silicon on MacOS.
-                  #
-                  # [1]: https://inbox.sourceware.org/gdb/6b48224b-9e2e-518d-793b-df4fc5514884@arm.com/
-                  if (this.system != "aarch64-darwin")
-                  then [gdb] # :tools debugger
-                  else [lldb] # :tools debugger
-                )
-                ++ optionals (!pkgs.stdenv.isDarwin)
-                [
-                  # NOTE Haskell is pretty much broken every couple of days on
-                  # MacOS and I usually don't write anything in Haskell while
-                  # I'm on my work laptop, so... ShellCheck seems to be working,
-                  # though.
-                  haskellPackages.ormolu # :lang haskell :editor format
-                  haskellPackages.haskell-language-server # :lang (haskell +lsp)
-                  haskellPackages.cabal-fmt # :lang haskell :editor format
-                  haskellPackages.cabal-install # :lang haskell
-                  haskellPackages.hoogle # :lang haskell
-                ];
-            in ''
-              ;; This will integrate packages which are required by various
-              ;; modules without polluting the user's profile.
-              (setq exec-path (append exec-path '(${
-                concatMapStringsSep " " (x: ''"${x}/bin"'') extraBins
-              })))
-              (setenv "PATH" (concat (getenv "PATH") ":${
-                concatMapStringsSep ":" (x: "${x}/bin") extraBins
-              }"))
-
-              (appendq! auth-sources '("${config.secrets.authinfo.path}"))
-
-              ;; Font must be set to N+2 because otherwise it looks too small.
-              (setq doom-font (font-spec
-                                :family "${config.fontScheme.monospaceFont.family}"
-                                :size ${toString (config.fontScheme.monospaceFont.size + 2)})
-                    doom-unicode-font doom-font)
-
-              ;; :app irc
-              (setq circe-default-nick "${my.username}"
-                    circe-default-realname "${my.email}"
-                    circe-default-user circe-default-nick)
-
-              ;; :lang plantuml
-              (setq org-plantuml-jar-path "${pkgs.plantuml}/lib/plantuml.jar")
-
-              ;; :input japanese
-              (setq migemo-dictionary "${pkgs.cmigemo}/share/migemo/utf-8/migemo-dict")
-
-              ;; :input japanese
-              (setq skk-large-jisyo "${pkgs.skk-dicts}/share/skk/SKK-JISYO.L")
-
-              ;; :lang nix
-              (setq nix-nixfmt-bin "${pkgs.writeShellScript "nixfmt" ''
-                ${pkgs.alejandra}/bin/alejandra --quiet "$@"
-              ''}")
-            '')
+          text = concatLines [
+            (
+              let
+                # NOTE gopls will require the "go" executable which must be provided
+                # by the project's flake/shell.
+                extraBins = with pkgs;
+                  [
+                    (aspellWithDicts (p: with p; [en ru])) # :checkers (spell +aspell)
+                    (python3.withPackages (p:
+                      with p; [
+                        black # :lang python :editor format
+                        isort # :lang python
+                        pyflakes # :lang python
+                        python-lsp-server # :lang (python +lsp)
+                      ]))
+                    asmfmt # :editor format
+                    bash-language-server # :lang (sh +lsp)
+                    clang-tools # :lang (cc +lsp) :editor format
+                    cmake # :term vterm
+                    cmake-format # :lang cc :editor format
+                    cmigemo # :lang japanese
+                    css-language-server # :lang (web +lsp)
+                    dhall-language-server # :lang (dhall +lsp)
+                    dockerfile-language-server # :tools (docker +lsp)
+                    editorconfig # :tools editorconfig
+                    fd # doom!
+                    gcc # :tools magit :term vterm
+                    gnumake # :term vterm
+                    gnuplot # :lang (org +gnuplot)
+                    gnutls # doom!
+                    go-language-server # :lang (go +lsp)
+                    gomodifytags # :lang go
+                    gore # :lang go
+                    gotests # :lang go
+                    gotools # :lang go
+                    graphviz # :lang (org +roam2) :lang plantuml
+                    html-language-server # :lang (web +lsp)
+                    html-tidy # :lang web
+                    jre # :lang plantuml
+                    json-language-server # :lang (json +lsp)
+                    libtool # :term vterm
+                    nix-language-server # :lang (nix +lsp)
+                    nodePackages.eslint # :lang (json +lsp)
+                    nodePackages.js-beautify # :lang web
+                    nodePackages.prettier # :editor format
+                    nodePackages.stylelint # :lang web
+                    nodejs # :tools debugger
+                    pandoc # :lang org markdown latex
+                    perl # term vterm
+                    pinentry-emacs # doom!
+                    pre-commit # :tools magit
+                    ripgrep # doom!
+                    rust-analyzer # :lang (rust +lsp)
+                    rustfmt # :lang rust
+                    shellcheck # :lang sh
+                    shfmt # :lang sh :editor format
+                    sqlite # :lang (org +roam2) :tools lookup
+                    texlab # lang (tex +lsp)
+                    texlive.combined.scheme-full # :lang org tex
+                    unzip # :tools debugger
+                    wordnet # :tools (lookup +dictionary +offline)
+                    yaml-language-server # :lang (yaml +lsp)
+                    zls # :lang (zig +lsp)
+                    zstd # :emacs undo
+                  ]
+                  ++ (
+                    # GDB doesn't support[1] Apple Silicon.
+                    #
+                    # [1]: https://inbox.sourceware.org/gdb/6b48224b-9e2e-518d-793b-df4fc5514884@arm.com/
+                    if (this.system != "aarch64-darwin")
+                    then [gdb] # :tools debugger
+                    else [lldb] # :tools debugger
+                  )
+                  ++ optionals (!pkgs.stdenv.isDarwin)
+                  [
+                    # NOTE Haskell is pretty much broken every couple of days on
+                    # MacOS and I usually don't write anything in Haskell while
+                    # I'm on my work laptop, so... ShellCheck seems to be working,
+                    # though.
+                    haskellPackages.ormolu # :lang haskell :editor format
+                    haskellPackages.haskell-language-server # :lang (haskell +lsp)
+                    haskellPackages.cabal-fmt # :lang haskell :editor format
+                    haskellPackages.cabal-install # :lang haskell
+                    haskellPackages.hoogle # :lang haskell
+                  ];
+              in ''
+                ;; This will integrate packages which are required by various
+                ;; modules without polluting the user's profile.
+                (setq exec-path (append exec-path '(${
+                  concatMapStringsSep " " (x: ''"${x}/bin"'') extraBins
+                })))
+                (setenv "PATH" (concat (getenv "PATH") ":${
+                  concatMapStringsSep ":" (x: "${x}/bin") extraBins
+                }"))
+
+                (appendq! auth-sources '("${config.secrets.authinfo.path}"))
+
+                ;; Font must be set to N+2 because otherwise it looks too small.
+                (setq doom-font (font-spec
+                                  :family "${config.fontScheme.monospaceFont.family}"
+                                  :size ${toString (config.fontScheme.monospaceFont.size + 2)})
+                      doom-unicode-font doom-font)
+
+                ;; :app irc
+                (setq circe-default-nick "${my.username}"
+                      circe-default-realname "${my.email}"
+                      circe-default-user circe-default-nick)
+
+                ;; :lang plantuml
+                (setq org-plantuml-jar-path "${pkgs.plantuml}/lib/plantuml.jar")
+
+                ;; :input japanese
+                (setq migemo-dictionary "${pkgs.cmigemo}/share/migemo/utf-8/migemo-dict")
+
+                ;; :input japanese
+                ;; (setq skk-large-jisyo "${pkgs.skk-dicts}/share/skk/SKK-JISYO.L")
+
+                ;; :lang nix
+                (setq nix-nixfmt-bin "${pkgs.writeShellScript "nixfmt" ''
+                  ${pkgs.alejandra}/bin/alejandra --quiet "$@"
+                ''}")
+              ''
+            )
             (with config.hm.accounts.email; let
               mu4eAccounts = let
                 muAccounts = filter (a: a.mu.enable) (attrValues accounts);
               in
-                concatMapStringsSep "\n" (a:
+                concatMapStringsSep "\n"
+                (a:
                   with a; let
                     personalAddresses = concatMapStringsSep " " (v: ''"${v}"'') aliases;
                   in ''
@@ -177,29 +206,6 @@ in {
             '')
             (builtins.readFile ./doom/config.el)
           ];
-          onChange = with config.hm.programs; ''
-            export DOOMDIR="$HOME/.config/doom"
-            export EMACSDIR="$HOME/.config/emacs"
-
-            if [[ ! -d "$EMACSDIR/.git" ]]; then
-              ${git.package}/bin/git clone --depth=1 --branch=master \
-                "https://github.com/doomemacs/doomemacs" "$EMACSDIR"
-            fi
-
-            if [[ ! -d "$DOOMDIR" ]]; then
-              mkdir -p "$DOOMDIR/snippets"
-            fi
-
-            if [[ -x "$EMACSDIR/bin/doom" ]]; then
-              oldpath="$PATH"
-              export PATH="''${PATH:-/bin}:${emacs.package}/bin:${git.package}/bin"
-
-              "$EMACSDIR/bin/doom" sync -e -p --force --verbose
-
-              export PATH="$oldpath"
-              unset oldpath
-            fi
-          '';
         };
       };
 
diff --git a/modules/common/qutebrowser.nix b/modules/common/qutebrowser.nix
index 68a41a5..7913001 100644
--- a/modules/common/qutebrowser.nix
+++ b/modules/common/qutebrowser.nix
@@ -509,7 +509,7 @@ in {
             }
           ];
         in
-          concatStringsSep "\n" final + "\n")
+          concatLines final + "\n")
         + (let
           allowSetting = setting: url: "config.set('content.${setting}', True, '${url}')";
 
@@ -530,7 +530,7 @@ in {
 
           final = allowedMediaCapture ++ allowedNotifications;
         in
-          concatStringsSep "\n" final + "\n");
+          concatLines final + "\n");
     };
   };
 }
diff --git a/modules/nixos/firefox/userContent.css b/modules/nixos/firefox/userContent.css
index 2de8cde..2b515e3 100644
--- a/modules/nixos/firefox/userContent.css
+++ b/modules/nixos/firefox/userContent.css
@@ -158,6 +158,15 @@
     }
 }
 
+@-moz-document regexp("https?://grafana\.com/docs/.*")
+{
+    .ads__content,
+    .scroll,
+    .sticky-footer {
+        display: none !important;
+    }
+}
+
 @-moz-document regexp("https?://habr\.com/(ru|en)/(article|company/.*/news|blog|post)/.*")
 {
     .Vue-Toastification__container,
diff --git a/modules/nixos/monitoring/dashboards/ntfy.json b/modules/nixos/monitoring/dashboards/ntfy.json
new file mode 100644
index 0000000..fd02a2e
--- /dev/null
+++ b/modules/nixos/monitoring/dashboards/ntfy.json
@@ -0,0 +1,2362 @@
+{
+  "annotations": {
+    "list": [
+      {
+        "builtIn": 1,
+        "datasource": {
+          "type": "grafana",
+          "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"
+      }
+    ]
+  },
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "graphTooltip": 0,
+  "id": 75,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "collapsed": false,
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 0
+      },
+      "id": 38,
+      "panels": [],
+      "title": "Overview",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "light-green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 0,
+        "y": 1
+      },
+      "id": 36,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "last"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.4.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "ntfy_messages_published_success{job=\"$job\"}",
+          "legendFormat": "Messages cached",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Published",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "orange",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 4,
+        "y": 1
+      },
+      "id": 33,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "last"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.4.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_messages_cached_total{job=\"$job\"}",
+          "legendFormat": "Messages cached",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Cached",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "#69bfb5",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 8,
+        "y": 1
+      },
+      "id": 31,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "last"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.4.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_visitors_total{job=\"$job\"}",
+          "legendFormat": "Visitors",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Visitors",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 12,
+        "y": 1
+      },
+      "id": 32,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "last"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.4.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_users_total{job=\"$job\"}",
+          "legendFormat": "Visitors",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Users",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "blue",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 16,
+        "y": 1
+      },
+      "id": 34,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "last"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.4.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_topics_total{job=\"$job\"}",
+          "legendFormat": "Topics",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Topics",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "description": "",
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "thresholds"
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "purple",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 4,
+        "x": 20,
+        "y": 1
+      },
+      "id": 35,
+      "options": {
+        "colorMode": "value",
+        "graphMode": "none",
+        "justifyMode": "auto",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "last"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "pluginVersion": "9.4.7",
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_subscribers_total",
+          "legendFormat": "Subscribers",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Subscribers",
+      "type": "stat"
+    },
+    {
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 4
+      },
+      "id": 10,
+      "title": "Metrics",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "description": "Number of successfully published messages, and messages that could not be published (due to rate limiting, bad formatting, etc.)",
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Failed"
+            },
+            "properties": [
+              {
+                "id": "custom.axisColorMode",
+                "value": "text"
+              },
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 0,
+        "y": 5
+      },
+      "id": 42,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_messages_published_success{job=\"$job\"}[$rate])",
+          "legendFormat": "Success",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_messages_published_failure{job=\"$job\"}[$rate])",
+          "hide": false,
+          "legendFormat": "Failed",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Messages published (per second)",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "description": "Number of messages published since last ntfy server restart",
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Failed"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 6,
+        "y": 5
+      },
+      "id": 4,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_messages_published_success{job=\"$job\"}",
+          "legendFormat": "Successful",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_messages_published_failure{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Failed",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Messages published",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "description": "Number of messages currently stored in message cache",
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 12,
+        "y": 5
+      },
+      "id": 2,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_messages_cached_total{job=\"$job\"}",
+          "legendFormat": "Messages in database",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Messages cached",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 18,
+        "y": 5
+      },
+      "id": 14,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_visitors_total{job=\"$job\"}",
+          "legendFormat": "Visitors",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_topics_total{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Topics",
+          "range": true,
+          "refId": "B"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_subscribers_total{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Subscribers",
+          "range": true,
+          "refId": "C"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_users_total{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Users",
+          "range": true,
+          "refId": "D"
+        }
+      ],
+      "title": "Visitors, subscribers, topics",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 0,
+        "y": 12
+      },
+      "id": 43,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "sum by(job) (rate(ntfy_http_requests_total{job=\"$job\"}[$rate]))",
+          "legendFormat": "Requests per second",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "HTTP requests (per second)",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 9,
+        "x": 6,
+        "y": 12
+      },
+      "id": 41,
+      "options": {
+        "legend": {
+          "calcs": [
+            "mean"
+          ],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true,
+          "sortBy": "Mean",
+          "sortDesc": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "sum by(http_code) (rate(ntfy_http_requests_total{job=\"$job\", http_code!=\"200\", http_code!=\"429\", http_code!=\"507\"}[$rate]))",
+          "legendFormat": "{{http_code}}",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "HTTP errors (per second, excl. 429/507)",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 9,
+        "x": 15,
+        "y": 12
+      },
+      "id": 16,
+      "options": {
+        "legend": {
+          "calcs": [
+            "mean"
+          ],
+          "displayMode": "table",
+          "placement": "right",
+          "showLegend": true,
+          "sortBy": "Mean",
+          "sortDesc": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "sum by(ntfy_code) (rate(ntfy_http_requests_total{http_code!=\"200\", job=\"$job\"}[$rate]))",
+          "legendFormat": "{{http_method}} {{http_code}} {{ntfy_code}}",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "HTTP errors (per second, ntfy code)",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "decbytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 0,
+        "y": 19
+      },
+      "id": 20,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_attachments_total_size{job=\"$job\"}",
+          "legendFormat": "Total size in MB",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Attachments: Total cache size",
+      "transformations": [],
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "color": {
+            "mode": "palette-classic"
+          },
+          "custom": {
+            "axisCenteredZero": false,
+            "axisColorMode": "text",
+            "axisLabel": "",
+            "axisPlacement": "auto",
+            "barAlignment": -1,
+            "drawStyle": "line",
+            "fillOpacity": 0,
+            "gradientMode": "none",
+            "hideFrom": {
+              "legend": false,
+              "tooltip": false,
+              "viz": false
+            },
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Failure"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 6,
+        "y": 19
+      },
+      "id": 27,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_firebase_published_success{job=\"$job\"}[$rate])",
+          "legendFormat": "Success",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_firebase_published_failure{job=\"$job\"}[$rate])",
+          "hide": false,
+          "legendFormat": "Failure",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Firebase messages sent",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Rejected (HTTP 507)"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 12,
+        "y": 19
+      },
+      "id": 26,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_unifiedpush_published_success{job=\"$job\"}[$rate])",
+          "legendFormat": "Success",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_http_requests_total{job=\"$job\",http_code=\"507\"}[$rate])",
+          "hide": false,
+          "legendFormat": "Rejected (HTTP 507)",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "UnifiedPush messages",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Failure"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 18,
+        "y": 19
+      },
+      "id": 24,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_matrix_published_success{job=\"$job\"}[$rate])",
+          "legendFormat": "Success",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "code",
+          "expr": "rate(ntfy_matrix_published_failure{job=\"$job\"}[$rate])",
+          "hide": false,
+          "legendFormat": "Failure",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Matrix messages published",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green"
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Failure"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 0,
+        "y": 26
+      },
+      "id": 12,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_emails_sent_success{job=\"$job\"}",
+          "legendFormat": "Success",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_emails_sent_failure{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Failure",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Emails sent",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green"
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": [
+          {
+            "matcher": {
+              "id": "byName",
+              "options": "Failure"
+            },
+            "properties": [
+              {
+                "id": "color",
+                "value": {
+                  "fixedColor": "red",
+                  "mode": "fixed"
+                }
+              }
+            ]
+          }
+        ]
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 6,
+        "y": 26
+      },
+      "id": 22,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_emails_received_success{job=\"$job\"}",
+          "legendFormat": "Success",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_emails_received_failure{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Failure",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Emails received",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green"
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "ms"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 12,
+        "y": 26
+      },
+      "id": 29,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "ntfy_message_publish_duration_ms{job=\"$job\"}",
+          "legendFormat": "Duration",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Message publish duration",
+      "type": "timeseries"
+    },
+    {
+      "collapsed": false,
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 33
+      },
+      "id": 8,
+      "panels": [],
+      "title": "Internals",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green"
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 0,
+        "y": 34
+      },
+      "id": 6,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": false
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "go_goroutines{job=\"$job\"}",
+          "legendFormat": "Go routines",
+          "range": true,
+          "refId": "A"
+        }
+      ],
+      "title": "Go routines",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "log": 10,
+              "type": "symlog"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green"
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 6,
+        "y": 34
+      },
+      "id": 44,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "process_open_fds{job=\"$job\"}",
+          "legendFormat": "Open",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "process_max_fds{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Max",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "File descriptors",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "PBFA97CFB590B2093"
+      },
+      "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",
+            "lineWidth": 1,
+            "pointSize": 5,
+            "scaleDistribution": {
+              "type": "linear"
+            },
+            "showPoints": "auto",
+            "spanNulls": false,
+            "stacking": {
+              "group": "A",
+              "mode": "none"
+            },
+            "thresholdsStyle": {
+              "mode": "off"
+            }
+          },
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green"
+              },
+              {
+                "color": "red",
+                "value": 80
+              }
+            ]
+          },
+          "unit": "decbytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 7,
+        "w": 6,
+        "x": 12,
+        "y": 34
+      },
+      "id": 45,
+      "options": {
+        "legend": {
+          "calcs": [],
+          "displayMode": "list",
+          "placement": "bottom",
+          "showLegend": true
+        },
+        "tooltip": {
+          "mode": "single",
+          "sort": "none"
+        }
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "process_resident_memory_bytes{job=\"$job\"}",
+          "legendFormat": "Resident memory used by ntfy (RSS)",
+          "range": true,
+          "refId": "A"
+        },
+        {
+          "datasource": {
+            "type": "prometheus",
+            "uid": "PBFA97CFB590B2093"
+          },
+          "editorMode": "builder",
+          "expr": "process_virtual_memory_bytes{job=\"$job\"}",
+          "hide": false,
+          "legendFormat": "Virtual memory used by ntfy (VSS)",
+          "range": true,
+          "refId": "B"
+        }
+      ],
+      "title": "Resident/virtual memory",
+      "type": "timeseries"
+    }
+  ],
+  "refresh": "10s",
+  "revision": 1,
+  "schemaVersion": 38,
+  "style": "dark",
+  "tags": [],
+  "templating": {
+    "list": [
+      {
+        "current": {
+          "isNone": true,
+          "selected": false,
+          "text": "None",
+          "value": ""
+        },
+        "datasource": {
+          "type": "prometheus",
+          "uid": "PBFA97CFB590B2093"
+        },
+        "definition": "label_values(ntfy_visitors_total, job)",
+        "hide": 0,
+        "includeAll": false,
+        "label": "Job",
+        "multi": false,
+        "name": "job",
+        "options": [],
+        "query": {
+          "query": "label_values(ntfy_visitors_total, job)",
+          "refId": "StandardVariableQuery"
+        },
+        "refresh": 1,
+        "regex": "",
+        "skipUrlSync": false,
+        "sort": 0,
+        "type": "query"
+      },
+      {
+        "auto": false,
+        "auto_count": 30,
+        "auto_min": "10s",
+        "current": {
+          "selected": false,
+          "text": "30m",
+          "value": "30m"
+        },
+        "description": "Average per-second rates over values from this time span",
+        "hide": 0,
+        "label": "Rate",
+        "name": "rate",
+        "options": [
+          {
+            "selected": false,
+            "text": "1m",
+            "value": "1m"
+          },
+          {
+            "selected": false,
+            "text": "5m",
+            "value": "5m"
+          },
+          {
+            "selected": false,
+            "text": "10m",
+            "value": "10m"
+          },
+          {
+            "selected": true,
+            "text": "30m",
+            "value": "30m"
+          },
+          {
+            "selected": false,
+            "text": "1h",
+            "value": "1h"
+          }
+        ],
+        "query": "1m,5m,10m,30m,1h",
+        "queryValue": "",
+        "refresh": 2,
+        "skipUrlSync": false,
+        "type": "interval"
+      }
+    ]
+  },
+  "time": {
+    "from": "now-5m",
+    "to": "now"
+  },
+  "timepicker": {},
+  "timezone": "",
+  "title": "ntfy",
+  "uid": "TO6HgexVz",
+  "version": 2,
+  "weekStart": ""
+}
\ No newline at end of file
diff --git a/modules/nixos/monitoring/default.nix b/modules/nixos/monitoring/default.nix
index 94fff67..6f74e9f 100644
--- a/modules/nixos/monitoring/default.nix
+++ b/modules/nixos/monitoring/default.nix
@@ -60,6 +60,12 @@ in {
               jsonData.client = "standalone";
             })
           ];
+          datasources.settings.deleteDatasources = [
+            {
+              name = "PostgreSQL";
+              orgId = 1;
+            }
+          ];
 
           # https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards
           dashboards.settings.providers = [
@@ -68,6 +74,10 @@ in {
               options.path = ./dashboards/node.json;
             }
             {
+              name = "ntfy";
+              options.path = ./dashboards/ntfy.json;
+            }
+            {
               name = "endlessh";
               options.path = ./dashboards/endlessh.json;
             }
@@ -120,6 +130,43 @@ in {
           with my.configurations;
           with config.services.prometheus.exporters; [
             {
+              job_name = "promtail";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets [
+                      manwe
+                      varda
+                      yavanna
+                    ]
+                    config.nixfiles.modules.promtail.port;
+                }
+              ];
+            }
+            {
+              job_name = "ntfy";
+              static_configs = [
+                {
+                  targets =
+                    mkTargets
+                    [
+                      manwe
+                    ]
+                    config.nixfiles.modules.ntfy.port;
+                }
+              ];
+            }
+            {
+              job_name = "soju";
+              static_configs = [
+                {
+                  targets = [
+                    "127.0.0.1:${toString config.nixfiles.modules.soju.prometheus.port}"
+                  ];
+                }
+              ];
+            }
+            {
               job_name = "endlessh-go";
               static_configs = [
                 {
diff --git a/modules/nixos/ntfy.nix b/modules/nixos/ntfy.nix
index 80b6247..f8510d5 100644
--- a/modules/nixos/ntfy.nix
+++ b/modules/nixos/ntfy.nix
@@ -1,6 +1,7 @@
 {
   config,
   lib,
+  this,
   ...
 }:
 with lib; let
@@ -20,6 +21,22 @@ in {
       type = with types; str;
       default = "ntfy.${config.networking.domain}";
     };
+
+    prometheus = {
+      enable = mkEnableOption "Prometheus exporter." // {default = true;};
+
+      address = mkOption {
+        description = "Address.";
+        type = with types; str;
+        default = this.wireguard.ipv4.address;
+      };
+
+      port = mkOption {
+        description = "Port.";
+        type = with types; port;
+        default = 9289;
+      };
+    };
   };
 
   config = mkIf cfg.enable {
@@ -27,9 +44,14 @@ in {
       enable = true;
       upstreams.ntfy.servers.${config.services.ntfy-sh.settings.listen-http} = {};
       virtualHosts.${cfg.domain} = {
-        locations."/" = {
-          proxyPass = "http://ntfy";
-          proxyWebsockets = true;
+        locations = {
+          "/" = {
+            proxyPass = "http://ntfy";
+            proxyWebsockets = true;
+          };
+          "/metrics".extraConfig = ''
+            deny all;
+          '';
         };
         extraConfig = nginxInternalOnly;
       };
@@ -44,6 +66,9 @@ in {
         behind-proxy = true;
         attachment-cache-dir = "/var/cache/ntfy/attachments";
         auth-file = "/var/lib/ntfy/user.db";
+        enable-metrics = cfg.prometheus.enable;
+        metrics-listen-http = with cfg.prometheus;
+          optionalString cfg.prometheus.enable "${address}:${toString port}";
       };
     };
 
diff --git a/modules/nixos/postgresql.nix b/modules/nixos/postgresql.nix
index 79515e8..c7085ce 100644
--- a/modules/nixos/postgresql.nix
+++ b/modules/nixos/postgresql.nix
@@ -70,10 +70,7 @@ in {
       };
     };
 
-    systemd.services.postgresql.postStart =
-      optionalString (cfg.extraPostStart != [])
-      concatStringsSep "\n"
-      cfg.extraPostStart;
+    systemd.services.postgresql.postStart = optionalString (cfg.extraPostStart != []) concatLines cfg.extraPostStart;
 
     environment.sessionVariables.PSQLRC = toString (pkgs.writeText "psqlrc" ''
       \set QUIET 1
diff --git a/modules/nixos/promtail.nix b/modules/nixos/promtail.nix
index a3a6fe9..d52384a 100644
--- a/modules/nixos/promtail.nix
+++ b/modules/nixos/promtail.nix
@@ -10,12 +10,16 @@ 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}";
-      };
+    port = mkOption {
+      description = "Port.";
+      type = with types; port;
+      default = 30181;
+    };
+
+    loki.url = mkOption {
+      description = "Address of a listening Loki service.";
+      type = with types; str;
+      default = "https://${config.nixfiles.modules.loki.domain}";
     };
   };
 
@@ -26,7 +30,7 @@ in {
       configuration = {
         server = rec {
           http_listen_address = this.wireguard.ipv4.address;
-          http_listen_port = 30181;
+          http_listen_port = cfg.port;
 
           grpc_listen_address = this.wireguard.ipv4.address;
           grpc_listen_port = http_listen_port + 1;
@@ -43,7 +47,11 @@ in {
           }
         ];
 
-        positions.filename = "/tmp/positions.yaml";
+        positions = {
+          filename = "/tmp/positions.yaml";
+          sync_period = "15s";
+          ignore_invalid_yaml = true;
+        };
 
         scrape_configs = [
           {
diff --git a/modules/nixos/soju.nix b/modules/nixos/soju.nix
index 14faf00..3cfe015 100644
--- a/modules/nixos/soju.nix
+++ b/modules/nixos/soju.nix
@@ -34,6 +34,16 @@ in {
       type = with types; str;
       default = config.networking.fqdn;
     };
+
+    prometheus = {
+      enable = mkEnableOption "Prometheus exporter." // {default = true;};
+
+      port = mkOption {
+        description = "Port.";
+        type = with types; port;
+        default = 9259;
+      };
+    };
   };
 
   config = let
@@ -68,6 +78,11 @@ in {
             # https://soju.im/doc/soju.1.html
             configFile = pkgs.writeText "soju.conf" ''
               listen ${cfg.protocol}://${cfg.address}:${toString cfg.port}
+              ${
+                with cfg.prometheus;
+                  optionalString enable
+                  "listen http+prometheus://localhost:${toString port}"
+              }
               db postgres ${
                 concatStringsSep " " [
                   "host=/run/postgresql"
diff --git a/nixosConfigurations/eonwe/default.nix b/nixosConfigurations/eonwe/default.nix
index 4c29013..ac00d17 100644
--- a/nixosConfigurations/eonwe/default.nix
+++ b/nixosConfigurations/eonwe/default.nix
@@ -28,7 +28,11 @@ with lib; {
   };
 
   hm = {
-    home.packages = with pkgs; [obs-studio];
+    home.packages = with pkgs; [
+      kdenlive
+      obs-studio
+      radeontop
+    ];
 
     programs = {
       beets.settings.directory = mkForce "/mnt/udata/music";
@@ -69,7 +73,7 @@ with lib; {
 
   boot = {
     # TODO Override Xanmod kernel to support ZFS. This probably will require
-    # some patching and whatnot.
+    # some patching.
     kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
 
     kernelParams = [
@@ -80,7 +84,11 @@ with lib; {
       # mce: [Hardware Error]: PROCESSOR 2:a60f12 TIME 1669988017 SOCKET 0 APIC 2 microcode a601201
       # ```
       "mce=nobootlog"
-      # Required for Hogwats Legacy.
+      # This disables[1] User Mode Instruction Protection (UMIP)[2]. Required
+      # for some games to run via Wine.
+      #
+      # [1]: https://docs.kernel.org/x86/cpuinfo.html
+      # [2]: https://en.wikichip.org/wiki/x86/umip
       "clearcpuid=514"
     ];
 
@@ -117,6 +125,18 @@ with lib; {
   # -o mountpoint=legacy
   # nixos/root/nix
   # ```
+  #
+  # physical structure (backside):
+  # [ 1 ] [ 2 ] [ 3 ]
+  # 1: disk/by-id/ata-WDC_WD20SPZX-22UA7T0_WD-WXP2E3163YW6
+  # 2: disk/by-id/ata-WDC_WD20SPZX-22UA7T0_WD-WXN2E312R5HP
+  # 3: disk/by-id/ata-WDC_WD20SPZX-22UA7T0_WD-WXP2E311E6P2
+  #
+  # physical structure (mobo M.2):
+  # [ 1 ]
+  # [ 2 ]
+  # 1: disk/by-id/nvme-Samsung_SSD_980_PRO_2TB_S69ENF0R872526A
+  # 2: disk/by-id/nvme-Samsung_SSD_980_PRO_2TB_S69ENX0T807723X
   fileSystems = {
     "/" = {
       device = "none";
@@ -153,8 +173,8 @@ with lib; {
     };
   };
 
-  # No swap space is declared here because the system already has 128Gb of RAM. :^)
+  # No swap space is declared here because the system already has 128Gb of RAM.
   # I've only manage to hit around 100Gb mark when running large
   # computations/compiling something big. Pretty sure that a hefty chunk of it
-  # was just residual cache and it wouldn't make me get to OOM situations.
+  # was just cache and it wouldn't make me get to OOM situations.
 }

Consider giving Nix/NixOS a try! <3