diff --git a/config/fudo/backplane/dns.nix b/config/fudo/backplane/dns.nix
index e36b775..9b20319 100644
--- a/config/fudo/backplane/dns.nix
+++ b/config/fudo/backplane/dns.nix
@@ -134,15 +134,14 @@ in {
     listen-v6-addresses = mkOption {
       type = with types; listOf str;
       description = "IPv6 addresses on which to listen for dns requests.";
-      example = [
-        "[abcd::1]"
-      ];
-      default = [];
+      example = [ "[abcd::1]" ];
+      default = [ ];
     };
 
     required-services = mkOption {
       type = with types; listOf str;
-      description = "A list of services required before the DNS server can start.";
+      description =
+        "A list of services required before the DNS server can start.";
     };
 
     user = mkOption {
@@ -177,18 +176,12 @@ in {
           createHome = true;
           home = "/var/home/${cfg.user}";
         };
-        backplane-powerdns = {
-          isSystemUser = true;
-        };
+        backplane-powerdns = { isSystemUser = true; };
       };
 
       groups = {
-        "${cfg.group}" = {
-          members = [cfg.user];
-        };
-        backplane-powerdns = {
-          members = [ "backplane-powerdns" ];
-        };
+        "${cfg.group}" = { members = [ cfg.user ]; };
+        backplane-powerdns = { members = [ "backplane-powerdns" ]; };
       };
     };
 
@@ -221,31 +214,30 @@ in {
             "backplane-dns-config-generator.service"
             "backplane-dns.target"
           ];
-          after = [
-            "network.target"
-            "postgresql.service"
-          ];
+          after = [ "network.target" "postgresql.service" ];
           wantedBy = [ "multi-user.target" ];
 
           path = with pkgs; [ postgresql ];
 
           serviceConfig = {
-            Restart="on-failure";
-            RestartSec="10";
-            StartLimitInterval="0";
-            PrivateDevices=true;
+            Restart = "on-failure";
+            RestartSec = "10";
+            StartLimitInterval = "0";
+            PrivateDevices = true;
             # CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT";
             # NoNewPrivileges=true;
             ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${powerdns-home}";
-            ExecStart = "${pkgs.powerdns}/bin/pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}";
-            ProtectSystem="full";
+            ExecStart =
+              "${pkgs.powerdns}/bin/pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}";
+            ProtectSystem = "full";
             # ProtectHome=true;
-            RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6";
+            RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
           };
         };
 
         backplane-dns-config-generator = {
-          description = "Generate postgres configuration for backplane DNS server.";
+          description =
+            "Generate postgres configuration for backplane DNS server.";
           requiredBy = [ "backplane-powerdns.service" ];
           requires = cfg.required-services;
           serviceConfig.Type = "oneshot";
@@ -260,35 +252,35 @@ in {
           # This builds the config in a bash script, to avoid storing the password
           # in the nix store at any point
           script = ''
-              if [ ! -d ${powerdns-conf-dir} ]; then
-              mkdir ${powerdns-conf-dir}
-              fi
+            if [ ! -d ${powerdns-conf-dir} ]; then
+            mkdir ${powerdns-conf-dir}
+            fi
 
-              TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX)
-              TMPCONF=$TMPDIR/pdns.local.gpgsql.conf
+            TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX)
+            TMPCONF=$TMPDIR/pdns.local.gpgsql.conf
 
-              if [ ! -f ${cfg.database.password-file} ]; then
-              echo "${cfg.database.password-file} does not exist!"
-              exit 1
-              fi
+            if [ ! -f ${cfg.database.password-file} ]; then
+            echo "${cfg.database.password-file} does not exist!"
+            exit 1
+            fi
 
-              touch $TMPCONF
-              chown backplane-powerdns:backplane-powerdns $TMPCONF
-              chmod go-rwx $TMPCONF
-              PASSWORD=$(cat ${cfg.database.password-file})
-              echo "launch+=gpgsql" >> $TMPCONF
-              echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF
-              echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF
-              echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF
-              echo "gpgsql-password=$PASSWORD" >> $TMPCONF
-              echo "gpgsql-dnssec=yes" >> $TMPCONF
+            touch $TMPCONF
+            chown backplane-powerdns:backplane-powerdns $TMPCONF
+            chmod go-rwx $TMPCONF
+            PASSWORD=$(cat ${cfg.database.password-file})
+            echo "launch+=gpgsql" >> $TMPCONF
+            echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF
+            echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF
+            echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF
+            echo "gpgsql-password=$PASSWORD" >> $TMPCONF
+            echo "gpgsql-dnssec=yes" >> $TMPCONF
 
