Head to head
Caddy vs Nginx Proxy Manager
Both get you valid certificates without reading an ACME spec. The difference is whether your proxy configuration is something you can read, diff and restore.
Should you use Caddy or Nginx Proxy Manager?
Use Caddy unless you specifically want a web form, in which case Nginx Proxy Manager is the honest answer and no amount of arguing changes it. Caddy is 25 MB, one text file, and a site is three lines; NPM is 100 MB, a SQLite database and a UI that generates nginx config you must not hand-edit. The deciding factor for most people is not daily use, it is recovery: a Caddyfile is four lines in git, while NPM needs /data and /etc/letsencrypt restored together or nginx will not start.
This is not a performance comparison. Both put nginx-class throughput in front of services that will never saturate a home connection, and both get Let's Encrypt certificates without you learning ACME.
The axis is where configuration lives. Nginx Proxy Manager keeps it in a SQLite database and generates nginx config from it. Caddy keeps it in a file you wrote. Everything else in this comparison follows from that one difference: how you back it up, how you reproduce it, how you review a change, and what you do when something is wrong at a level the UI does not expose.
If you have never written a server block and do not want to start, NPM is the honest answer. The rest of this page is about what that choice costs later.
The specs are close, the ops load is not#
Memory is 25 MB against 100 MB idle, which matters on a 1 GB VPS and nowhere else. The row worth reading is the datastore, because it is the source of every difference below.
| Specification | Caddy | Nginx Proxy Manager |
|---|---|---|
| Licence | Apache-2.0 (Permissive) | MIT (Permissive) |
| Written in | Go | Node.js (Express) driving nginx/OpenResty |
| First release | 2015 | 2018 |
| Maturity | Mature | Mature |
| Datastore | Filesystem (certificates, keys and ACME account data in the data directory) | SQLite by default; MariaDB/MySQL or PostgreSQL optional |
| Services to run | 1 | 1 |
| Idle memory | 25 MB | 100 MB |
| Memory in use | 70 MB | 200 MB |
| Operational load | 1 / 5, Set and forget | 2 / 5, Light |
| Identity | Not applicable | Local accounts only |
| arm64 builds | Yes | Yes |
| Default ports | 80, 443, 2019 | 80, 443, 81 |
| Backup shape | File copy | SQLite backup |
NPM scores 2 on ops load rather than 1 for a specific reason: two coupled data directories have to be restored together, and the generated config is not something you can safely hand-edit.
Four lines against a form#
Here is a complete, working Caddy configuration for two services, including HTTPS, redirect and renewal:
jellyfin.example.com {
reverse_proxy 192.168.1.20:8096
}The NPM equivalent is: log in on port 81, Hosts, Proxy Hosts, Add Proxy Host, type the domain, type the IP, type the port, tick Websockets Support, SSL tab, Request a new certificate, Force SSL, Save. It takes about the same amount of time the first time and rather longer the tenth.
What you cannot do is show the NPM version to someone in a chat message, keep it in git, or diff it against last month. That is the trade, stated plainly.
The maintenance picture, as of now#
NPM's current release is 2.15.1, June 2026. Look at what recent releases contain: Debian base image bumps, OpenResty and certbot updates, CVE patches. Not new capability. There are roughly 890 open issues and the project is effectively one maintainer.
The v3 rewrite has an official status issue that has been open since June 2021, describing a Go backend, a React frontend, acme.sh in place of certbot and SQLite only. Five years later it has not shipped, and the released line is still the original Express and certbot stack.
That is not a scandal and it is not abandonment. Security updates arrive. But choose NPM for what it does today, because the feature set is not going to grow, and one concrete consequence has already landed: version 2.14 dropped armv7 images over a Node.js requirement, so a 32-bit Raspberry Pi is frozen at 2.13.7 and missing the OpenResty CVE fixes from 2.15.0. The fix is a 64-bit OS on the same board.
Caddy's release cadence is ordinary and its 2.11.x line has been shipping hardening work on the admin API. Different projects, different risk shapes: Caddy's is that you must rebuild custom plugin images on every upgrade, NPM's is that the thing you are running is in maintenance.
When the database gets confused#
NPM's state is /data/database.sqlite plus /etc/letsencrypt, and they reference each other. Proxy hosts point at certificates by numeric ID.
Restore /data from Tuesday and /etc/letsencrypt from Sunday and you get hosts referencing certificates that are not there. nginx then refuses to start, which means every site behind the proxy is down, not just the one that is inconsistent. The same class of failure appears when a volume is recreated, when you copy the data directory without preserving permissions, or when a certificate is deleted in the UI while a host still uses it.
The correct backup is both directories, taken together, with the container stopped or with sqlite3 database.sqlite ".backup out.db":
docker compose stop npm
tar czf npm-$(date +%F).tar.gz ./data ./letsencrypt
docker compose start npm/data/nginx and /data/logs can be excluded. The first is regenerated from the database on start; the second is disposable.
The Caddy equivalent is the Caddyfile, which is already in git, plus a volume of certificates that Caddy will simply re-issue if it is missing. There is no consistency requirement between two things, because there is only one thing. See Backups that actually restore for turning either into something that runs nightly.
Where NPM is genuinely better#
Three places, and they are not consolation prizes:
- Someone else can add a proxy host. In a household or small office where more than one person needs to publish a service and nobody else has SSH, a web form is not a compromise, it is the requirement.
- DNS-01 certificates without a rebuild. NPM bundles certbot DNS provider plugins and exposes them in a dropdown. Caddy ships zero DNS modules in its official binary and image, so wildcards or a port-80-unreachable host mean
xcaddy build --with github.com/caddy-dns/cloudflareand a rebuild on every upgrade. For a wildcard-first setup, NPM is less work than Caddy. That surprises people. - TCP and UDP streams. NPM's Streams tab forwards raw ports. Caddy needs a third-party layer 4 module and a custom build for the same thing.
It also ships HTTP basic auth access lists, which is enough protection for a couple of internal tools without deploying Authelia and everything that comes with it.
The two NPM traps that cost people a weekend#
Port 81 with default credentials. First boot creates admin@example.com with the password changeme. These are documented publicly and there is no rate limiting in front of the login. People expose 81 "just for setup", get scanned within the hour, and hand a stranger a proxy that can be pointed at anything on their LAN. Bind 81 to a LAN address or keep it behind a VPN, and finish the forced credential change before DNS points anywhere. A security baseline for a home server covers the rest of the exposure question.
Websockets and HTTP/2 are off by default, per host. A new proxy host does not pass upgrade headers until you tick the box. The symptom is an app that loads perfectly and whose live updates never arrive: Home Assistant sitting at "connecting", Jellyfin sync play doing nothing. The nginx error log stays empty because nothing errored. Nothing in the UI hints at it.
Which one for your situation#
| Situation | Use | Why |
|---|---|---|
| You have never written an nginx server block and do not want to | Nginx Proxy Manager | The form is the product, and it works |
| Your configuration should live in git | Caddy | One readable file, diffable, reviewable |
| More than one person needs to add hosts, without SSH | Nginx Proxy Manager | Local accounts with per-user permissions |
| Wildcard certificate via DNS-01, no custom images | Nginx Proxy Manager | Bundled certbot DNS plugins in a dropdown |
| 1 GB VPS where 75 MB matters | Caddy | 25 MB idle against 100 MB |
| Forwarding raw TCP or UDP ports | Nginx Proxy Manager | Streams tab; Caddy needs a layer 4 plugin build |
| You plan to add Authelia or authentik later | Caddy | forward_auth is one directive; NPM has no forward auth |
| 32-bit Raspberry Pi | Caddy | NPM armv7 images stopped at 2.13.7 |
| Containers appear and disappear weekly | Neither | See Caddy vs Traefik |
| You want to rebuild the whole host from a repo | Caddy | Four lines reproduce it; a SQLite database does not |
What to do next#
If you are starting from nothing, write the Caddyfile first and see whether it bothers you. It is three lines, and if the answer is that you hate it, NPM is fifteen minutes away and you have lost nothing. Generate a first config with Reverse proxy generator, read Reverse proxy and TLS for the DNS and certificate decisions underneath either choice, and if you go with NPM, set up the two-directory backup on day one rather than after the first inconsistent restore. The rest of the category is at Networking.
Questions#
Is Nginx Proxy Manager still maintained in 2026?
Yes, but read the shape of it. 2.15.1 shipped in June 2026, and recent releases have been Debian base image, OpenResty and certbot updates plus CVE patches rather than new capability. There are roughly 890 open issues and it is effectively one maintainer. The v3 rewrite (Go backend, React frontend, acme.sh instead of certbot, SQLite only) has had an open status issue since June 2021 and has not landed. That is not abandonment. It does mean choosing NPM for its current feature set, not a future one.
What happens if NPM's database gets confused?
Proxy hosts reference certificates by numeric ID, so the failure is usually a mismatch between /data/database.sqlite and /etc/letsencrypt. Restore one without the other and you get hosts pointing at certificates that do not exist, and nginx refuses to start, which takes every site down at once rather than the one you broke. Back both directories up together and restore them together. /data/nginx is regenerated from the database on start, so it is not worth keeping.
Why does my app load through NPM but its live updates never arrive?
Websockets are a per-host toggle and it is off by default, as is HTTP/2 on the SSL tab. The request is proxied as plain HTTP, so the page loads and nothing errors, the nginx log stays empty, and only the socket-based features fail. Home Assistant, Jellyfin sync play and anything with live status hit this. Tick Websockets Support on the host and save. Caddy's reverse_proxy passes upgrade headers with no configuration.
Can Caddy do TCP or UDP forwarding like NPM's streams?
Not in the stock build. NPM has a Streams tab that forwards raw TCP and UDP ports, which is useful for game servers and SMTP relays, and Caddy needs a third-party layer 4 module and therefore a custom build to match it. If stream forwarding is a requirement, that is a genuine point for NPM, or an argument for leaving those ports to the firewall entirely.
Which one is easier to move to a new machine?
Caddy, by a wide margin. Copy the Caddyfile and the /data volume, start the container, and certificates are already there; even without the volume it re-issues them on first request. Moving NPM means stopping the container, copying both /data and /etc/letsencrypt with permissions intact, and starting the new one before DNS moves. If migration and reproducibility matter to you, that difference is the whole comparison.
Does NPM support single sign-on?
No. It has local accounts with per-user permissions and an audit log, plus HTTP basic auth access lists you can attach to a host. There is no OIDC, no LDAP and no forward auth, and none is planned in the v2 line. If you want Authelia or authentik in front of your apps, you need Caddy's forward_auth directive or a Traefik ForwardAuth middleware instead.
Sources#
- Nginx Proxy Manager setup documentation, ports, volumes and databases
- Nginx Proxy Manager guide and feature list
- Nginx Proxy Manager releases
- Nginx Proxy Manager v3 project status issue
- Nginx Proxy Manager repository, licence and activity
- Caddy documentation, automatic HTTPS
- Caddy documentation, conventions and data directory paths
- Caddy documentation, build from source and plugins
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.