diff --git a/configuration/default.nix b/configuration/default.nix
index f6ff072..42e43ec 100644
--- a/configuration/default.nix
+++ b/configuration/default.nix
@@ -1,8 +1,9 @@
-{ config, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 {
   imports = [
     ./services/gitea.nix
+    ./services/hydra.nix
     ./services/minecraft.nix
     ./services/nextcloud.nix
     ./services/webserver.nix
@@ -54,18 +55,21 @@
     recommendedGzipSettings = true;
     recommendedProxySettings = true;
     clientMaxBodySize = "10G";
+    domain = "tlater.net";
 
     virtualHosts = let
       host = port: extra:
-        {
+        lib.recursiveUpdate {
           forceSSL = true;
           enableACME = true;
-          locations."/" = { proxyPass = "http://localhost:${toString port}"; };
-        } // extra;
+          locations."/" = { proxyPass = "http://127.0.0.1:${toString port}"; };
+        } extra;
+      domain = config.services.nginx.domain;
     in {
-      "tlater.net" = host 3002 { serverAliases = [ "www.tlater.net" ]; };
-      "gitea.tlater.net" = host 3000 { };
-      "nextcloud.tlater.net" = host 3001 { };
+      "${domain}" = host 3002 { serverAliases = [ "www.${domain}" ]; };
+      "gitea.${domain}" = host 3000 { };
+      "nextcloud.${domain}" = host 3001 { };
+      "hydra.${domain}" = host config.services.hydra.port { };
     };
   };
 
diff --git a/configuration/services/gitea.nix b/configuration/services/gitea.nix
index 0abdb49..2258566 100644
--- a/configuration/services/gitea.nix
+++ b/configuration/services/gitea.nix
@@ -14,6 +14,7 @@
   virtualisation.pods.gitea = {
     hostname = "gitea.tlater.net";
     publish = [ "3000:3000" "2221:2221" ];
+    network = "slirp4netns";
 
     containers = {
       gitea = {
@@ -26,7 +27,6 @@
           DB_HOST = "gitea-postgres:5432";
           DB_NAME = "gitea";
           DB_USER = "gitea";
-          DB_PASSWD = "/qNDDK9WCMuubfA7D8DFwfl9T+Gy2IMDvPhiNpcxZjY=";
 
           USER_UID = toString config.users.extraUsers.gitea.uid;
           USER_GID = toString config.users.extraGroups.gitea.gid;
@@ -42,7 +42,6 @@
         environment = {
           POSTGRES_DB = "gitea";
           POSTGRES_USER = "gitea";
-          POSTGRES_PASSWORD = "/qNDDK9WCMuubfA7D8DFwfl9T+Gy2IMDvPhiNpcxZjY=";
         };
         volumes = [ "gitea-db-data:/var/lib/postgresql/data" ];
       };
diff --git a/configuration/services/hydra.nix b/configuration/services/hydra.nix
new file mode 100644
index 0000000..2321407
--- /dev/null
+++ b/configuration/services/hydra.nix
@@ -0,0 +1,16 @@
+{ ... }:
+
+{
+  services.hydra = {
+    enable = true;
+    port = 3003;
+
+    hydraURL = "hydra.tlater.net";
+    notificationSender = "hydra@tlater.net";
+    extraConfig = ''
+      <gitea_authorization>
+
+      </gitea_authorization>
+    '';
+  };
+}
diff --git a/configuration/services/minecraft.nix b/configuration/services/minecraft.nix
index c3831aa..9b77c09 100644
--- a/configuration/services/minecraft.nix
+++ b/configuration/services/minecraft.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 let
   minecraft-server-args = [
@@ -52,7 +52,7 @@ let
 
 in {
   nixpkgs.config.allowUnfreePredicate = pkg:
-    builtins.elem (pkgs.lib.getName pkg) [ "forge-server" ];
+    builtins.elem (lib.getName pkg) [ "forge-server" ];
 
   virtualisation.oci-containers.containers.minecraft-voor-kia = let
     properties = ./configs/minecraft/voor-kia/server.properties;
diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix
index ba1754b..4b74ac7 100644
--- a/configuration/services/nextcloud.nix
+++ b/configuration/services/nextcloud.nix
@@ -4,6 +4,7 @@
   virtualisation.pods.nextcloud = {
     hostname = "nextcloud.tlater.net";
     publish = [ "3001:80" ];
+    network = "slirp4netns";
 
     containers = {
       nextcloud = {
@@ -18,7 +19,6 @@
           POSTGRES_DB = "nextcloud";
           POSTGRES_USER = "nextcloud";
           POSTGRES_HOST = "nextcloud-postgres";
-          POSTGRES_PASSWORD = "rI7t7Nek1yGA9ucrRc7Uhy0jcjwPjnXa8me4o8tJON8=";
           OVERWRITEPROTOCOL = "https";
         };
       };
@@ -43,7 +43,6 @@
         environment = {
           POSTGRES_DB = "nextcloud";
           POSTGRES_USER = "nextcloud";
-          POSTGRES_PASSWORD = "rI7t7Nek1yGA9ucrRc7Uhy0jcjwPjnXa8me4o8tJON8=";
         };
         volumes = [ "nextcloud-db-data:/var/lib/postgresql/data" ];
       };
diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix
index e1c396d..d912618 100644
--- a/configuration/services/webserver.nix
+++ b/configuration/services/webserver.nix
@@ -34,6 +34,10 @@
 
     ports = [ "3002:3002" ];
     volumes = [ "tlaternet-mail:/srv/mail" ];
-    extraOptions = [ "--hostname=tlater.net" ];
+    extraOptions = [
+      "--hostname=tlater.net"
+      # This can change with rocket 0.5.
+      "--stop-signal=SIGKILL"
+    ];
   };
 }
diff --git a/flake.lock b/flake.lock
index a1b77b8..03e3f45 100644
--- a/flake.lock
+++ b/flake.lock
@@ -2,11 +2,11 @@
   "nodes": {
     "flake-utils": {
       "locked": {
-        "lastModified": 1619345332,
-        "narHash": "sha256-qHnQkEp1uklKTpx3MvKtY6xzgcqXDsz5nLilbbuL+3A=",
+        "lastModified": 1620759905,
+        "narHash": "sha256-WiyWawrgmyN0EdmiHyG2V+fqReiVi8bM9cRdMaKQOFg=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "2ebf2558e5bf978c7fb8ea927dfaed8fefab2e28",
+        "rev": "b543720b25df6ffdfcf9227afafc5b8c1fabfae8",
         "type": "github"
       },
       "original": {
@@ -37,11 +37,11 @@
     },
     "nixos-hardware": {
       "locked": {
-        "lastModified": 1619336929,
-        "narHash": "sha256-joIC2D26jtSbHodR/o8r+5T9XL2hXs6NIp5mVyA4JOc=",
+        "lastModified": 1620983891,
+        "narHash": "sha256-E2OKVgGo/cUqDsrIeYGVx64b4cxgzd7+bX33NHL0rbA=",
         "owner": "nixos",
         "repo": "nixos-hardware",
-        "rev": "f7540d6c27704ec0fe56ecc8b2a9b663181850b0",
+        "rev": "c4399b921fa7ff5f93ee10b3521b56b722ed74d8",
         "type": "github"
       },
       "original": {
@@ -53,11 +53,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1619486598,
-        "narHash": "sha256-ZEvJ+uItcKLQ9uSjGbQFE6Euu9w1Y98x0TWydXIgHAI=",
+        "lastModified": 1621088295,
+        "narHash": "sha256-hn8Rk6o5nRbWK/GE+z2OK8DIDIgGxCmVkaXpNYkald0=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "d4e7af972158a14ebdd9c828b1c2e07e2ce7ef1c",
+        "rev": "17d3dab8647a31a00d8a11433a56cc12d84b5ab4",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index a9baf34..bf1c240 100644
--- a/flake.nix
+++ b/flake.nix
@@ -68,12 +68,28 @@
             (import ./modules)
 
             (import ./configuration)
-            ({ ... }: {
+            ({ config, lib, ... }: {
               users.users.tlater.password = "insecure";
 
               # Disable graphical tty so -curses works
               boot.kernelParams = [ "nomodeset" ];
 
+              # Sets the base domain for nginx to localhost so that we
+              # can easily test locally with the VM.
+              services.nginx.domain = lib.mkOverride 99 "localhost";
+
+              # Hydra uses X-Forwarded-Port to figure out how to
+              # include links to static files, but because we redirect
+              # 443 -> 3443 in the vm we need to explicitly set this
+              # (nginx can't see qemu's port redirection).
+              services.nginx.virtualHosts =
+                let domain = config.services.nginx.domain;
+                in {
+                  "hydra.${domain}".locations."/".extraConfig = ''
+                    proxy_set_header X-Forwarded-Port 3443;
+                  '';
+                };
+
               # # Set up VM settings to match real VPS
               # virtualisation.memorySize = 3941;
               # virtualisation.cores = 2;
diff --git a/modules/default.nix b/modules/default.nix
index 0bc1f1c..1fd86fc 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -1,5 +1,12 @@
-{ ... }:
+{ lib, ... }:
+
+with lib;
 
 {
   imports = [ ./virtualisation/pods.nix ];
+
+  options.services.nginx.domain = mkOption {
+    type = types.str;
+    description = "The base domain name to append to virtual domain names";
+  };
 }