diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 40e5d59..df9d62b 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -9,6 +9,3 @@ fd138d45e6a2cad89fead6e9f246ba282070d6b7
 
 # Switch to alejandra formatting
 046a88905ddfa7f9edc3291c310dbb985dee34f9
-
-# Apply wide linting
-63b3cbe00be80ccb4b221aad64eb657ae5c96d70
diff --git a/checks/default.nix b/checks/default.nix
deleted file mode 100644
index f3db4ce..0000000
--- a/checks/default.nix
+++ /dev/null
@@ -1,61 +0,0 @@
-{
-  self,
-  nixpkgs,
-  deploy-rs,
-  system,
-  ...
-}:
-let
-  pkgs = nixpkgs.legacyPackages.${system};
-
-  statix' = pkgs.statix.overrideAttrs (old: {
-    patches = old.patches ++ [
-      (pkgs.fetchpatch {
-        url = "https://github.com/oppiliappan/statix/commit/925dec39bb705acbbe77178b4d658fe1b752abbb.patch";
-        hash = "sha256-0wacO6wuYJ4ufN9PGucRVJucFdFFNF+NoHYIrLXsCWs=";
-      })
-    ];
-  });
-
-  runNuCheck =
-    {
-      name,
-      packages,
-      check,
-    }:
-    pkgs.stdenvNoCC.mkDerivation {
-      inherit name;
-
-      src = nixpkgs.lib.cleanSourceWith {
-        src = self;
-        filter = nixpkgs.lib.cleanSourceFilter;
-      };
-
-      dontPatch = true;
-      dontConfigure = true;
-      dontBuild = true;
-      dontInstall = true;
-      dontFixup = true;
-      doCheck = true;
-
-      checkInputs = nixpkgs.lib.singleton pkgs.nushell ++ packages;
-
-      checkPhase = ''
-        nu ${check}
-      '';
-    };
-in
-nixpkgs.lib.recursiveUpdate {
-  lints = runNuCheck {
-    name = "lints";
-
-    packages = [
-      pkgs.deadnix
-      pkgs.nixfmt-rfc-style
-      pkgs.shellcheck
-      statix'
-    ];
-
-    check = ./lints.nu;
-  };
-} (deploy-rs.lib.${system}.deployChecks self.deploy)
diff --git a/checks/lints.nu b/checks/lints.nu
deleted file mode 100644
index ffc2047..0000000
--- a/checks/lints.nu
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env nu
-
-let shell_files = ls **/*.sh | get name
-let nix_files = ls **/*.nix | where name !~ "hardware-configuration.nix|_sources" | get name
-
-let linters = [
-  ([shellcheck] ++ $shell_files)
-  ([nixfmt --check --strict] ++ $nix_files)
-  ([deadnix --fail] ++ $nix_files)
-  ([statix check] ++ $nix_files)
-]
-
-mkdir $env.out
-
-def run-linter [linterArgs: list<string>] {
-  print $'Running ($linterArgs.0)...'
-
-  let exit_code = try {
-    ^$linterArgs.0 ...($linterArgs | skip 1)
-    $env.LAST_EXIT_CODE
-  } catch {|e| $e.exit_code}
-
-  [$linterArgs.0, $exit_code]
-}
-
-let results = $linters | each {|linter| run-linter $linter}
-
-print 'Linter results:'
-
-let success = $results | each {|result|
-  match $result.1 {
-    0 => {print $'(ansi green)($result.0)(ansi reset)'}
-    _ => {print $'(ansi red)($result.0)(ansi reset)'}
-  }
-
-  $result.1
-} | math sum
-
-exit $success
diff --git a/configuration/default.nix b/configuration/default.nix
index 0377e9c..f874733 100644
--- a/configuration/default.nix
+++ b/configuration/default.nix
@@ -1,5 +1,7 @@
 {
   config,
+  pkgs,
+  lib,
   modulesPath,
   flake-inputs,
   ...
@@ -19,8 +21,8 @@
     ./services/crowdsec.nix
     ./services/foundryvtt.nix
     ./services/gitea.nix
-    ./services/immich.nix
     ./services/metrics
+    ./services/minecraft.nix
     ./services/nextcloud.nix
     ./services/webserver.nix
     ./services/wireguard.nix
@@ -30,7 +32,13 @@
     ./sops.nix
   ];
 
-  nixpkgs.overlays = [ (_: prev: { local = import ../pkgs { pkgs = prev; }; }) ];
+  nixpkgs.overlays = [
+    (final: prev: {
+      local = import ../pkgs {
+        pkgs = prev;
+      };
+    })
+  ];
 
   nix = {
     extraOptions = ''
@@ -63,8 +71,6 @@
         8448
         # starbound
         21025
-        # Minecraft
-        25565
 
         config.services.coturn.listening-port
         config.services.coturn.tls-listening-port
@@ -73,9 +79,6 @@
       ];
 
       allowedUDPPorts = [
-        # More minecraft
-        25565
-
         config.services.coturn.listening-port
         config.services.coturn.tls-listening-port
         config.services.coturn.alt-listening-port
diff --git a/configuration/hardware-specific/hetzner/disko.nix b/configuration/hardware-specific/hetzner/disko.nix
index 7e1acd7..cc15471 100644
--- a/configuration/hardware-specific/hetzner/disko.nix
+++ b/configuration/hardware-specific/hetzner/disko.nix
@@ -80,17 +80,6 @@
                     inherit mountOptions;
                     mountpoint = "/var";
                   };
-                  "/volume/var/lib/private/matrix-conduit" = {
-                    mountOptions = [
-                      # Explicitly don't compress here, since
-                      # conduwuit's database does compression by
-                      # itself, and relies on being able to read the
-                      # raw file data from disk (which is impossible
-                      # if btrfs compresses it)
-                      "noatime"
-                    ];
-                    mountpoint = "/var/lib/private/matrix-conduit";
-                  };
                   "/volume/nix-store" = {
                     inherit mountOptions;
                     mountpoint = "/nix";
diff --git a/configuration/hardware-specific/vm.nix b/configuration/hardware-specific/vm.nix
index 71870fb..db563fe 100644
--- a/configuration/hardware-specific/vm.nix
+++ b/configuration/hardware-specific/vm.nix
@@ -6,35 +6,26 @@
   boot.kernelParams = [ "nomodeset" ];
 
   networking.hostName = "testvm";
-
-  services = {
-    # Sets the base domain for nginx to a local domain so that we can
-    # easily test locally with the VM.
-    nginx.domain = "dev.local";
-
-    # Don't run this
-    batteryManager.enable = lib.mkForce false;
-
-    openssh.hostKeys = lib.mkForce [
-      {
-        type = "rsa";
-        bits = 4096;
-        path = "/etc/staging.key";
-      }
-    ];
-  };
+  # Sets the base domain for nginx to a local domain so that we can
+  # easily test locally with the VM.
+  services.nginx.domain = "dev.local";
 
   # Use the staging secrets
   sops.defaultSopsFile = lib.mkOverride 99 ../../keys/staging.yaml;
 
   systemd.network.networks."10-eth0" = {
     matchConfig.Name = "eth0";
-    gateway = [ "192.168.9.1" ];
+    gateway = [
+      "192.168.9.1"
+    ];
     networkConfig = {
       Address = "192.168.9.2/24";
     };
   };
 
+  # Don't run this
+  services.batteryManager.enable = lib.mkForce false;
+
   # Both so we have a predictable key for the staging env, as well as
   # to have a static key for decrypting the sops secrets for the
   # staging env.
@@ -43,6 +34,14 @@
     source = ../../keys/hosts/staging.key;
   };
 