-              mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf
+            mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf
 
-              rm -rf $TMPDIR
+            rm -rf $TMPDIR
 
-              exit 0
-            '';
+            exit 0
+          '';
         };
 
         backplane-dns = {
@@ -296,11 +288,13 @@ in {
           restartIfChanged = true;
 
           serviceConfig = {
-            ExecStart = "${pkgs.backplane-dns-server}/bin/launch-backplane-dns.sh";
+            ExecStart =
+              "${pkgs.backplane-dns-server}/bin/launch-backplane-dns.sh";
             Restart = "on-failure";
             PIDFile = "/run/backplane-dns.$USERNAME.pid";
             User = cfg.user;
             Group = cfg.group;
+            StandardOutput = "journal";
           };
 
           environment = {
@@ -311,13 +305,15 @@ in {
             FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file;
             FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host;
             FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database;
-            FUDO_DNS_BACKPLANE_DATABASE_USERNAME = cfg.backplane.database.username;
-            FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = cfg.backplane.database.password-file;
+            FUDO_DNS_BACKPLANE_DATABASE_USERNAME =
+              cfg.backplane.database.username;
+            FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE =
+              cfg.backplane.database.password-file;
 
             # CL_SOURCE_REGISTRY = "${pkgs.localLispPackages.backplane-dns}//";
 
-            CL_SOURCE_REGISTRY = lib.concatStringsSep ":" (map (pkg: "${pkg}//")
-               lisp-pkgs);
+            CL_SOURCE_REGISTRY =
+              lib.concatStringsSep ":" (map (pkg: "${pkg}//") lisp-pkgs);
           };
 
           requires = cfg.required-services;
diff --git a/fudo/user-aliases.nix b/fudo/user-aliases.nix
index 05cfb9e..e38b19f 100644
--- a/fudo/user-aliases.nix
+++ b/fudo/user-aliases.nix
@@ -7,6 +7,7 @@
     "peter@fudo.link"
     "pselby@fudo.org"
     "yiliu@fudo.org"
+    "forum@selby.ca"
 
     "peter@selby.ca"
   ];
@@ -26,11 +27,7 @@
     "reaper@fudo.link"
   ];
 
-  "swaff@fudo.org" = [
-    "mark@fudo.org"
-  ];
+  "swaff@fudo.org" = [ "mark@fudo.org" ];
 
-  "ken@selby.ca" = [
-    "kselby@selby.ca"
-  ];
+  "ken@selby.ca" = [ "kselby@selby.ca" ];
 }
diff --git a/fudo/users.nix b/fudo/users.nix
index 3f53c62..9ce4ea3 100644
--- a/fudo/users.nix
+++ b/fudo/users.nix
@@ -75,7 +75,7 @@
     uid = 10035;
     group = "selby";
     common-name = "Ken Selby";
-    hashed-password = "{SSHA}wUGV/9dr8inz/HyqSF/OWKxy0DCy5AI3";
+    hashed-password = "{SSHA}flr48Sao0/fUp8yl9zFpm8ERnI7qYTds";
     # hashed-password = "{SSHA}X8DxUcwH2Fzel5UKbGVNhC5B2vg0Prsc";
   };
 
@@ -272,7 +272,7 @@
     uid = 10086;
     group = "selby";
     common-name = "Helen Selby";
