---
title: '009. Caddy Server Auth Crunch Plugin'
url: 'https://prozak.org/009-caddy-server-auth-crunch-plugin'
markdown: 'https://prozak.org/009-caddy-server-auth-crunch-plugin.md'
date: '2025-07-29'
description: 'This guide extends on the previous Caddy reverse proxy setup by introducing multi-user authentication with the Caddy-Security (AuthCrunch) plugin'
taxonomy:
  category:
    - self-hosting
  tag:
    - self-hosting
    - caddy
  archives_month:
    - jul_2025
  archives_year:
    - '2025'
---

[ Blog Posts ](https://prozak.org/)      009. Caddy Server Auth Crunch Plugin    

   29 *Jul*   
#### [009. Caddy Server Auth Crunch Plugin](https://prozak.org/009-caddy-server-auth-crunch-plugin)

  [self-hosting](https://prozak.org/tag:self-hosting) [caddy](https://prozak.org/tag:caddy)  ![](https://prozak.org/images/a/9/9/9/3/a99931ffcf96ce89fb2edc90b4f5f5b30d3dc51a-caddyautharchitecture.png) 

Building on our previous setup of [Caddy as Webproxy](https://prozak.org/blog/007-setting-up-vps-webproxy-to-homelab-servers-using-tailscale/) to your #homelab resources most likely you will be now in the situation where you end up in two sets of sites/services that you want to make available to the internet.

*Public service* - deemed as completely public, al content is to be visible and searchable through the internet some examples of this may be, a static website, your blog, a public RSS viewer, etc. Anything that you feel comfortable on trusting to be completely public.

*Private service* - deemed as available to the internet, but either the data, functionality or even the use case is deemed as something restricted to just you or a specific set of users.

In this scenario, you are bound to trust the application that you are hosting and exposing in terms of who can access/see/use the #selfhosted application.

Caddy Server does have basic\_auth capabilities, this means that you can add a basic HARD CODED user name and password for any exposed endpoint. This is good enough for a very basic authentication schema, but is not necessarily good enough considering a multi-users scenario.

On the other hand, Caddy Server is flexible enough to add extensions to it and increase its capabilities. With our target of enabling a multi user environment that is non complex like setting up Authelia or other SSO solution, we will be leveraging extra features to provide these capabilities.

**The Stack: What You’ll Use**

Tailscale – For secure mesh networking between your VPS and LAN machines. Optional: Any VPN solution that enable you have access

Caddy Server – A modern web server that handles automatic HTTPS and reverse proxying.

A VPS – A simple, external node that acts as your gateway. Recommendation: Only Debian — no Ubuntu here thank you very much.

go language - Language used to write the Caddy Server and plugins

Git - Version control system to get latest code and enable modified version

xcaddy - Official go language based caddy binary builder

**Architecture Overview**

![caddy_auth_architecture](https://prozak.org/images/7/e/b/5/7/7eb57873f7d34cd798f9793a30398db8d986ff43-caddyautharchitecture.png "caddy_auth_architecture")

```
#scenario 1 - Non Auth Service
[Client] → https://yourdomain.com → [VPS + Caddy] → Tailscale Tunnel → [Your LAN service]

#scenario 1 - Auth Service
[Client] → https://yourdomain.com → [VPS + Caddy] → [Caddy-Security Auth] → Tailscale Tunnel → [Your LAN service]
```

Expected outcomes, Password Correct, access granted, Password Wrong, access denied

**Step-by-Step Guide**

**I. VPS Setup**Enable your Debian VPS to build Caddy binaries.

GIT Install - xcaddy requires GIT installed in your VPS to pull latest version of the caddy plugins.

```
$ sudo apt install git -y
```

Stock version in Debian 12 is good for this project

Go Language Install - Follow install instructions published in the \[https://go.dev/doc/install) website.

*Recommended steps:*

DO NOT use the Debian 12 version in the repositories, as of the write of this post, version in repositories is 1.19, xcaddy requires 1.2+

```
$ sudo rm -rf /usr/local/go
$ sudo tar -C /usr/local -xzf go1.24.5.linux-amd64.tar.gz
```

.profile setup, create an sh script in /etc/profile.d/01-name-file.sh contents of the file should be:

```
#!/bin/sh
export PATH=$PATH:/usr/local/go/bin
```

Once you log out and log in go lang will be available on your VPS

```
$ go version
go version go1.24.5 linux/amd64
```

xcaddy Install - Follow the install instructions published in their [GitHub](https://github.com/caddyserver/xcaddy) repository.

Recommended steps:

```
$ sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-xcaddy.list
$ sudo apt update
$ sudo apt install xcaddy
```

This concludes the setup of the tooling, by now, your VPS is capable of building custom Caddy server versions.

**Custom Caddy Binary Build**

Create your new Caddy Server binary with your chosen extensions.

Create a working directory in your $HOME Invoke xcaddy with the arguments for caddy-security extension

```
$ xcaddy build --with github.com/greenpau/caddy-security
```

note: you can add as many extensions you prefer, they are published in their directory.

Enable Debian Package Support to quickly swap the version you want to run, the stock one, or your modified one.

```
$ sudo dpkg-divert --divert /usr/bin/caddy.default --rename /usr/bin/caddy
$ sudo mv ./caddy /usr/bin/caddy.custom
$ sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.default 10
$ sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.custom 50
$ sudo systemctl restart caddy

# To change the running binary behind your Caddy installation:

$ update-alternatives --config caddy
```

Note: To keep your Caddy custom version up to date in future, instead of updating through apt, you will update it using

```
$ sudo caddy upgrade
```

This command will pull the latest base version, and compile your local custom one with the latest and greatest both from Caddy and the Extensions you embedded.

**Caddy Configuration**

By now, your Caddy server runs with the extension of AuthCrunch (caddy-security). In their [documentation](https://docs.authcrunch.com/docs/intro), Paul Greenberg outlines the different use cases and deep configuration. For the sake of this guide, we will narrow down the use case on the local authorization, which still enables you to have a multi-user environment, but with simple integration.

Create the folder where you will store the local user base. /etc/caddy/auth/local

*Note: You do not need to create the users.json file, this will be created by Caddy upon first run and then you can modify it.*

Update your Caddyfile as this example:

```
{
    # Caddy directives to user authorization before reverse proxying
    order authenticate before respond
    order authorize before reverse_proxy
    # Configuration of the Local configuration
    # users.json will be created on first run, make sure path exists
    security {
        local identity store localdb {
            realm local
            path /etc/caddy/auth/local/users.json
        }
        #Setting up your Authentication Portal
        authentication portal myportal {
            enable identity store localdb
            cookie lifetime 86400 # 24 hours time to live
            cookie domain foo.com
            # Auth Portal UI configuration, adding links to some common features or other Authentication base application, visible when logged in
            ui {
                links {
                    "Application one" https://one.foo.com/ icon "las la-star"
                    "My Identity" "/whoami" icon "las la-user"
                }
            }
            transform user {
                match email admin@foo.com
                action add role authp/user
                ui link "Portal Settings" /settings icon "las la-cog"
            }
        }
        # Enablement of your Auth portal
        authorization policy admin_policy {
            set auth url https://auth.foo.com
            allow roles authp/user
        }
    }
}

# [ .... all your other Caddy configs/sites .... ]

# Auth Site
auth.foo.com {
    authenticate with myportal
}

# Your behind the Authentication site(s) you can add more and just make them authorize with admin_policy as first directive
one.foo.com {
    authorize with admin_policy
    reverse_proxy 192.168.1.125:8080 # Application service URL in your homelab 
}
whilst in /etc/caddy you can use caddy itself to validate your Caddyfile
$ caddy validate Caddyfile
Once everything is up and running, you can go ahead and restart your caddy service
$ sudo systemctl restart caddy
note: If caddy service does not restart, check the permissions for /etc/caddy/auth/local/users.json is 664. 
Hash your password using bcrypt, your can use an online converter such as this
Modify /etc/caddy/auth/local/users.json to have your admin@foo.com account
{
  "version": "1.1.7",
  "policy": {
    "password": {
      "keep_versions": 10,
      "min_length": 8,
      "max_length": 128,
      "require_uppercase": false,
      "require_lowercase": false,
      "require_number": false,
      "require_non_alpha_numeric": false,
      "block_reuse": false,
      "block_password_change": false
    },
    "user": {
      "min_length": 3,
      "max_length": 50,
      "allow_non_alpha_numeric": false,
      "allow_uppercase": false
    }
  },
  "revision": 3,
  "last_modified": "2025-07-29T02:25:01.685391413Z",
  "users": [
    {
      "id": "d65aac0f-98d6-4e5e-be88-4024fb6a3482",
      "username": "admin",
      "email_address": {
        "address": "admin@foo.com",
        "domain": "foo.com"
      },
      "email_addresses": [
        {
          "address": "admin@foo.com",
          "domain": "foo.com"
        }
      ],
      "passwords": [
        {
          "purpose": "generic",
          "algorithm": "bcrypt",
          "hash": <STRINGWITHHASHEDPASSWORDINBCRYPT>,
          "cost": 10,
          "expired_at": "0001-01-01T00:00:00Z",
          "created_at": "2025-07-29T02:14:50.923069716Z",
          "disabled_at": "0001-01-01T00:00:00Z"
        }
      ],
      "created": "2025-07-29T02:14:50.923069074Z",
      "last_modified": "2025-07-29T02:14:50.994872031Z",
      "roles": [
        {
          "name": "admin",
          "organization": "authp"
        }
      ]
    },
    {
      <MODIFY OTHER USERS....>
}
```

**Conclusion**

This how-to enables secure, repeatable authorization layer to your #homelab services without exposing your LAN to the internet and without adding additional infrastructure to support, relying only on tools that do one thing and do it well.

 [ Next Post](https://prozak.org/010-centralized-wildcard-certificate-on-debian-with-lets-encrypt-desec) [Previous Post ](https://prozak.org/008-the-web-is-meant-to-be-personal)

#### Related Posts

- 100 [011. Deploying Centralized TLS Certificates Node + Docker Application](https://prozak.org/011-deploying-centralized-tls-certificates-node-docker-application "011. Deploying Centralized TLS Certificates Node + Docker Application")
- 100 [007. Setting up VPS Webproxy to #homelab servers using Tailscale](https://prozak.org/007-setting-up-vps-webproxy-to-homelab-servers-using-tailscale "007. Setting up VPS Webproxy to #homelab servers using Tailscale")
- 50 [012. Dynamic DNS Python Script -> deSEC.io DNS Service](https://prozak.org/012-dynamic-dns-python-script-greater-than-desec-io-dns-service "012. Dynamic DNS Python Script -> deSEC.io DNS Service")
- 50 [010. Centralized Wildcard Certificate on Debian with Let’s Encrypt + deSEC](https://prozak.org/010-centralized-wildcard-certificate-on-debian-with-lets-encrypt-desec "010. Centralized Wildcard Certificate on Debian with Let’s Encrypt + deSEC")
- 50 [006. Why #SelfHosting should be preferred](https://prozak.org/006-why-selfhosting-should-be-preferred "006. Why #SelfHosting should be preferred")

#### Links

- [< ](https://webring.obeythesystem.com/page?=previous)[OBEY THE SYSTEM Webring](https://webring.obeythesystem.com/)[ >](https://webring.obeythesystem.com/page?=next)
- [rc64k@Github](https://github.com/rc64k)
- [Prozak's Mastodon](https://mastodon.social/@prozak)

#### Popular Tags

  [self-hosting](https://prozak.org/tag:self-hosting) [linux](https://prozak.org/tag:linux) [python](https://prozak.org/tag:python) [caddy](https://prozak.org/tag:caddy) [cloud](https://prozak.org/tag:cloud) [privacy](https://prozak.org/tag:privacy) [Philosophy](https://prozak.org/tag:Philosophy) [Quote](https://prozak.org/tag:Quote) [emby](https://prozak.org/tag:emby) [tailscale](https://prozak.org/tag:tailscale) [influxdb](https://prozak.org/tag:influxdb) [network](https://prozak.org/tag:network) [unifi](https://prozak.org/tag:unifi) [artificial intelligence](https://prozak.org/tag:artificial%20intelligence) 

#### Archives

- [ 1 October 2026  ](https://prozak.org/blog/archives_month:oct_2026)
- [ 2 July 2026  ](https://prozak.org/blog/archives_month:jul_2026)
- [ 3 March 2026  ](https://prozak.org/blog/archives_month:mar_2026)
- [ 3 July 2025  ](https://prozak.org/blog/archives_month:jul_2025)
- [ 1 May 2025  ](https://prozak.org/blog/archives_month:may_2025)
- [ 1 March 2025  ](https://prozak.org/blog/archives_month:mar_2025)
- [ 1 September 2024  ](https://prozak.org/blog/archives_month:sep_2024)
- [ 1 February 2024  ](https://prozak.org/blog/archives_month:feb_2024)
- [ 1 August 2023  ](https://prozak.org/blog/archives_month:aug_2023)
- [ 1 March 2021  ](https://prozak.org/blog/archives_month:mar_2021)
- [ 2 February 2021  ](https://prozak.org/blog/archives_month:feb_2021)

#### RSS & Atom Feeds

 [ Atom 1.0](https://prozak.org/blog.atom) [ RSS](https://prozak.org/blog.rss)

---

## Navigation

- Parent: [Blog Posts](https://prozak.org/index.md)
- Previous: [010. Centralized Wildcard Certificate on Debian with Let’s Encrypt + deSEC](https://prozak.org/010-centralized-wildcard-certificate-on-debian-with-lets-encrypt-desec.md)
- Next: [008. The Web Is Meant to Be Personal](https://prozak.org/008-the-web-is-meant-to-be-personal.md)
