Turning a Raspberry Pi into a DIY Caddy Reverse Proxy for Home‑Hosted Services

Turning a Raspberry Pi into a DIY Caddy Reverse Proxy for Home‑Hosted Services

I’ve run a handful of services on a Pi for years—Plex, Nextcloud, Home Assistant—so the idea of putting a single, tidy entry point on top of them is pretty appealing. Caddy is the go‑to for this because it hands you a working HTTPS server, auto‑issues Let’s Encrypt certs, and runs nicely on ARM. Below is a hands‑on walk‑through that keeps all the commands and links intact, but reads like a coffee‑chat with a fellow sysadmin.

Why bother with a reverse proxy?

  • One hostname – instead of juggling http://192.168.1.5:32400, http://192.168.1.5:8080, etc., you hit https://home.example.com and let Caddy route the rest.
  • HTTPS everywhere – Caddy pulls certificates for you, so you never have to remember to renew or configure certbot.
  • Future‑proof – if you ever spin up a second Plex or add a new service, you just tweak the Caddyfile.
  • Centralised logs – one place to see who’s hitting what.

If you’re already deep into Nginx or Traefik, Caddy is a lighter alternative that still covers the essentials.


1. Prepare the Pi

1.1 Update the system

sudo apt update && sudo apt upgrade -y

1.2 Install required packages

Caddy only needs curl and systemd, but I’ll pull in git for later steps.

sudo apt install -y curl git

1.3 Create a dedicated user

Running everything as root is a no‑no. A separate caddy user keeps the attack surface tight.

sudo useradd -r -s /usr/sbin/nologin caddy
sudo mkdir /etc/caddy
sudo chown caddy:caddy /etc/caddy

2. Install Caddy

Grab the ARM build straight from the official download endpoint.

curl -sL https://caddyserver.com/download/arm64 | sudo tee /usr/local/bin/caddy > /dev/null
sudo chmod +x /usr/local/bin/caddy

Quick sanity check:

caddy version

You should see something like caddy 2.7.4.


3. Configure Caddy

Caddy’s Caddyfile lives in /etc/caddy/Caddyfile. Open it up:

sudo nano /etc/caddy/Caddyfile

Add a minimal skeleton:

{
    # Global options
    email [email protected]
    acme_ca https://acme-v02.api.letsencrypt.org/directory
}

home.example.com {
    reverse_proxy /plex 127.0.0.1:32400
    reverse_proxy /nextcloud 127.0.0.1:8080
    reverse_proxy /homeassistant 127.0.0.1:8123
}

3.1 Path‑based routing

The reverse_proxy /plex line tells Caddy to forward any request that starts with /plex to the local Plex instance. If you prefer subdomains, just replace the prefix with a domain:

plex.home.example.com {
    reverse_proxy 127.0.0.1:32400
}

3.2 TLS and automatic renewal

Caddy will automatically fetch and renew certs. Make sure your DNS A record points to the Pi’s public IP (or a dynamic DNS provider). The email field is where renewal notices land.


4. Set up a systemd unit

Create /etc/systemd/system/caddy.service:

[Unit]
Description=Caddy HTTP/2 web server
After=network.target

[Service]
User=caddy
Group=caddy
ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile
KillMode=mixed
Restart=on-failure
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target

Reload systemd and enable the service:

sudo systemctl daemon-reload
sudo systemctl enable --now caddy

Check that it’s running:

sudo systemctl status caddy

You should see “active (running)”.


5. Firewall considerations

If you’re using ufw, allow only the ports Caddy needs:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

If you expose other services directly, add those rules too.


6. Test the setup

Open a browser and hit:

  • https://home.example.com/plex – should bring up Plex’s web UI.
  • https://home.example.com/nextcloud – should show the Nextcloud login page.
  • https://home.example.com/homeassistant – should load Home Assistant.

If something’s off, tail the logs:

journalctl -u caddy -f

Common hiccups:

Symptom Likely cause Fix
502 Bad Gateway Backend not listening on the port you told Caddy Verify the service is running (`sudo netstat -tulpn
404 Not Found Path mismatch Make sure the URL matches the reverse_proxy prefix
TLS error DNS not pointing to the Pi Update the record or wait for propagation

7. Advanced tweaks

7.1 Rate limiting

If you want to guard against abuse, add a simple limiter:

home.example.com {
    @plex {
        path /plex
    }
    route @plex {
        limit_req zone=plex burst=10 nodelay
        reverse_proxy 127.0.0.1:32400
    }
}

7.2 Basic authentication

For a quick password guard:

home.example.com {
    reverse_proxy /homeassistant 127.0.0.1:8123 {
        basicauth {
            admin JDJhJDEwJH...
        }
    }
}

Generate the hash with htpasswd -nbB admin.

7.3 Logging format

If you prefer JSON logs for easier parsing:

log {
    output file /var/log/caddy/access.log {
        roll_size 10MiB
        roll_keep 5
        roll_keep_for 72h
    }
    format json
}

8. Security hardening

Measure Why it matters How to enable
Disable unused services Shrinks the attack surface sudo systemctl disable <service>
Use strong passwords Stops brute‑force passwd for local accounts
Keep Caddy updated Fixes vulnerabilities sudo apt update && sudo apt upgrade
Monitor logs Spot anomalies early journalctl -u caddy -f or ship to syslog

Caddy’s TLS handling already takes care of many web‑app pitfalls, but keep the Pi’s OS up to date.


9. Troubleshooting checklist

  1. Caddy not starting – journalctl -u caddy -xe; common culprits: missing Caddyfile, wrong permissions, or the caddy user not existing.
  2. Certificate not issued – Verify DNS, ensure ports 80/443 are reachable, and check Let’s Encrypt rate limits.
  3. Service unreachable – Double‑check the backend port and that it accepts connections from 127.0.0.1.
  4. Slow responses – Look for timeouts in the logs; add header_upstream if the backend needs the client IP.

10. Scaling beyond a single Pi

If you later add more Pis or a small server cluster, Caddy can load‑balance:

home.example.com {
    reverse_proxy /plex {
        to 192.168.1.10:32400 192.168.1.11:32400
        lb_policy round_robin
    }
}

Add a health check (health_path /health) to skip nodes that are down.


11. Final thoughts

Putting Caddy on a Raspberry Pi gives you a clean, secure, and low‑power gateway to all your home‑hosted services. The configuration is straightforward, TLS is handled automatically, and the small footprint leaves room for other tasks. With the steps above, you’ll have a hardened reverse proxy up and running in under an hour—no fancy tooling required.

See also