NixOS Home Server Series - Part 4 Secure Vaultwarden with Caddy and Porkbun DNS

In part 4, we’re going to make Vaultwarden production-ready by putting it behind a secure, auto-renewing HTTPS reverse proxy. We’ll use Caddy for its dead-simple configuration and built-in Let’s Encrypt support. To make SSL renewal hands-off, we’ll use Porkbun’s DNS-01 challenge, so you never have to worry about certificate warnings or manual renewals.

We will be using a special module for Caddy to enable this, but fret not if you don’t use Porkbun. You can adapt this to many other DNS providers that support the DNS-01 challenge, see here for more modules.

Let’s get those fancy pants 🩲 out—it’s time to go pro.

Caddy Reverse Proxy with Porkbun DNS

Caddy is my go-to for HTTPS. It just works. With the right plugin, it can handle DNS-01 challenges for wildcard or private domains, and Porkbun is a great registrar with free private WHOIS.

Here’s how to set up Caddy as a reverse proxy for Vaultwarden, using Porkbun for automatic SSL all managed natively in NixOS.

First create a new module for Caddy in your NixOS configuration:

# configuration.nix

{
  imports = [ 
    ./hardware-configuration.nix
    ./boot.nix
    ./users.nix
    ./networking.nix
    ./filesystems.nix
    ./system.nix
    ./modules/vaultwarden.nix
    ./modules/caddy.nix # New Caddy module
  ];

...

Then create the caddy.nix module file in your modules directory.

# caddy.nix

{ config, lib, pkgs, ... }:

{
    services.caddy = {
        enable = true;
        package = pkgs.caddy.withPlugins {
            plugins = [ "github.com/caddy-dns/porkbun@v0.3.1" ];
            hash = "update-me-after-installation"; # Replace with actual hash after installation
        };
    };

    # Provide Porkbun credentials to Caddy via environment file
    systemd.services.caddy.serviceConfig = {
        EnvironmentFile = "/etc/caddy/porkbun-credentials";
    };

    networking.firewall.allowedTCPPorts = [ 80 443 ];

    systemd.tmpfiles.rules = [
        "d /etc/caddy 0755 root root -"
        "f /etc/caddy/porkbun-credentials 0600 caddy caddy -"
    ];
}

Note: The withPlugins line ensures Caddy is built with Porkbun DNS support. THe first build will fail with hash mismatch - replace the temporary hash with the one Nix suggests.

Next you will need to create the credentials file for Porkbun. Create a file at /etc/caddy/porkbun-credentials with the following content:

# go to https://porkbun.com/account/api to set this up

sudo mkdir -p /etc/caddy
echo "PORKBUN_API_KEY=your_porkbun_key" | sudo tee /etc/caddy/porkbun-creds
echo "PORKBUN_SECRET_API_KEY=your_porkbun_secret" | sudo tee -a /etc/caddy/porkbun-creds

Create a new DNS record for your Vaultwarden instance in Porkbun. For example, if your domain is vault.example.com, create an A record pointing to your server’s IP address.

# notice in the example below you can use a local IP or a public one
# the beauty of DNS-01 means you can use a private IP if you want to keep it internal only

"vault.yourdomain.com  A  <your-private-or-public-server-ip>"

Update your existing vaulwarden.nix file from part 3, or if you are starting fresh, create a new vaultwarden.nix module:

# modules/vaultwarden.nix

{ config, lib, pkgs, ... }:

{
  services.vaultwarden = {
    enable = true;
    config = {
      ROCKET_PORT = 8222;
      ROCKET_ADDRESS = "127.0.0.1";
      DATA_FOLDER = "/var/lib/vaultwarden";
      WEB_VAULT_ENABLED = true;
    };
  };

  services.caddy = {
    enable = true;
    package = pkgs.caddy.withPlugins (plugins: [ "github.com/caddy-dns/porkbun@v1.3.0" ]);
  };

  services.caddy.virtualHosts."vault.yourdomain.com" = {
    extraConfig = ''
      tls {
        dns porkbun {
          api_key {env.PORKBUN_API_KEY}
          api_secret_key {env.PORKBUN_SECRET_API_KEY}
        }
      }

      reverse_proxy 127.0.0.1:8222

      header {
        X-Frame-Options DENY
        X-Content-Type-Options nosniff
        Referrer-Policy same-origin
        X-XSS-Protection "1; mode=block"
      }
    '';
  };

  systemd.services.caddy.serviceConfig.EnvironmentFile = "/etc/caddy/porkbun-creds";
  networking.firewall.allowedTCPPorts = [ 80 443 ];
}

Now, rebuild your NixOS configuration:

sudo nixos-rebuild switch

Don’t forget to replace the hash in the Caddy package line with the actual hash provided by Nix after the first build attempt. You can find this in the error message when you first try to build.

Now, you can access Vaultwarden securely at https://vault.yourdomain.com.

Caddy will keep your SSL certificates up to date, and you don’t need to mess with ports or self-signed certs.

That’s it—your self-hosted password manager is now as secure and easy to access as any commercial service, but you’re the one in control.

May 26, 2025