+  services.openssh.hostKeys = lib.mkForce [
+    {
+      type = "rsa";
+      bits = 4096;
+      path = "/etc/staging.key";
+    }
+  ];
+
   virtualisation.vmVariant = {
     virtualisation = {
       memorySize = 3941;
diff --git a/configuration/nginx.nix b/configuration/nginx.nix
index 3abef7f..3ec3bd9 100644
--- a/configuration/nginx.nix
+++ b/configuration/nginx.nix
@@ -1,50 +1,41 @@
 { config, lib, ... }:
 {
-  services = {
-    nginx = {
-      enable = true;
-      recommendedTlsSettings = true;
-      recommendedOptimisation = true;
-      recommendedGzipSettings = true;
-      recommendedProxySettings = true;
-      clientMaxBodySize = "10G";
+  services.nginx = {
+    enable = true;
+    recommendedTlsSettings = true;
+    recommendedOptimisation = true;
+    recommendedGzipSettings = true;
+    recommendedProxySettings = true;
+    clientMaxBodySize = "10G";
 
-      statusPage = true; # For metrics, should be accessible only from localhost
+    statusPage = true; # For metrics, should be accessible only from localhost
 
-      commonHttpConfig = ''
-        log_format upstream_time '$remote_addr - $remote_user [$time_local] '
-                           '"$request" $status $body_bytes_sent '
-                           '"$http_referer" "$http_user_agent" '
-                           'rt=$request_time uct="$upstream_connect_time" '
-                           'uht="$upstream_header_time" urt="$upstream_response_time"';
-      '';
-    };
-
-    logrotate.settings =
-      {
-        # Override the default, just keep fewer logs
-        nginx.rotate = 6;
-      }
-      // lib.mapAttrs' (
-        virtualHost: _:
-        lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" {
-          frequency = "daily";
-          rotate = 2;
-          compress = true;
-          delaycompress = true;
-          su = "${config.services.nginx.user} ${config.services.nginx.group}";
-          postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
-        }
-      ) config.services.nginx.virtualHosts;
-
-    backups.acme = {
-      user = "acme";
-      paths = lib.mapAttrsToList (
-        virtualHost: _: "/var/lib/acme/${virtualHost}"
-      ) config.services.nginx.virtualHosts;
-    };
+    commonHttpConfig = ''
+      log_format upstream_time '$remote_addr - $remote_user [$time_local] '
+                         '"$request" $status $body_bytes_sent '
+                         '"$http_referer" "$http_user_agent" '
+                         'rt=$request_time uct="$upstream_connect_time" '
+                         'uht="$upstream_header_time" urt="$upstream_response_time"';
+    '';
   };
 
+  services.logrotate.settings =
+    {
+      # Override the default, just keep fewer logs
+      nginx.rotate = 6;
+    }
+    // lib.mapAttrs' (
+      virtualHost: _:
+      lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" {
+        frequency = "daily";
+        rotate = 2;
+        compress = true;
+        delaycompress = true;
+        su = "${config.services.nginx.user} ${config.services.nginx.group}";
+        postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
+      }
+    ) config.services.nginx.virtualHosts;
+
   systemd.tmpfiles.rules = lib.mapAttrsToList (
     virtualHost: _:
     #
@@ -75,4 +66,11 @@
   systemd.services.nginx.serviceConfig.SupplementaryGroups = [
     config.security.acme.certs."tlater.net".group
   ];
+
+  services.backups.acme = {
+    user = "acme";
+    paths = lib.mapAttrsToList (
+      virtualHost: _: "/var/lib/acme/${virtualHost}"
+    ) config.services.nginx.virtualHosts;
+  };
 }