-    hashed-password = "{MD5}cT8gLj4MDWqeP/GnzPfgHQ==";
+    hashed-password = "{SSHA}uckUXX09MjYq9++sF3f9b2IY8a9UBIxm";
   };
 
   vee = {
diff --git a/hosts/france.nix b/hosts/france.nix
index 596e2cc..a22a472 100644
--- a/hosts/france.nix
+++ b/hosts/france.nix
@@ -8,7 +8,7 @@ let
   host_ipv4 = "208.81.3.117";
   # Use a special IP for git.fudo.org, since it needs to be SSH-able
   link_ipv4 = "208.81.3.126";
-  all-hostnames = [];
+  all-hostnames = [ ];
 
   acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem";
   acme-certificate = hostname: "/var/lib/acme/${hostname}/fullchain.pem";
@@ -33,6 +33,7 @@ in {
     ../defaults.nix
     ./france/jabber.nix
     ./france/backplane.nix
+    ./france/selby-forum.nix
   ];
 
   environment.systemPackages = with pkgs; [
@@ -55,22 +56,18 @@ in {
 
     www-root = /srv/www;
 
-    local-networks = [
-      "208.81.1.128/28"
-      "208.81.3.112/28"
-      "172.17.0.0/16"
-      "127.0.0.0/8"
-    ];
+    local-networks =
+      [ "208.81.1.128/28" "208.81.3.112/28" "172.17.0.0/16" "127.0.0.0/8" ];
   };
 
   fudo.prometheus = {
     enable = true;
     hostname = "metrics.fudo.org";
     service-discovery-dns = {
-      node =    [ "node._metrics._tcp.fudo.org" ];
+      node = [ "node._metrics._tcp.fudo.org" ];
       postfix = [ "postfix._metrics._tcp.fudo.org" ];
       dovecot = [ "dovecot._metrics._tcp.fudo.org" ];
-      rspamd =  [ "rspamd._metrics._tcp.fudo.org" ];
+      rspamd = [ "rspamd._metrics._tcp.fudo.org" ];
     };
   };
 
@@ -91,9 +88,7 @@ in {
   };
 
   # So that grafana waits for postgresql
-  systemd.services.grafana.after = [
-    "postgresql.service"
-  ];
+  systemd.services.grafana.after = [ "postgresql.service" ];
 
   fudo.postgresql = {
     enable = true;
@@ -162,27 +157,16 @@ in {
           };
         };
       };
-      niten = {};
+      niten = { };
     };
 
-    local-users = [
-      "niten"
-      "fudo_git"
-    ];
+    local-users = [ "niten" "fudo_git" ];
 
     databases = {
-      fudo_git = {
-        users = ["niten"];
-      };
-      grafana = {
-        users = ["niten"];
-      };
-      mattermost = {
-        users = ["niten"];
-      };
-      webmail = {
-        users = ["niten"];
-      };
+      fudo_git = { users = [ "niten" ]; };
+      grafana = { users = [ "niten" ]; };
+      mattermost = { users = [ "niten" ]; };
+      webmail = { users = [ "niten" ]; };
     };
   };
 
@@ -207,7 +191,8 @@ in {
       ns3 = {
         ip-addresses = [ "104.131.53.95" ];
         ipv6-addresses = [ "2604:a880:800:10::8:7001" ];
-        description = "Nameserver 3, ns2.henchmman21.net, in New York City, NY, US";
+        description =
+          "Nameserver 3, ns2.henchmman21.net, in New York City, NY, US";
         rp = "reaper reaper.rp";
       };
       ns4 = {
@@ -218,11 +203,9 @@ in {
       };
     };
 
-    listen-ips = [host_ipv4];
+    listen-ips = [ host_ipv4 ];
 
-    domains = {
-      "fudo.org" = import ../fudo/fudo.org.nix { inherit config; };
-    };
+    domains = { "fudo.org" = import ../fudo/fudo.org.nix { inherit config; }; };
   };
 
   # Not all users need access to france; don't allow LDAP-user access.
@@ -251,11 +234,7 @@ in {
       # sslKey = (acme-private-key hostname);
       # sslCACert = acme-ca;
 
-      listen-uris = [
-        "ldap:///"
-        "ldaps:///"
-        "ldapi:///"
-      ];
+      listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ];
 
       users = import ../fudo/users.nix;
 
@@ -271,11 +250,7 @@ in {
       realm = "FUDO.ORG";
       mkey-file = "/var/heimdal/m-key";
       acl-file = "/etc/heimdal/kdc.acl";
-      bind-addresses = [
-        host_ipv4
-        "127.0.0.1"
-        "127.0.1.1"
-      ];
+      bind-addresses = [ host_ipv4 "127.0.0.1" "127.0.1.1" ];
     };
   };
 
@@ -293,7 +268,7 @@ in {
 
   fudo.mail-server = import ../fudo/email.nix { inherit config; } // {
     enableContainer = true;
-    debug = true;
+    debug = false;
     monitoring = true;
 
     hostname = mail-hostname;
@@ -459,32 +434,26 @@ in {
         # result of:
         # echo $FQDN-extif|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
         macAddress = "02:d4:e8:3b:10:2f";
-        ipv4.addresses = [
-          {
-            address = host_ipv4;
-            prefixLength = 28;
-          }
-        ];
+        ipv4.addresses = [{
+          address = host_ipv4;
+          prefixLength = 28;
+        }];
       };
       extif1 = {
         macAddress = "02:6d:e2:e1:ad:ca";
-        ipv4.addresses = [
-          {
-            address = link_ipv4;
-            prefixLength = 28;
-          }
-        ];
+        ipv4.addresses = [{
+          address = link_ipv4;
+          prefixLength = 28;
+        }];
       };
       intif0 = {
         # result of:
         # echo $FQDN-intif|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
         macAddress = "02:ba:ba:e9:08:21";
-        ipv4.addresses = [
-          {
-            address = "192.168.11.1";
-            prefixLength = 24;
-          }
-        ];
+        ipv4.addresses = [{
+          address = "192.168.11.1";
+          prefixLength = 24;
+        }];
       };
     };
   };
