← All TILs · proxmox

ntfy in a Proxmox LXC, private and behind Caddy for TLS

proxmox - 2026-10-08

ntfy is a self-hosted push notification server: a script publishes a message to a topic with one HTTP request, and phones or browsers subscribed to that topic receive it. It's a good alert channel for a homelab. This entry installs it as an LXC (a lightweight Linux container managed by Proxmox, sharing the host's kernel) and puts it behind a reverse proxy that handles HTTPS, with anonymous access turned off.

Install the LXC

The Community Scripts page gives the command to run in the Proxmox host shell:

var_os='debian' bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/ntfy.sh)"

Its default profile is Debian 13, 1 CPU, 512 MB RAM and 2 GB disk; ntfy listens on plain HTTP, port 80, and its config is /etc/ntfy/server.yml. The page listed ntfy v2.28.0 on 2026-10-06.

Does it need TLS?

Not on a trusted LAN. Over plain HTTP, though, messages, topic names, passwords and tokens travel unencrypted. Add HTTPS as soon as you use passwords or tokens, reach it from outside the LAN (the phone app away from home), or send anything sensitive.

The usual layout keeps ntfy on HTTP and puts a reverse proxy in front: a server that receives the HTTPS connections, decrypts them (TLS termination) and forwards the requests to ntfy. ntfy can serve HTTPS itself (listen-https, cert-file, key-file), but then it has to manage certificates.

Configure ntfy

In /etc/ntfy/server.yml in the LXC:

base-url: "https://ntfy.example.net"
listen-http: ":80"
behind-proxy: true
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"

Restart it (systemctl restart ntfy, the Debian package's service), then add users. Users have a role (admin can read and write everything), regular users get per-topic rights (an ACL, access control list), and a token lets a script authenticate without a password:

ntfy user add --role=admin admin       # for the phone app
ntfy user add backup                   # for the scripts
ntfy access backup alerts write-only
ntfy token add backup                  # prints tk_...

Caddy in front

Caddy gets certificates on its own (as in this entry). With a public DNS name pointing at the proxy, and ports 80 and 443 reachable for Let's Encrypt:

ntfy.example.net {
    reverse_proxy http://NTFY_LXC_IP:80
}

LAN-only, either get a real certificate through the DNS-01 challenge (Caddy with deSEC), or add tls internal: Caddy signs the certificate with its own local CA (/data/caddy/pki/authorities/local/root.crt in the Caddy container), which clients must then trust. ntfy's docs note that Caddy's reverse_proxy handles WebSockets too, with no extra config.

Check it

curl -d "hi" https://ntfy.example.net/alerts                     # 403 forbidden
curl -H "Authorization: Bearer tk_..." -H "Title: backup" \
     -d "nightly backup ok" https://ntfy.example.net/alerts      # 200
curl -u admin "https://ntfy.example.net/alerts/json?poll=1"       # the message

With the setup above, in containers (ntfy 2.28.0 behind Caddy 2 with tls internal):

Request Result
anonymous publish or read 403
backup token, publish to alerts 200
backup token, read alerts (write-only) 403
backup token, publish to another topic 403
admin, read alerts the message
http:// GET through Caddy 308 to https://

Expose only Caddy's ports; keep the LXC's port 80 reachable from the proxy alone, since direct HTTP bypasses TLS.

Sources

Created 2026-10-08T18:16:04+02:00 · Edit