diff --git a/configuration/services/backups.nix b/configuration/services/backups.nix
index baa61e3..81e3554 100644
--- a/configuration/services/backups.nix
+++ b/configuration/services/backups.nix
@@ -57,7 +57,7 @@ in
       '';
       type = types.attrsOf (
         types.submodule (
-          { name, ... }:
+          { config, name, ... }:
           {
             options = {
               user = lib.mkOption {
@@ -246,7 +246,7 @@ in
         };
       }
       // lib.mapAttrs' (
-        name: _:
+        name: backup:
         lib.nameValuePair "backup-${name}" {
           wantedBy = [ "timers.target" ];
           timerConfig = {
diff --git a/configuration/services/conduit/default.nix b/configuration/services/conduit/default.nix
index 4ba5271..c7e4ab4 100644
--- a/configuration/services/conduit/default.nix
+++ b/configuration/services/conduit/default.nix
@@ -1,5 +1,4 @@
 {
-  pkgs,
   config,
   lib,
   ...
@@ -17,166 +16,159 @@ in
     ./matrix-hookshot.nix
   ];
 
-  services = {
-    matrix-conduit = {
-      enable = true;
-      package = pkgs.matrix-continuwuity;
-      settings.global = {
-        address = "127.0.0.1";
-        server_name = domain;
-        new_user_displayname_suffix = "🦆";
-        allow_check_for_updates = true;
+  services.matrix-conduit = {
+    enable = true;
+    settings.global = {
+      address = "127.0.0.1";
+      server_name = domain;
+      database_backend = "rocksdb";
 
-        # Set up delegation: https://docs.conduit.rs/delegation.html#automatic-recommended
-        # This is primarily to make sliding sync work
-        well_known = {
-          client = "https://${domain}";
-          server = "${domain}:443";
-        };
-
-        turn_uris =
-          let
-            address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
-            tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
-          in
-          [
-            "turn:${address}?transport=udp"
-            "turn:${address}?transport=tcp"
-            "turns:${tls-address}?transport=udp"
-            "turns:${tls-address}?transport=tcp"
-          ];
+      # Set up delegation: https://docs.conduit.rs/delegation.html#automatic-recommended
+      # This is primarily to make sliding sync work
+      well_known = {
+        client = "https://${domain}";
+        server = "${domain}:443";
       };
-    };
 
-    coturn = {
-      enable = true;
-      no-cli = true;
-      use-auth-secret = true;
-      static-auth-secret-file = config.sops.secrets."turn/secret".path;
-      realm = turn-realm;
-      relay-ips = [ "116.202.158.55" ];
-
-      # SSL config
-      pkey = "${config.security.acme.certs."tlater.net".directory}/key.pem";
-      cert = "${config.security.acme.certs."tlater.net".directory}/fullchain.pem";
-
-      # Based on suggestions from
-      # https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
-      # and
-      # https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
-      no-tcp-relay = true;
-      secure-stun = true;
-      extraConfig = ''
-        # Deny various local IP ranges, see
-        # https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
-        no-multicast-peers
-        denied-peer-ip=0.0.0.0-0.255.255.255
-        denied-peer-ip=10.0.0.0-10.255.255.255
-        denied-peer-ip=100.64.0.0-100.127.255.255
-        denied-peer-ip=127.0.0.0-127.255.255.255
-        denied-peer-ip=169.254.0.0-169.254.255.255
-        denied-peer-ip=172.16.0.0-172.31.255.255
-        denied-peer-ip=192.0.0.0-192.0.0.255
-        denied-peer-ip=192.0.2.0-192.0.2.255
-        denied-peer-ip=192.88.99.0-192.88.99.255
-        denied-peer-ip=192.168.0.0-192.168.255.255
-        denied-peer-ip=198.18.0.0-198.19.255.255
-        denied-peer-ip=198.51.100.0-198.51.100.255
-        denied-peer-ip=203.0.113.0-203.0.113.255
-        denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1
-        denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
-        denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
-        denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
-        denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
-        denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-        denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-        denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-
-        # *Allow* any IP addresses that we explicitly set as relay IPs
-        ${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
-
-        # Various other security settings
-        no-tlsv1
-        no-tlsv1_1
-
-        # Monitoring
-        prometheus
-      '';
-    };
-
-    nginx.virtualHosts."${domain}" = {
-      useACMEHost = "tlater.net";
-
-      listen = [
-        {
-          addr = "0.0.0.0";
-          port = 80;
-        }
-        {
-          addr = "[::0]";
-          port = 80;
-        }
-        {
-          addr = "0.0.0.0";
-          port = 443;
-          ssl = true;
-        }
-        {
-          addr = "[::0]";
-          port = 443;
-          ssl = true;
-        }
-        {
-          addr = "0.0.0.0";
-          port = 8448;
-          ssl = true;
-        }
-        {
-          addr = "[::0]";
-          port = 8448;
-          ssl = true;
-        }
-      ];
-
-      forceSSL = true;
-      enableHSTS = true;
-      extraConfig = ''
-        merge_slashes off;
-      '';
-
-      locations = {
-        "/_matrix" = {
-          proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
-          # Recommended by conduit
-          extraConfig = ''
-            proxy_buffering off;
-          '';
-        };
-        "/.well-known/matrix" = {
-          proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
-        };
-      };
-    };
-
-    backups.conduit = {
-      user = "root";
-      paths = [ "/var/lib/private/matrix-conduit/" ];
-      # Other services store their data in conduit, so no other services
-      # need to be shut down currently.
-      pauseServices = [ "conduit.service" ];
+      turn_uris =
+        let
+          address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
+          tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
+        in
+        [
+          "turn:${address}?transport=udp"
+          "turn:${address}?transport=tcp"
+          "turns:${tls-address}?transport=udp"
+          "turns:${tls-address}?transport=tcp"
+        ];
     };
   };
 
-  systemd.services.conduit.serviceConfig = {
-    ExecStart = lib.mkForce "${config.services.matrix-conduit.package}/bin/conduwuit";
-    # Pass in the TURN secret via EnvironmentFile, not supported by
-    # upstream module currently.
-    #
-    # See also https://gitlab.com/famedly/conduit/-/issues/314
-    EnvironmentFile = config.sops.secrets."turn/env".path;
-  };
+  # Pass in the TURN secret via EnvironmentFile, not supported by
+  # upstream module currently.
+  #
+  # See also https://gitlab.com/famedly/conduit/-/issues/314
+  systemd.services.conduit.serviceConfig.EnvironmentFile = config.sops.secrets."turn/env".path;
 
   systemd.services.coturn.serviceConfig.SupplementaryGroups = [
     config.security.acme.certs."tlater.net".group
   ];
+
+  services.coturn = {
+    enable = true;
+    no-cli = true;
+    use-auth-secret = true;
+    static-auth-secret-file = config.sops.secrets."turn/secret".path;
+    realm = turn-realm;
+    relay-ips = [ "116.202.158.55" ];
+
+    # SSL config
+    pkey = "${config.security.acme.certs."tlater.net".directory}/key.pem";
+    cert = "${config.security.acme.certs."tlater.net".directory}/fullchain.pem";
+
+    # Based on suggestions from
+    # https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
+    # and
+    # https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
+    no-tcp-relay = true;
+    secure-stun = true;
+    extraConfig = ''
+      # Deny various local IP ranges, see
+      # https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
+      no-multicast-peers
+      denied-peer-ip=0.0.0.0-0.255.255.255
+      denied-peer-ip=10.0.0.0-10.255.255.255
+      denied-peer-ip=100.64.0.0-100.127.255.255
+      denied-peer-ip=127.0.0.0-127.255.255.255
+      denied-peer-ip=169.254.0.0-169.254.255.255
+      denied-peer-ip=172.16.0.0-172.31.255.255
+      denied-peer-ip=192.0.0.0-192.0.0.255
+      denied-peer-ip=192.0.2.0-192.0.2.255
+      denied-peer-ip=192.88.99.0-192.88.99.255
+      denied-peer-ip=192.168.0.0-192.168.255.255
+      denied-peer-ip=198.18.0.0-198.19.255.255
+      denied-peer-ip=198.51.100.0-198.51.100.255
+      denied-peer-ip=203.0.113.0-203.0.113.255
+      denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1
+      denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
+      denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
+      denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
+      denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
+      denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+      denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+      denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+
+      # *Allow* any IP addresses that we explicitly set as relay IPs
+      ${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
+
+      # Various other security settings
+      no-tlsv1
+      no-tlsv1_1
+
+      # Monitoring
+      prometheus
+    '';
+  };
+
+  services.nginx.virtualHosts."${domain}" = {
+    useACMEHost = "tlater.net";
+
+    listen = [
+      {
+        addr = "0.0.0.0";
+        port = 80;
+      }
+      {
+        addr = "[::0]";
+        port = 80;
+      }
+      {
+        addr = "0.0.0.0";
+        port = 443;
+        ssl = true;
+      }
+      {
+        addr = "[::0]";
+        port = 443;
+        ssl = true;
+      }
+      {
+        addr = "0.0.0.0";
+        port = 8448;
+        ssl = true;
+      }
+      {
+        addr = "[::0]";
+        port = 8448;
+        ssl = true;
+      }
+    ];
+
+    forceSSL = true;
+    enableHSTS = true;
+    extraConfig = ''
+      merge_slashes off;
+    '';
+
+    locations = {
+      "/_matrix" = {
+        proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
+        # Recommended by conduit
+        extraConfig = ''
+          proxy_buffering off;
+        '';
+      };
+      "/.well-known/matrix" = {
+        proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
+      };
+    };
+  };
+
+  services.backups.conduit = {
+    user = "root";
+    paths = [ "/var/lib/private/matrix-conduit/" ];
+    # Other services store their data in conduit, so no other services
+    # need to be shut down currently.
+    pauseServices = [ "conduit.service" ];
+  };
 }
diff --git a/configuration/services/conduit/matrix-hookshot.nix b/configuration/services/conduit/matrix-hookshot.nix
index 6b788b2..6f11728 100644
--- a/configuration/services/conduit/matrix-hookshot.nix
+++ b/configuration/services/conduit/matrix-hookshot.nix
@@ -29,29 +29,16 @@ let
     };
 
     # Encryption support
-    # TODO(tlater): Enable when
-    # https://github.com/matrix-org/matrix-hookshot/issues/1060 is
-    # fixed
-    # extraSettings = {
-    #   "de.sorunome.msc2409.push_ephemeral" = true;
-    #   push_ephemeral = true;
-    #   "org.matrix.msc3202" = true;
-    # };
+    extraSettings = {
+      "de.sorunome.msc2409.push_ephemeral" = true;
+      push_ephemeral = true;
+      "org.matrix.msc3202" = true;
+    };
 
     runtimeRegistration = "${cfg.registrationFile}";
   };
 in
 {
-  # users = {
-  #   users.matrix-hookshot = {
-  #     home = "/run/matrix-hookshot";
-  #     group = "matrix-hookshot";
-  #     isSystemUser = true;
-  #   };
-
-  #   groups.matrix-hookshot = { };
-  # };
-
   systemd.services.matrix-hookshot = {
     serviceConfig = {
       Type = lib.mkForce "exec";
@@ -62,7 +49,6 @@ in
       # Some library in matrix-hookshot wants a home directory
       Environment = [ "HOME=/run/matrix-hookshot" ];
 
-      # User = "matrix-hookshot";
       DynamicUser = true;
       StateDirectory = "matrix-hookshot";
       RuntimeDirectory = "matrix-hookshot";
@@ -76,11 +62,7 @@ in
       ProtectKernelModules = true;
       ProtectKernelLogs = true;
       ProtectControlGroups = true;
-      RestrictAddressFamilies = [
-        # "AF_UNIX"
-        "AF_INET"
-        "AF_INET6"
-      ];
+      RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
       LockPersonality = true;
       RestrictRealtime = true;
       ProtectProc = "invisible";
@@ -89,15 +71,12 @@ in
     };
   };
 
-  # services.redis.servers.matrix-hookshot = {
-  #   enable = true;
-  #   user = "matrix-hookshot";
-  # };
-
   services.matrix-hookshot = {
     enable = true;
 
-    serviceDependencies = [ "conduit.service" ];
+    serviceDependencies = [
+      "conduit.service"
+    ];
 
     registrationFile = "/run/matrix-hookshot/registration.yaml";
 
@@ -112,8 +91,6 @@ in
 
       bot.displayname = "Hookshot";
 
-      # cache.redisUri = "redis://${config.services.redis.servers.matrix-hookshot.unixSocket}";
-
       generic = {
         enabled = true;
         outbound = false;
@@ -123,10 +100,7 @@ in
         allowJsTransformationFunctions = true;
       };
 
-      # TODO(tlater): Enable when
-      # https://github.com/matrix-org/matrix-hookshot/issues/1060 is
-      # fixed
-      # encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore";
+      encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore";
 
       permissions = [
         {
@@ -152,15 +126,19 @@ in
       listeners = [
         {
           port = 9000;
-          resources = [ "webhooks" ];
+          resources = [
+            "webhooks"
+          ];
         }
         {
           port = 9001;
-          resources = [ "metrics" ];
+          resources = [
+            "metrics"
+          ];
         }
       ];
 
-      metrics.enabled = true;
+      metrics.enable = true;
     };
   };
 }
diff --git a/configuration/services/crowdsec.nix b/configuration/services/crowdsec.nix
index b736047..6860354 100644
--- a/configuration/services/crowdsec.nix
+++ b/configuration/services/crowdsec.nix
@@ -8,7 +8,21 @@
   security.crowdsec = {
     enable = true;
 
-    parserWhitelist = [ "10.45.249.2" ];
+    parserWhitelist = [
+      "10.45.249.2"
+    ];
+
+    extraConfig."postoverflows/s01-whitelist/matrix-whitelist.yaml" = {
+      name = "tetsumaki/matrix";
+      description = "custom matrix whitelist";
+      whitelist = {
+        reason = "whitelist false positive for matrix";
+        expression = [
+          "evt.Overflow.Alert.Events[0].GetMeta('target_fqdn') == '${config.services.matrix-conduit.settings.global.server_name}'"
+          "evt.Overflow.Alert.GetScenario() in ['crowdsecurity/http-probing', 'crowdsecurity/http-crawl-non_statics']"
+        ];
+      };
+    };
 
     extraGroups = [
       "systemd-journal"
@@ -19,19 +33,25 @@
       {
         source = "journalctl";
         labels.type = "syslog";
-        journalctl_filter = [ "SYSLOG_IDENTIFIER=Nextcloud" ];
+        journalctl_filter = [
+          "SYSLOG_IDENTIFIER=Nextcloud"
+        ];
       }
 
       {
         source = "journalctl";
         labels.type = "syslog";
-        journalctl_filter = [ "SYSLOG_IDENTIFIER=sshd-session" ];
+        journalctl_filter = [
+          "SYSLOG_IDENTIFIER=sshd-session"
+        ];
       }
 
       {
         labels.type = "nginx";
         filenames =
-          [ "/var/log/nginx/*.log" ]
+          [
+            "/var/log/nginx/*.log"
+          ]
           ++ lib.mapAttrsToList (
             vHost: _: "/var/log/nginx/${vHost}/access.log"
           ) config.services.nginx.virtualHosts;
@@ -47,36 +67,4 @@
       };
     };
   };
-
-  # Add whitelists for matrix
-  systemd.tmpfiles.settings."10-matrix" =
-    let
-      stateDir = config.security.crowdsec.stateDirectory;
-    in
-    {
-      "${stateDir}/config/postoverflows".d = {
-        user = "crowdsec";
-        group = "crowdsec";
-        mode = "0700";
-      };
-
-      "${stateDir}/config/postoverflows/s01-whitelist".d = {
-        user = "crowdsec";
-        group = "crowdsec";
-        mode = "0700";
-      };
-
-      "${stateDir}/config/postoverflows/s01-whitelist/matrix-whitelist.yaml"."L+".argument =
-        ((pkgs.formats.yaml { }).generate "crowdsec-matrix-whitelist.yaml" {
-          name = "tetsumaki/matrix";
-          description = "custom matrix whitelist";
-          whitelist = {
-            reason = "whitelist false positive for matrix";
-            expression = [
-              "evt.Overflow.Alert.Events[0].GetMeta('target_fqdn') == '${config.services.matrix-conduit.settings.global.server_name}'"
-              "evt.Overflow.Alert.GetScenario() in ['crowdsecurity/http-probing', 'crowdsecurity/http-crawl-non_statics']"
-            ];
-          };
-        }).outPath;
-    };
 }
diff --git a/configuration/services/foundryvtt.nix b/configuration/services/foundryvtt.nix
index 6c475a3..614b818 100644
--- a/configuration/services/foundryvtt.nix
+++ b/configuration/services/foundryvtt.nix
@@ -11,39 +11,37 @@ in
 {
   imports = [ flake-inputs.foundryvtt.nixosModules.foundryvtt ];
 
-  services = {
-    foundryvtt = {
-      enable = true;
-      hostName = domain;
-      minifyStaticFiles = true;
-      proxySSL = true;
-      proxyPort = 443;
-      package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_13;
-    };
-
-    nginx.virtualHosts."${domain}" =
-      let
-        inherit (config.services.foundryvtt) port;
-      in
-      {
-        forceSSL = true;
-        useACMEHost = "tlater.net";
-        enableHSTS = true;
-
-        locations."/" = {
-          proxyWebsockets = true;
-          proxyPass = "http://localhost:${toString port}";
-        };
-      };
-
-    backups.foundryvtt = {
-      user = "foundryvtt";
-      paths = [ config.services.foundryvtt.dataDir ];
-      pauseServices = [ "foundryvtt.service" ];
-    };
+  services.foundryvtt = {
+    enable = true;
+    hostName = domain;
+    minifyStaticFiles = true;
+    proxySSL = true;
+    proxyPort = 443;
+    package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_11;
   };
 
   # Want to start it manually when I need it, not have it constantly
   # running
   systemd.services.foundryvtt.wantedBy = lib.mkForce [ ];
+
+  services.nginx.virtualHosts."${domain}" =
+    let
+      inherit (config.services.foundryvtt) port;
+    in
+    {
+      forceSSL = true;
+      useACMEHost = "tlater.net";
+      enableHSTS = true;
+
+      locations."/" = {
+        proxyWebsockets = true;
+        proxyPass = "http://localhost:${toString port}";
+      };
+    };
+
+  services.backups.foundryvtt = {
+    user = "foundryvtt";
+    paths = [ config.services.foundryvtt.dataDir ];
+    pauseServices = [ "foundryvtt.service" ];
+  };
 }
diff --git a/configuration/services/gitea.nix b/configuration/services/gitea.nix
index 613d30c..da01cde 100644
--- a/configuration/services/gitea.nix
+++ b/configuration/services/gitea.nix
@@ -8,68 +8,24 @@ let
   domain = "gitea.${config.services.nginx.domain}";
 in
 {
-  services = {
-    forgejo = {
-      enable = true;
-      database.type = "postgres";
+  services.forgejo = {
+    enable = true;
+    database.type = "postgres";
 
-      settings = {
-        server = {
-          DOMAIN = domain;
-          HTTP_ADDR = "127.0.0.1";
-          ROOT_URL = "https://${domain}/";
-          SSH_PORT = 2222;
-        };
-
-        metrics = {
-          ENABLED = true;
-          TOKEN = "#metricstoken#";
-        };
-        service.DISABLE_REGISTRATION = true;
-        session.COOKIE_SECURE = true;
-      };
-    };
-
-    # Set up SSL
-    nginx.virtualHosts."${domain}" =
-      let
-        httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
-        httpPort = config.services.forgejo.settings.server.HTTP_PORT;
-      in
-      {
-        forceSSL = true;
-        useACMEHost = "tlater.net";
-        enableHSTS = true;
-
-        locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
-        locations."/metrics" = {
-          extraConfig = ''
-            access_log off;
-            allow 127.0.0.1;
-            ${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
-            deny all;
-          '';
-        };
+    settings = {
+      server = {
+        DOMAIN = domain;
+        HTTP_ADDR = "127.0.0.1";
+        ROOT_URL = "https://${domain}/";
+        SSH_PORT = 2222;
       };
 
-    backups.forgejo = {
-      user = "forgejo";
-      paths = [
-        "/var/lib/forgejo/forgejo-db.sql"
-        "/var/lib/forgejo/repositories/"
-        "/var/lib/forgejo/data/"
-        "/var/lib/forgejo/custom/"
-        # Conf is backed up via nix
-      ];
-      preparation = {
-        packages = [ config.services.postgresql.package ];
-        text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
+      metrics = {
+        ENABLED = true;
+        TOKEN = "#metricstoken#";
       };
-      cleanup = {
-        packages = [ pkgs.coreutils ];
-        text = "rm /var/lib/forgejo/forgejo-db.sql";
-      };
-      pauseServices = [ "forgejo.service" ];
+      service.DISABLE_REGISTRATION = true;
+      session.COOKIE_SECURE = true;
     };
   };
 
@@ -80,4 +36,46 @@ in
       runConfig = "${config.services.forgejo.customDir}/conf/app.ini";
     in
     [ "+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'" ];
+
+  # Set up SSL
+  services.nginx.virtualHosts."${domain}" =
+    let
+      httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
+      httpPort = config.services.forgejo.settings.server.HTTP_PORT;
+    in
+    {
+      forceSSL = true;
+      useACMEHost = "tlater.net";
+      enableHSTS = true;
+
+      locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
+      locations."/metrics" = {
+        extraConfig = ''
+          access_log off;
+          allow 127.0.0.1;
+          ${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
+          deny all;
+        '';
+      };
+    };
+
+  services.backups.forgejo = {
+    user = "forgejo";
+    paths = [
+      "/var/lib/forgejo/forgejo-db.sql"
+      "/var/lib/forgejo/repositories/"
+      "/var/lib/forgejo/data/"
+      "/var/lib/forgejo/custom/"
+      # Conf is backed up via nix
+    ];
+    preparation = {
+      packages = [ config.services.postgresql.package ];
+      text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
+    };
+    cleanup = {
+      packages = [ pkgs.coreutils ];
+      text = "rm /var/lib/forgejo/forgejo-db.sql";
+    };
+    pauseServices = [ "forgejo.service" ];
+  };
 }
diff --git a/configuration/services/immich.nix b/configuration/services/immich.nix
deleted file mode 100644
index 516ea3e..0000000
--- a/configuration/services/immich.nix
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  pkgs,
-  config,
-  lib,
-  ...
-}:
-let
-  hostName = "immich.${config.services.nginx.domain}";
-in
-{
-  services = {
-    immich = {
-      enable = true;
-      settings.server.externalDomain = "https://${hostName}";
-
-      environment.IMMICH_TELEMETRY_INCLUDE = "all";
-    };
-
-    nginx.virtualHosts.${hostName} =
-      let
-        local = "http://${config.services.immich.host}:${toString config.services.immich.port}";
-      in
-      {
-        forceSSL = true;
-        useACMEHost = "tlater.net";
-        enableHSTS = true;
-
-        locations."/" = {
-          proxyPass = local;
-          proxyWebsockets = true;
-        };
-        locations."/metrics" = {
-          extraConfig = ''
-            access_log off;
-            allow 127.0.0.1;
-            ${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
-            deny all;
-          '';
-        };
-      };
-
-    backups.immich =
-      let
-        db-dump = "${config.services.immich.mediaLocation}/immich-db.sql";
-      in
-      {
-        user = "immich";
-        paths = [ config.services.immich.mediaLocation ];
-
-        preparation = {
-          packages = [ config.services.postgresql.package ];
-          text = ''
-            pg_dump ${config.services.immich.database.name} --clean --if-exists --file=${db-dump}
-          '';
-        };
-
-        cleanup = {
-          packages = [ pkgs.coreutils ];
-          text = "rm ${db-dump}";
-        };
-        pauseServices = [
-          "immich-server.service"
-          "immich-machine-learning.service"
-        ];
-      };
-  };
-}
diff --git a/configuration/services/metrics/exporters.nix b/configuration/services/metrics/exporters.nix
index 52c2a46..78ba684 100644
--- a/configuration/services/metrics/exporters.nix
+++ b/configuration/services/metrics/exporters.nix
@@ -74,7 +74,7 @@ in
         listenAddress = "127.0.0.1";
         group = "nginx";
 
-        settings.namespaces = lib.mapAttrsToList (name: _: {
+        settings.namespaces = lib.mapAttrsToList (name: virtualHost: {
           inherit name;
           metrics_override.prefix = "nginxlog";
           namespace_label = "vhost";
@@ -97,6 +97,4 @@ in
     #   - postgres (?)
     #   - ssl_exporter (?)
   };
-
-  services.dbus.implementation = "broker";
 }
diff --git a/configuration/services/metrics/grafana.nix b/configuration/services/metrics/grafana.nix
index b30806c..b872833 100644
--- a/configuration/services/metrics/grafana.nix
+++ b/configuration/services/metrics/grafana.nix
@@ -1,4 +1,9 @@
-{ pkgs, config, ... }:
+{
+  pkgs,
+  config,
+  flake-inputs,
+  ...
+}:
 let
   domain = "metrics.${config.services.nginx.domain}";
 in
@@ -30,7 +35,7 @@ in
 
     declarativePlugins = [
       pkgs.grafanaPlugins.victoriametrics-metrics-datasource
-      pkgs.grafanaPlugins.victoriametrics-logs-datasource
+      flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}.grafanaPlugins.victoriametrics-logs-datasource
     ];
 
     provision = {
diff --git a/configuration/services/metrics/options.nix b/configuration/services/metrics/options.nix
index e1b0761..d69ecfb 100644
--- a/configuration/services/metrics/options.nix
+++ b/configuration/services/metrics/options.nix
@@ -38,7 +38,7 @@ in
     services.victoriametrics.scrapeConfigs = mkOption {
       type = types.attrsOf (
         types.submodule (
-          { name, ... }:
+          { name, self, ... }:
           {
             options = {
               job_name = mkOption {
@@ -106,37 +106,35 @@ in
               # module is an intractable mess
               wantedBy = [ "multi-user.target" ];
               after = [ "network.target" ];
-              serviceConfig = {
-                Restart = mkDefault "always";
-                PrivateTmp = mkDefault true;
-                WorkingDirectory = mkDefault /tmp;
-                DynamicUser = mkDefault true;
-                # Hardening
-                CapabilityBoundingSet = mkDefault [ "" ];
-                DeviceAllow = [ "" ];
-                LockPersonality = true;
-                MemoryDenyWriteExecute = true;
-                NoNewPrivileges = true;
-                PrivateDevices = mkDefault true;
-                ProtectClock = mkDefault true;
-                ProtectControlGroups = true;
-                ProtectHome = true;
-                ProtectHostname = true;
-                ProtectKernelLogs = true;
-                ProtectKernelModules = true;
-                ProtectKernelTunables = true;
-                ProtectSystem = mkDefault "strict";
-                RemoveIPC = true;
-                RestrictAddressFamilies = [
-                  "AF_INET"
-                  "AF_INET6"
-                ];
-                RestrictNamespaces = true;
-                RestrictRealtime = true;
-                RestrictSUIDSGID = true;
-                SystemCallArchitectures = "native";
-                UMask = "0077";
-              };
+              serviceConfig.Restart = mkDefault "always";
+              serviceConfig.PrivateTmp = mkDefault true;
+              serviceConfig.WorkingDirectory = mkDefault /tmp;
+              serviceConfig.DynamicUser = mkDefault true;
+              # Hardening
+              serviceConfig.CapabilityBoundingSet = mkDefault [ "" ];
+              serviceConfig.DeviceAllow = [ "" ];
+              serviceConfig.LockPersonality = true;
+              serviceConfig.MemoryDenyWriteExecute = true;
+              serviceConfig.NoNewPrivileges = true;
+              serviceConfig.PrivateDevices = mkDefault true;
+              serviceConfig.ProtectClock = mkDefault true;
+              serviceConfig.ProtectControlGroups = true;
+              serviceConfig.ProtectHome = true;
+              serviceConfig.ProtectHostname = true;
+              serviceConfig.ProtectKernelLogs = true;
+              serviceConfig.ProtectKernelModules = true;
+              serviceConfig.ProtectKernelTunables = true;
+              serviceConfig.ProtectSystem = mkDefault "strict";
+              serviceConfig.RemoveIPC = true;
+              serviceConfig.RestrictAddressFamilies = [
+                "AF_INET"
+                "AF_INET6"
+              ];
+              serviceConfig.RestrictNamespaces = true;
+              serviceConfig.RestrictRealtime = true;
+              serviceConfig.RestrictSUIDSGID = true;
+              serviceConfig.SystemCallArchitectures = "native";
+              serviceConfig.UMask = "0077";
             }
             exporter.serviceOpts
           ]
@@ -146,7 +144,7 @@ in
       {
         vmagent-scrape-exporters =
           let
-            inherit (config.services.victoriametrics) listenAddress;
+            listenAddress = config.services.victoriametrics.listenAddress;
             vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress;
             promscrape = yaml.generate "prometheus.yml" {
               scrape_configs = lib.mapAttrsToList (
@@ -155,7 +153,7 @@ in
                   inherit (scrape) job_name;
                   static_configs =
                     scrape.static_configs
-                    ++ lib.optional (scrape.targets != [ ]) { inherit (scrape) targets; };
+                    ++ lib.optional (scrape.targets != [ ]) { targets = scrape.targets; };
                 } scrape.extraSettings
               ) config.services.victoriametrics.scrapeConfigs;
             };
@@ -214,7 +212,7 @@ in
 
     services.victoriametrics.scrapeConfigs =
       let
-        allExporters = lib.mapAttrs (_: exporter: { inherit (exporter) listenAddress port; }) (
+        allExporters = lib.mapAttrs (name: exporter: { inherit (exporter) listenAddress port; }) (
           (lib.filterAttrs (
             name: exporter:
             # A bunch of deprecated exporters that need to be ignored
diff --git a/configuration/services/metrics/victorialogs.nix b/configuration/services/metrics/victorialogs.nix
index 413659a..ed74c59 100644
--- a/configuration/services/metrics/victorialogs.nix
+++ b/configuration/services/metrics/victorialogs.nix
@@ -1,22 +1,37 @@
-{ config, lib, ... }:
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
 let
   cfg = config.services.victorialogs;
+  pkg = pkgs.victoriametrics;
+  dirname = "victorialogs";
 in
 {
-  options.services.victorialogs.bindAddress = lib.mkOption {
-    readOnly = true;
-    type = lib.types.str;
-    description = ''
-      Final address on which victorialogs listens.
-    '';
-  };
+  options.services.victorialogs =
+    let
+      inherit (lib.types) str;
+    in
+    {
+      listenAddress = lib.mkOption {
+        default = ":9428";
+        type = str;
+      };
+
+      bindAddress = lib.mkOption {
+        readOnly = true;
+        type = str;
+        description = ''
+          Final address on which victorialogs listens.
+        '';
+      };
+    };
 
   config = {
-    services.victorialogs = {
-      enable = true;
-      bindAddress =
-        (lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress;
-    };
+    services.victorialogs.bindAddress =
+      (lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress;
 
     services.journald.upload = {
       enable = true;
@@ -25,6 +40,71 @@ in
         NetworkTimeoutSec = "20s";
       };
     };
+
     systemd.services."systemd-journal-upload".after = [ "victorialogs.service" ];
+
+    systemd.services.victorialogs = {
+      description = "VictoriaLogs log database";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      startLimitBurst = 5;
+
+      serviceConfig = {
+        ExecStart = lib.escapeShellArgs [
+          "${pkg}/bin/victoria-logs"
+          "-storageDataPath=/var/lib/${dirname}"
+          "-httpListenAddr=${cfg.listenAddress}"
+        ];
+
+        DynamicUser = true;
+        RestartSec = 1;
+        Restart = "on-failure";
+        RuntimeDirectory = dirname;
+        RuntimeDirectoryMode = "0700";
+        StateDirectory = dirname;
+        StateDirectoryMode = "0700";
+
+        LimitNOFILE = 1048576;
+
+        # Hardening
+        DeviceAllow = [ "/dev/null rw" ];
+        DevicePolicy = "strict";
+        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;
+        ProtectProc = "invisible";
+        ProtectSystem = "full";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          "AF_UNIX"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
+      };
+
+      postStart = lib.mkBefore ''
+        until ${lib.getBin pkgs.curl}/bin/curl -s -o /dev/null http://${cfg.bindAddress}/ping; do
+          sleep 1;
+        done
+      '';
+    };
   };
 }
diff --git a/configuration/services/metrics/victoriametrics.nix b/configuration/services/metrics/victoriametrics.nix
index f37b8b0..4a78d46 100644
--- a/configuration/services/metrics/victoriametrics.nix
+++ b/configuration/services/metrics/victoriametrics.nix
@@ -84,16 +84,9 @@ in
         in
         [ "${address}:${toString port}" ];
 
-      immich.targets = [
-        "127.0.0.1:8081"
-        "127.0.0.1:8082"
-      ];
-
       # Configured in the hookshot listeners, but it's hard to filter
       # the correct values out of that config.
       matrixHookshot.targets = [ "127.0.0.1:9001" ];
-
-      victorialogs.targets = [ config.services.victorialogs.bindAddress ];
     };
   };
 }
diff --git a/configuration/services/minecraft.nix b/configuration/services/minecraft.nix
new file mode 100644
index 0000000..0477f44
--- /dev/null
+++ b/configuration/services/minecraft.nix
@@ -0,0 +1,83 @@
+{
+  pkgs,
+  lib,
+  config,
+  ...
+}:
+{
+  services.minecraft-server = {
+    enable = true;
+    eula = true;
+    # jvmOpts are set using a file for forge
+    # jvmOpts = "-Xmx8G -Xms8G";
+    openFirewall = true;
+
+    declarative = true;
+
+    whitelist = {
+      tlater = "140d177a-966f-41b8-a4c0-e305babd291b";
+      romino25 = "59cd1648-14a4-4bcf-8f5a-2e1bde678f2c";
+      lasi25 = "0ab6e3d1-544a-47e7-8538-2e6c248e49a4";
+    };
+
+    serverProperties = {
+      allow-flight = true;
+      difficulty = "hard";
+      motd = "tlater.net";
+      spawn-protection = 1;
+      white-list = true;
+      enable-query = true;
+      enable-status = true;
+
+      # Allows the server to write chunks without hogging the main
+      # thread...
+      sync-chunk-writes = false;
+      # Disables chat reporting, because we don't need any of that
+      # drama on a lil' friends-only server.
+      enforce-secure-profile = false;
+    };
+
+    package = pkgs.writeShellApplication {
+      name = "minecraft-server";
+      runtimeInputs = with pkgs; [ jdk17_headless ];
+
+      text = ''
+        exec /var/lib/minecraft/run.sh $@
+      '';
+    };
+  };
+
+  systemd.services.minecraft-server = {
+    path = with pkgs; [ jdk17_headless ];
+
+    # Since we read from our own HTTP server, we need to wait for it
+    # to be up
+    after = [ "nginx.service" ];
+
+    serviceConfig = {
+      # Use packwiz to install mods
+      ExecStartPre = [
+        "${pkgs.jdk17_headless}/bin/java -jar ${config.services.minecraft-server.dataDir}/packwiz-installer-bootstrap.jar -g -s server 'https://minecraft.${config.services.nginx.domain}/cobblemon-pack/pack.toml'"
+      ];
+      # Forge requires some bonus JVM options, which they include in a
+      # little `run.sh` script
+      ExecStart = lib.mkForce "${config.services.minecraft-server.dataDir}/run.sh --nogui";
+    };
+  };
+
+  systemd.tmpfiles.settings."10-minecraft" = {
+    "/srv/minecraft".d = {
+      user = "nginx";
+      group = "minecraft";
+      mode = "0775";
+    };
+  };
+
+  services.nginx.virtualHosts."minecraft.${config.services.nginx.domain}" = {
+    forceSSL = true;
+    useACMEHost = "tlater.net";
+    enableHSTS = true;
+
+    root = "/srv/minecraft";
+  };
+}
diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix
index 4af77a9..b5cb691 100644
--- a/configuration/services/nextcloud.nix
+++ b/configuration/services/nextcloud.nix
@@ -5,99 +5,97 @@
   ...
 }:
 let
-  nextcloud = pkgs.nextcloud31;
+  nextcloud = pkgs.nextcloud30;
   hostName = "nextcloud.${config.services.nginx.domain}";
 in
 {
-  services = {
-    nextcloud = {
-      inherit hostName;
+  services.nextcloud = {
+    inherit hostName;
 
-      package = nextcloud;
-      phpPackage = lib.mkForce (
-        pkgs.php.override {
-          packageOverrides = _: prev: {
-            extensions = prev.extensions // {
-              pgsql = prev.extensions.pgsql.overrideAttrs (_: {
-                configureFlags = [ "--with-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
-              });
-              pdo_pgsql = prev.extensions.pdo_pgsql.overrideAttrs (_: {
-                configureFlags = [ "--with-pdo-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
-              });
-            };
+    package = nextcloud;
+    phpPackage = lib.mkForce (
+      pkgs.php.override {
+        packageOverrides = final: prev: {
+          extensions = prev.extensions // {
+            pgsql = prev.extensions.pgsql.overrideAttrs (old: {
+              configureFlags = [ "--with-pgsql=${lib.getDev config.services.postgresql.package}" ];
+            });
+            pdo_pgsql = prev.extensions.pdo_pgsql.overrideAttrs (old: {
+              configureFlags = [ "--with-pdo-pgsql=${lib.getDev config.services.postgresql.package}" ];
+            });
           };
-        }
-      );
-      enable = true;
-      maxUploadSize = "2G";
-      https = true;
+        };
+      }
+    );
+    enable = true;
+    maxUploadSize = "2G";
+    https = true;
 
-      configureRedis = true;
+    configureRedis = true;
 
-      config = {
-        dbtype = "pgsql";
-        dbhost = "/run/postgresql";
+    config = {
+      dbtype = "pgsql";
+      dbhost = "/run/postgresql";
 
-        adminuser = "tlater";
-        adminpassFile = config.sops.secrets."nextcloud/tlater".path;
-      };
-
-      settings = {
-        default_phone_region = "AT";
-        overwriteprotocol = "https";
-      };
-
-      phpOptions = {
-        "opcache.interned_strings_buffer" = "16";
-      };
-
-      extraApps = {
-        inherit (config.services.nextcloud.package.packages.apps)
-          calendar
-          contacts
-          cookbook
-          news
-          ;
-      };
+      adminuser = "tlater";
+      adminpassFile = config.sops.secrets."nextcloud/tlater".path;
     };
 
-    # Set up SSL
-    nginx.virtualHosts."${hostName}" = {
-      forceSSL = true;
-      useACMEHost = "tlater.net";
-      # The upstream module already adds HSTS
+    settings = {
+      default_phone_region = "AT";
+      overwriteprotocol = "https";
     };
 
-    backups.nextcloud = {
-      user = "nextcloud";
-      paths = [
-        "/var/lib/nextcloud/nextcloud-db.sql"
-        "/var/lib/nextcloud/data/"
-        "/var/lib/nextcloud/config/config.php"
-      ];
-      preparation = {
-        packages = [
-          config.services.postgresql.package
-          config.services.nextcloud.occ
-        ];
-        text = ''
-          nextcloud-occ maintenance:mode --on
-          pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
-        '';
-      };
-      cleanup = {
-        packages = [
-          pkgs.coreutils
-          config.services.nextcloud.occ
-        ];
-        text = ''
-          rm /var/lib/nextcloud/nextcloud-db.sql
-          nextcloud-occ maintenance:mode --off
-        '';
-      };
+    phpOptions = {
+      "opcache.interned_strings_buffer" = "16";
+    };
+
+    extraApps = {
+      inherit (config.services.nextcloud.package.packages.apps)
+        calendar
+        contacts
+        cookbook
+        news
+        ;
     };
   };
 
   # Ensure that this service doesn't start before postgres is ready
   systemd.services.nextcloud-setup.after = [ "postgresql.service" ];
+
+  # Set up SSL
+  services.nginx.virtualHosts."${hostName}" = {
+    forceSSL = true;
+    useACMEHost = "tlater.net";
+    # The upstream module already adds HSTS
+  };
+
+  services.backups.nextcloud = {
+    user = "nextcloud";
+    paths = [
+      "/var/lib/nextcloud/nextcloud-db.sql"
+      "/var/lib/nextcloud/data/"
+      "/var/lib/nextcloud/config/config.php"
+    ];
+    preparation = {
+      packages = [
+        config.services.postgresql.package
+        config.services.nextcloud.occ
+      ];
+      text = ''
+        nextcloud-occ maintenance:mode --on
+        pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
+      '';
+    };
+    cleanup = {
+      packages = [
+        pkgs.coreutils
+        config.services.nextcloud.occ
+      ];
+      text = ''
+        rm /var/lib/nextcloud/nextcloud-db.sql
+        nextcloud-occ maintenance:mode --off
+      '';
+    };
+  };
 }
diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix
index 864f6c0..e6b49b3 100644
--- a/configuration/services/webserver.nix
+++ b/configuration/services/webserver.nix
@@ -1,6 +1,6 @@
 { config, ... }:
 let
-  inherit (config.services.nginx) domain;
+  domain = config.services.nginx.domain;
 in
 {
   services.tlaternet-webserver = {
diff --git a/flake.lock b/flake.lock
index 885b579..37c3d5f 100644
--- a/flake.lock
+++ b/flake.lock
@@ -300,11 +300,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1747742835,
-        "narHash": "sha256-kYL4GCwwznsypvsnA20oyvW8zB/Dvn6K5G/tgMjVMT4=",
+        "lastModified": 1739841949,
+        "narHash": "sha256-lSOXdgW/1zi/SSu7xp71v+55D5Egz8ACv0STkj7fhbs=",
         "owner": "nix-community",
         "repo": "disko",
-        "rev": "df522e787fdffc4f32ed3e1fca9ed0968a384d62",
+        "rev": "15dbf8cebd8e2655a883b74547108e089f051bf0",
         "type": "github"
       },
       "original": {
@@ -595,11 +595,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1746877938,
-        "narHash": "sha256-N9J96pSPg4vbozV+ZZ++dwLnMIf2Le6ONNMO0kZCj1M=",
+        "lastModified": 1739712626,
+        "narHash": "sha256-u3m+awbdL+0BKk8IWidsWMr+R0ian3GZMUlH7623kd8=",
         "owner": "reckenrode",
         "repo": "nix-foundryvtt",
-        "rev": "f1b401831d796dd94cf5a11b65fd169a199d4ff0",
+        "rev": "a7fa493ba2c623cf90e83756b62285b3b58f18d2",
         "type": "github"
       },
       "original": {
@@ -744,18 +744,34 @@
         "type": "github"
       }
     },
-    "nixpkgs_2": {
+    "nixpkgs-unstable": {
       "locked": {
-        "lastModified": 1748085680,
-        "narHash": "sha256-XG90Q/040NiV70gAVvoYbXg1lULbiwIzfkWmwSINyGQ=",
+        "lastModified": 1740215764,
+        "narHash": "sha256-wzBbGGZ6i1VVBA/cDJaLfuuGYCUriD7fwsLgJJHRVRk=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "4e6eeca5ed45465087274fc9dc6bc2011254a0f3",
+        "rev": "8465e233b0668cf162c608a92e62e8d78c1ba7e4",
         "type": "github"
       },
       "original": {
         "owner": "nixos",
-        "ref": "nixos-25.05-small",
+        "ref": "nixos-unstable-small",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1740162160,
+        "narHash": "sha256-SSYxFhqCOb3aiPb6MmN68yEzBIltfom8IgRz7phHscM=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "11415c7ae8539d6292f2928317ee7a8410b28bb9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-24.11-small",
         "repo": "nixpkgs",
         "type": "github"
       }
@@ -1006,6 +1022,7 @@
         "disko": "disko",
         "foundryvtt": "foundryvtt",
         "nixpkgs": "nixpkgs_2",
+        "nixpkgs-unstable": "nixpkgs-unstable",
         "sonnenshift": "sonnenshift",
         "sops-nix": "sops-nix",
         "tlaternet-webserver": "tlaternet-webserver"
@@ -1079,11 +1096,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1747603214,
-        "narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=",
+        "lastModified": 1739262228,
+        "narHash": "sha256-7JAGezJ0Dn5qIyA2+T4Dt/xQgAbhCglh6lzCekTVMeU=",
         "owner": "Mic92",
         "repo": "sops-nix",
-        "rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd",
+        "rev": "07af005bb7d60c7f118d9d9f5530485da5d1e975",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index da8455f..b31d108 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,7 +2,8 @@
   description = "tlater.net host configuration";
 
   inputs = {
-    nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small";
+    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11-small";
+    nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable-small";
     disko = {
       url = "github:nix-community/disko";
       inputs.nixpkgs.follows = "nixpkgs";
@@ -91,7 +92,7 @@
       #########
       # Tests #
       #########
-      checks.${system} = import ./checks (inputs // { inherit system; });
+      checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
 
       ###########################
       # Garbage collection root #
@@ -116,6 +117,8 @@
         run-vm = {
           type = "app";
           program =
+            let
+            in
             (pkgs.writeShellScript "" ''
               ${vm.config.system.build.vm.outPath}/bin/run-testvm-vm
             '').outPath;
diff --git a/modules/crowdsec/default.nix b/modules/crowdsec/default.nix
index 44e6bc5..cc14939 100644
--- a/modules/crowdsec/default.nix
+++ b/modules/crowdsec/default.nix
@@ -31,6 +31,22 @@ let
     ${lib.concatMapStringsSep "\n---\n" builtins.toJSON cfg.acquisitions}
     ---
   '';
+
+  extraConfigs = pkgs.symlinkJoin {
+    name = "crowdsec-extra-configs";
+    paths = lib.mapAttrsToList (
+      path: settings:
+      (settingsFormat.generate path settings).overrideAttrs (old: {
+        patchPhase = ''
+          mkdir -p "$out/${dirOf path}/"
+          out="$out/${dirOf path}/"
+
+          echo $out
+          exit 1
+        '';
+      })
+    ) cfg.extraConfig;
+  };
 in
 {
   imports = [ ./remediations ];
@@ -38,6 +54,7 @@ in
   options.security.crowdsec =
     let
       inherit (lib.types)
+        attrsOf
         nullOr
         listOf
         package
@@ -85,6 +102,16 @@ in
         '';
       };
 
+      extraConfig = lib.mkOption {
+        type = attrsOf (settingsFormat.type);
+        default = {
+          "parsers/s02-enrich/nixos-whitelist.yaml" = cfg.parserWhitelist;
+        };
+        description = ''
+          Set of additional configurations to install.
+        '';
+      };
+
       acquisitions = lib.mkOption {
         type = listOf settingsFormat.type;
         default = [ ];
@@ -247,7 +274,10 @@ in
             online_client = {
               # By default, we don't let crowdsec phone home, since
               # this is usually within NixOS users' concerns.
-              sharing = lib.mkDefault false;
+              #
+              # TODO: Enable when this option becomes available
+              # (1.6.4, current nixpkgs-unstable)
+              # sharing = lib.mkDefault false;
               credentials_path = cfg.centralApiCredentials;
             };
           };
@@ -264,7 +294,9 @@ in
       };
     };
 
-    systemd.packages = [ cfg.package ];
+    systemd.packages = [
+      cfg.package
+    ];
 
     environment = {
       systemPackages = [
@@ -295,33 +327,6 @@ in
           group = "crowdsec";
           mode = "0700";
         };
-
-        "${cfg.stateDirectory}/config/parsers".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
-          user = "crowdsec";
-          group = "crowdsec";
-          mode = "0700";
-        };
-
-        "${cfg.stateDirectory}/config/parsers/s02-enrich".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
-          user = "crowdsec";
-          group = "crowdsec";
-          mode = "0700";
-        };
-
-        "${cfg.stateDirectory}/config/parsers/s02-enrich/nixos-whitelist.yaml" =
-          lib.mkIf (cfg.parserWhitelist != [ ])
-            {
-              "L+".argument =
-                (settingsFormat.generate "crowdsec-nixos-whitelist.yaml" {
-                  name = "nixos/parser-whitelist";
-                  description = "Parser whitelist generated by the crowdsec NixOS module";
-                  whitelist = {
-                    reason = "Filtered by NixOS whitelist";
-                    ip = lib.lists.filter (ip: !(lib.hasInfix "/" ip)) cfg.parserWhitelist;
-                    cidr = lib.lists.filter (ip: lib.hasInfix "/" ip) cfg.parserWhitelist;
-                  };
-                }).outPath;
-            };
       };
 
       services = {
@@ -331,6 +336,8 @@ in
           description = "Crowdsec database and config preparation";
 
           script = ''
+            cp --copy-contents --recursive ${extraConfigs}/. ${cfg.stateDirectory}/config
+
             if [ ! -e '${cfg.settings.config_paths.simulation_path}' ]; then
                 cp '${cfg.package}/share/crowdsec/config/simulation.yaml' '${cfg.settings.config_paths.simulation_path}'
             fi
diff --git a/modules/crowdsec/remediations/cs-firewall-bouncer.nix b/modules/crowdsec/remediations/cs-firewall-bouncer.nix
index 42accc6..aa70552 100644
--- a/modules/crowdsec/remediations/cs-firewall-bouncer.nix
+++ b/modules/crowdsec/remediations/cs-firewall-bouncer.nix
@@ -6,11 +6,10 @@
   ...
 }:
 let
-  inherit (flake-inputs.self.packages.${pkgs.system}) crowdsec-firewall-bouncer;
-
   crowdsecCfg = config.security.crowdsec;
   cfg = crowdsecCfg.remediationComponents.firewallBouncer;
   settingsFormat = pkgs.formats.yaml { };
+  crowdsec-firewall-bouncer = flake-inputs.self.packages.${pkgs.system}.crowdsec-firewall-bouncer;
 in
 {
   options.security.crowdsec.remediationComponents.firewallBouncer = {
@@ -32,7 +31,9 @@ in
     security.crowdsec.remediationComponents.firewallBouncer.settings = {
       mode = lib.mkDefault "${if config.networking.nftables.enable then "nftables" else "iptables"}";
       log_mode = "stdout";
-      iptables_chains = [ "nixos-fw" ];
+      iptables_chains = [
+        "nixos-fw"
+      ];
 
       # Don't let users easily override this; unfortunately we need to
       # set up this key through substitution at runtime.
@@ -77,7 +78,9 @@ in
           requiredBy = [ "crowdsec.service" ];
 
           path =
-            lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [ pkgs.ipset ]
+            lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [
+              pkgs.ipset
+            ]
             ++ lib.optional (cfg.settings.mode == "iptables") pkgs.iptables
             ++ lib.optional (cfg.settings.mode == "nftables") pkgs.nftables;
         };
diff --git a/modules/crowdsec/remediations/default.nix b/modules/crowdsec/remediations/default.nix
index c3c0790..7df6ade 100644
--- a/modules/crowdsec/remediations/default.nix
+++ b/modules/crowdsec/remediations/default.nix
@@ -1 +1,5 @@
-{ imports = [ ./cs-firewall-bouncer.nix ]; }
+{
+  imports = [
+    ./cs-firewall-bouncer.nix
+  ];
+}
diff --git a/pkgs/crowdsec/_sources/generated.json b/pkgs/crowdsec/_sources/generated.json
index f3c8f01..ac251aa 100644
--- a/pkgs/crowdsec/_sources/generated.json
+++ b/pkgs/crowdsec/_sources/generated.json
@@ -21,7 +21,7 @@
     },
     "crowdsec-hub": {
         "cargoLocks": null,
-        "date": "2025-05-17",
+        "date": "2025-02-22",
         "extract": null,
         "name": "crowdsec-hub",
         "passthru": null,
@@ -33,10 +33,10 @@
             "name": null,
             "owner": "crowdsecurity",
             "repo": "hub",
-            "rev": "850614b9fcd4298f559b422c5ac685a69aa2e5ff",
-            "sha256": "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k=",
+            "rev": "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df",
+            "sha256": "sha256-45pUln7Qj5luY9I9BE2qhzjH7kv4IbYvNoEX3/4AVVg=",
             "type": "github"
         },
-        "version": "850614b9fcd4298f559b422c5ac685a69aa2e5ff"
+        "version": "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df"
     }
 }
\ No newline at end of file
diff --git a/pkgs/crowdsec/_sources/generated.nix b/pkgs/crowdsec/_sources/generated.nix
index 19a7f5a..9c63cc5 100644
--- a/pkgs/crowdsec/_sources/generated.nix
+++ b/pkgs/crowdsec/_sources/generated.nix
@@ -14,14 +14,14 @@
   };
   crowdsec-hub = {
     pname = "crowdsec-hub";
-    version = "850614b9fcd4298f559b422c5ac685a69aa2e5ff";
+    version = "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df";
     src = fetchFromGitHub {
       owner = "crowdsecurity";
       repo = "hub";
-      rev = "850614b9fcd4298f559b422c5ac685a69aa2e5ff";
+      rev = "f9883cd6c7d1913c13e4a3a69d9a0b887a7d57df";
       fetchSubmodules = false;
-      sha256 = "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k=";
+      sha256 = "sha256-45pUln7Qj5luY9I9BE2qhzjH7kv4IbYvNoEX3/4AVVg=";
     };
-    date = "2025-05-17";
+    date = "2025-02-22";
   };
 }
diff --git a/pkgs/crowdsec/hub.nix b/pkgs/crowdsec/hub.nix
index 1b8c9b3..d057ca8 100644
--- a/pkgs/crowdsec/hub.nix
+++ b/pkgs/crowdsec/hub.nix
@@ -1 +1,4 @@
-{ sources }: sources.crowdsec-hub.src
+{
+  sources,
+}:
+sources.crowdsec-hub.src