@@ -496,35 +465,31 @@ in {
       enable = true;
       enableOnBoot = true;
 
-      autoPrune = {
-        enable = true;
-      };
+      autoPrune = { enable = true; };
     };
 
-    lxd = {
-      enable = true;
-    };
+    lxd = { enable = true; };
   };
 
   fileSystems = {
     "/srv/archiva" = {
       fsType = "btrfs";
-      options = ["subvol=archiva"];
+      options = [ "subvol=archiva" ];
       label = "pool0";
     };
     "/srv/grafana" = {
       fsType = "btrfs";
-      options = ["subvol=grafana"];
+      options = [ "subvol=grafana" ];
       label = "pool0";
     };
     "${system-mail-directory}" = {
       fsType = "btrfs";
-      options = ["subvol=mail"];
+      options = [ "subvol=mail" ];
       label = "pool0";
     };
     "/srv/gitlab" = {
       fsType = "btrfs";
-      options = ["subvol=gitlab"];
+      options = [ "subvol=gitlab" ];
       label = "pool0";
     };
     "/var/lib/lxd/storage-pools/pool0" = {
@@ -576,12 +541,12 @@ in {
           locations."/" = {
             proxyPass = "http://127.0.0.1:8001";
             extraConfig = ''
-                proxy_set_header Host $host;
-                proxy_set_header X-Real-IP $remote_addr;
-                proxy_set_header X-Forwarded-By $server_addr:$server_port;
-                proxy_set_header X-Forwarded-For $remote_addr;
-                proxy_set_header X-Forwarded-Proto $scheme;
-              '';
+              proxy_set_header Host $host;
+              proxy_set_header X-Real-IP $remote_addr;
+              proxy_set_header X-Forwarded-By $server_addr:$server_port;
+              proxy_set_header X-Forwarded-For $remote_addr;
+              proxy_set_header X-Forwarded-Proto $scheme;
+            '';
           };
         };
 
@@ -600,12 +565,10 @@ in {
   docker-containers = {
     archiva = {
       image = "xetusoss/archiva";
-      ports = ["127.0.0.1:8001:8080"];
+      ports = [ "127.0.0.1:8001:8080" ];
       # Ugly: name-to-uid lookup fails.
       user = toString config.users.users.archiva.uid;
-      volumes = [
-        "/srv/archiva:/archiva-data"
-      ];
+      volumes = [ "/srv/archiva:/archiva-data" ];
       environment = {
         # Not directly connected to the world anyway
         SSL_ENABLED = "false";
diff --git a/hosts/france/selby-forum.nix b/hosts/france/selby-forum.nix
new file mode 100644
index 0000000..e3948b9
--- /dev/null
+++ b/hosts/france/selby-forum.nix
@@ -0,0 +1,68 @@
+{ config, lib, pkgs, ... }:
+
+let
+  hostname = "forum.test.selby.ca";
+  postgres-host = "france.fudo.org";
+  config-path = "/srv/selby-forum/conf";
+  redis-data-path = "/srv/selby-forum/redis-data";
+  sidekiq-data-path = "/srv/selby-forum/sidekiq-data";
+  discourse-data-path = "/srv/selby-forum/discourse-data";
+
+in {
+  config = {
+    users.users = {
+      selby-discourse = { isSystemUser = true; };
+      selby-discourse-redis = { isSystemUser = true; };
+      selby-discourse-sidekiq = { isSystemUser = true; };
+    };
+
+    docker-containers = {
+      selby-discourse = {
+        image = "bitnami/discourse";
+        ports = [ ];
+        user = toString config.users.users.selby-discourse.uid;
+        volumes = [
+          "${config-path}:/opt/bitnami/discourse/mounted-conf"
+          "${discourse-data-path}:/bitnami"
+        ];
+        extraDockerOptions = [ "--network=selby-discourse" ];
+        environment = {
+          DISCOURSE_SITENAME = "Selby Forum";
+          DISCOURSE_EMAIL = "forum@selby.ca";
+          DISCOURSE_HOSTNAME = hostname;
+        };
+      };
+
+      selby-discourse-redis = {
+        image = "bitnami/redis";
+        user = toString config.users.users.selby-discourse-redis.uid;
+        volumes = [ "${redis-data-path}:/bitnami" ];
+        extraDockerOptions = [ "--network=selby-discourse" ];
+        environment = { ALLOW_EMPTY_PASSWORD = "yes"; };
+      };
+
+      selby-discourse-sidekiq = {
+        image = "bitnami/discourse";
+        user = toString config.users.users.selby-discourse-sidekiq.uid;
+        volumes = [ "${sidekiq-data-path}:/bitnami" ];
+        entrypoint = "nami";
+        cmd = [ "start" "discourse-sidekiq" ];
+        extraDockerOptions = [ "--network=selby-discourse" ];
+      };
+    };
+
+    systemd = {
+      services = {
+        # selby-discourse-config = {
+        #   description = "Generate configuration for Selby discourse server.";
+        #   requiredBy = [ "docker-selby-discourse.service" ];
+        #   requires = [ "fudo-passwords.target" ];
+        #   serviceConfig.Type = "oneshot";
+        #   restartIfChanged = true;
+
+        #   script = "";
+        # };
+      };
+    };
+  };
+}
diff --git a/packages/backplane-dns.nix b/packages/backplane-dns.nix
deleted file mode 100644
index 10d08c2..0000000
--- a/packages/backplane-dns.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-{ stdenv, fetchgit, pkgs }:
-
-let
-  url = "https://git.fudo.org/fudo-public/backplane-dns.git";
-  version = "0.1";
-
-in stdenv.mkDerivation {
-  name = "backplane-dns-${version}";
-
-  src = fetchgit {
-    url = url;
-    rev = "c552394e55816541a9426974c5f8e6f1f83bf195";
-    sha256 = "0r61bwj5a2dvzl41cwdf2pdnhdsmp3kzfyxa5x5hsg67al6s7vi8";
-    fetchSubmodules = false;
-  };
-
-  phases = ["installPhase"];
-
-  installPhase = ''
-    mkdir -p "$out/lib/common-lisp/backplane-dns"
-    cp "$src/backplane-dns.asd" "$out/lib/common-lisp/backplane-dns"
-    cp -R $src/*.lisp "$out/lib/common-lisp/backplane-dns"
-  '';
-}
diff --git a/packages/lisp/backplane-dns.nix b/packages/lisp/backplane-dns.nix
index 2f3345e..b2e7d5f 100644
--- a/packages/lisp/backplane-dns.nix
+++ b/packages/lisp/backplane-dns.nix
@@ -14,13 +14,14 @@ pkgs.lispPackages.buildLispPackage {
     cl-ppcre
     ip-utils
     postmodern
+    prove
     trivia
   ];
 
   src = pkgs.fetchgit {
-    url    = "https://git.fudo.org/fudo-public/backplane-dns.git";
-    rev    = "d9e13bae165b08976fd025053bb2dde44bb4278d";
-    sha256 = "0b4y75hq5753v8pk47c4pwpyc95igpjl7md7f29jjvqaviys66xh";
+    url = "https://git.fudo.org/fudo-public/backplane-dns.git";
+    rev = "3075453a8ccc8bf285bfc83d84317044590ae060";
+    sha256 = "1sdgr9zxqam4c8f7nlkgm77si45j0qvvgj6rav9kd6jz6vqgcbi5";
     fetchSubmodules = false;
   };
 
diff --git a/packages/lisp/backplane-server.nix b/packages/lisp/backplane-server.nix
index f23f621..096ea7e 100644
--- a/packages/lisp/backplane-server.nix
+++ b/packages/lisp/backplane-server.nix
@@ -7,17 +7,12 @@ pkgs.lispPackages.buildLispPackage {
 
   buildSystems = [ "backplane-server" ];
 
-  deps = with pkgs.lispPackages; [
-    alexandria
-    arrows
-    cl-json
-    cl-xmpp
-  ];
+  deps = with pkgs.lispPackages; [ alexandria arrows cl-json cl-xmpp prove ];
 
   src = pkgs.fetchgit {
-    url    = "https://git.fudo.org/fudo-public/backplane-server.git";
-    rev    = "665f362ce1a0a22bc10d3bbe95d5a8adec2df653";
-    sha256 = "0lzq0vlqjymcwxbc80x6wp5mij80am12w9fi7abs5wnqcs68lwnf";
+    url = "https://git.fudo.org/fudo-public/backplane-server.git";
+    rev = "5b50dd8badf5b5460e9cc7e76e191d274712a3bd";
+    sha256 = "18fysksmrbfk131fgazbw1cpaxz47015ashap9y4rswd904dzzss";
     fetchSubmodules = false;
   };