Caddy Installation Guide for Proxmox 9 LXC
Step-by-step guide to installing Caddy as a reverse proxy in a Proxmox VE 9 LXC container with Debian 13, including DNS challenge setup with Cloudflare for valid SSL certificates.
Caddy Installation Guide for Proxmox 9 LXC (Debian 13)
How I Use Caddy
- Reverse proxy. Instead of 192.168.10.100 for Pi-hole, I can use pihole.hake.rodeo.
- DNS Challenge to get valid SSL certificates
Prerequisites
- Proxmox VE 9.0.x installed and running
- SSH or console access to Proxmox host
- Available static IP address on your network (e.g.,
192.168.10.103) - Your network gateway IP address
- Cloudflare API token (optional DNS challenge section)
- Pi-hole installed (optional DNS challenge section)
Network Configuration Required
Before starting, determine:
- Desired Caddy IP: An unused static IP (e.g.,
192.168.10.103) - Gateway (typically router) IP: Your router's IP (e.g.,
192.168.10.1) - Subnet Mask: Usually
/24for home networks
Step 1: Create the LXC
Update templates
pveam updateIdentify the correct version of Debian
pveam available | grep debian | grep -v turnkeyThis guide will use the latest Debian 13 (update for your case)
pveam download local debian-13-standard_13.1-2_amd64.tar.zstCreate the container
pct create 103 local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst \
--hostname caddy \
--memory 512 \
--cores 1 \
--rootfs local-lvm:4 \
--net0 name=eth0,bridge=vmbr0,ip=192.168.10.103/24,gw=192.168.10.1 \
--unprivileged 1 \
--onboot 1 \
--start 1Parameters explained:
103: Container ID (must be unique; adjust if already in use)--memory 512: 512MB RAM (sufficient for Caddy)--cores 1: Single CPU core (sufficient for home use)--rootfs local-lvm:4: 4GB disk space--net0: Static IP configuration--unprivileged 1: Runs as unprivileged container for better security (limits root access to host system)--onboot 1: Auto-start on system boot--start 1: Start immediately after creation
Important adjustments:
- Container ID (103): Change if already in use (check with
pct list) - IP Address: Replace
192.168.10.103/24with your desired static IP - Gateway: Replace
192.168.10.1with your network's gateway - Storage: Replace
local-lvmif using different storage - Bridge: Replace
vmbr0if using different bridge (check withip a | grep vmbr)
Verify the container is running
pct status 103Step 2: Container Setup
Enter the container
pct enter 103Set Locale to avoid warnings (paste all as one)
sed -i 's/^# en_US.UTF-8 UTF-8$/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
update-locale LANG=en_US.UTF-8
export LANG=en_US.UTF-8Update the container:
apt update && apt upgrade -yInstall required packages
apt install -y curl ca-certificates gnupg nano debian-keyringStep 3: Install Caddy
Download Caddy GPG key
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpgCreate the sources file
nano /etc/apt/sources.list.d/caddy-stable.sourcesThen paste in
Types: deb deb-src
URIs: https://dl.cloudsmith.io/public/caddy/stable/deb/debian
Suites: any-version
Components: main
Signed-By: /usr/share/keyrings/caddy-stable-archive-keyring.gpg
Set proper permissions
chmod 644 /etc/apt/sources.list.d/caddy-stable.sourceschmod 644 /usr/share/keyrings/caddy-stable-archive-keyring.gpgUpdate apt
apt updateInstall Caddy
apt install caddyVerify installation
caddy versionCaddy should now be running. In your browser navigate to http://192.168.10.103:80. You should see the default Caddy page.
Step 4: Optional DNS Challenge
I have a domain registered on Cloudflare, which I use with Caddy to get a wildcard certificate.
Go to Cloudflare and generate an API Token. You can do this under your profile > API Tokens > Edit zone DNS > Use template
Then under zone resources select your domain.
Download a custom-built Caddy binary from Caddy's official build server
curl -o caddy 'https://caddyserver.com/api/download?os=linux&arch=amd64&p=github.com%2Fcaddy-dns%2Fcloudflare'Stop Caddy service
systemctl stop caddyMake new Caddy binary executable
chmod +x caddyReplace existing binary
mv caddy /usr/bin/caddyStart the new binary
systemctl start caddyCreate a .env file to store your Cloudflare API token
nano /etc/caddy/.envEnter your API Token
CF_API_TOKEN=your_cloudflare_api_token_here
Set proper permissions
chmod 600 /etc/caddy/.envchown caddy:caddy /etc/caddy/.envThen update the systemd service to use the .env file - add the following
systemctl edit caddyLook for the line:
### Anything between here and the comment below will become the contents of the drop-in file
Below it, add:
[Service]
EnvironmentFile=/etc/caddy/.env
If you do not add it in this section, it will not be applied.
Press CTRL+X then Y to save and exit
Now open the Caddyfile to configure the DNS challenge and reverse proxies.
nano /etc/caddy/CaddyfileThis is a global options block that tells Caddy: "For ALL sites in this Caddyfile, use Cloudflare DNS challenge to get SSL certificates." Use global when all your domains share the same TLS requirements. Skip global when you need different configurations per site.
{
acme_dns cloudflare {env.CF_API_TOKEN}
}
Then below it add your route (replace hake.rodeo with your domain)
pihole.hake.rodeo {
reverse_proxy 192.168.10.100:80
}
dashboard.hake.rodeo {
reverse_proxy 192.168.10.102:80
}
Then CTRL+X then Y to save and exit. Then run
systemctl daemon-reloadAnd restart Caddy
systemctl restart caddyFinally, ensure that Pi-hole's local DNS records have been configured to point to your reverse proxy (Caddy) for each service's domain. This assumes Caddy is on 192.168.10.103:
pihole.hake.rodeo → 192.168.10.103
dashboard.hake.rodeo → 192.168.10.103
How It All Works Together
- DNS Resolution: Browser asks Pi-hole "What's the IP for pihole.hake.rodeo?"
- Pi-hole Response: Pi-hole returns Caddy's IP (192.168.10.103)
- HTTPS Connection: Browser connects to Caddy on port 443 (HTTPS)
- Certificate: Caddy presents valid SSL certificate (obtained via DNS challenge)
- Reverse Proxy: Caddy forwards request to backend (192.168.10.100:80)
- Response: Backend responds to Caddy, Caddy returns to browser
Security Considerations
- Caddy automatically obtains and renews SSL certificates
- The .env file contains sensitive credentials - never commit to git
- Consider firewall rules:
ufw allow 80/tcpandufw allow 443/tcp - Regularly update:
apt update && apt upgrade caddy
Next Steps
- Add more services to your Caddyfile
- Monitor logs:
journalctl -u caddy -f - Consider adding basic auth for sensitive services
- Explore Caddy modules: https://caddyserver.com/docs/modules/