Tech Digest

Category

Networking, DNS and remote access

Every service you add is another certificate, another port and another login form. One proxy in front of all of them collapses that into one thing to get right.

8 tools profiledMedian idle memory 90 MBLast reviewed

Every service you add brings a port, a certificate problem and a login form. Handled one at a time, that is a growing pile of things to keep patched and a growing number of ways to be wrong. Handled once, at the edge, it is a single file you can read end to end.

That is the whole shape of this category. One proxy terminating TLS for everything, one way in from outside, and a resolver that makes internal names work. Get those three right and most of the rest of self-hosting stops being a networking problem.

The default: Caddy in front, WireGuard for the way in#

Caddy terminates TLS for every service on the box. It idles around 25 MB, scores 1 on ops load, and a working site with automatic HTTPS, HTTP redirect, OCSP stapling and unattended renewal is three lines:

caddyfile
jellyfin.example.com {
	reverse_proxy jellyfin:8096
}

For internal names it mints its own local CA, so https://nas.internal stops throwing warnings. The cost is modules: the stock binary and official image ship no DNS provider plugins, so wildcard certificates mean building with xcaddy and rebuilding on every upgrade.

For remote access, wg-easy gives you a WireGuard server with a QR code per device. Treat v15 as a different product from v14: there is no migration path at all, the latest tag still points at v14, and the licence changed to AGPL-3.0-only.

Two situations where those defaults are wrong.

Your containers change most weeks and you redeploy stacks constantly. Then Traefik, which builds its routing table from Docker labels so a new service routes itself. Budget 60 MB idle and ops 3, and know that pasted v2 snippets fail silently on v3: matcher values need backticks, Headers became Header, IPWhiteList became IPAllowList, and a stale label produces a 404 from whichever router did match rather than an error.

Both ends are behind CGNAT. A WireGuard server needs a reachable endpoint. Headscale instead runs the coordination server for official Tailscale clients, so you get NAT traversal and MagicDNS without your device inventory living at a vendor. It is pre-1.0, you must upgrade one minor at a time, and v0.29.3 documents a Tailscale client floor of v1.80.0.

DNS blocking is not security#

Pi-hole and AdGuard Home are worth running. They cut ads, kill a lot of telemetry, and make an entire household's browsing quieter for 60 to 100 MB. What they do not do is protect anything. They see only queries that arrive on port 53, and browser DoH, phone private-DNS profiles and TVs with hardcoded resolvers bypass them entirely. If you want them enforced, block outbound 53 and 853 to everything except your resolver at the firewall. That is a firewall control, not a Pi-hole feature.

Pick AdGuard Home when you want DoH, DoT and DoQ built in without a cloudflared sidecar. Pick Pi-hole for group-based per-device rules and the larger list ecosystem. Pick Technitium DNS Server only when you actually own a domain and need authoritative zones, DNSSEC signing or split horizon, and know that creating a primary zone for example.com makes that server authoritative for every name under it, so any public record you did not copy in returns NXDOMAIN, mail included.

The mistakes people actually make#

  • Exposing an app's own login. Nginx Proxy Manager's port 81 ships admin@example.com / changeme with no rate limiting. Technitium's console is admin / admin on 5380. These are documented publicly and scanned for within hours.
  • Bind-mounting acme.json that does not exist yet. Docker creates a directory with that name, Traefik cannot persist certificates, and it re-requests them every restart until Let's Encrypt rate limits you. Run touch acme.json && chmod 600 acme.json first.
  • Forgetting websockets. Nginx Proxy Manager has websocket support off per host by default. The app loads, live updates never arrive, and the nginx error log stays empty.
  • Editing config that is read once. Technitium's DNS_SERVER_* variables apply only when no config file exists. AdGuard Home overwrites its own YAML while running. Pi-hole v6 renamed every Docker variable and ignores the old ones silently.
  • Leaving the Docker socket writable. Traefik with socket access is a compromise path to host root. Mount it :ro at minimum, and prefer a socket proxy that whitelists only the container and event endpoints.

Reverse proxy and TLS is the build instructions for the default above, and Reverse proxy generator writes the config for you. Remote access without port forwarding covers the VPN choice properly, DNS for self-hosters covers subdomains and split horizon, and A security baseline for a home server is where the firewall rules that make DNS filtering stick actually live. For head-to-head calls: Caddy vs Traefik, Caddy vs NPM, Pi-hole vs AdGuard Home and Headscale vs wg-easy. Check Port conflict checker before you add anything that wants 53, 80 or 443.

Every networking tool we profile#

Sorted by operational load, lightest commitment first. Every row links to a full profile with the resource numbers, the upgrade traps and the restore path.

ToolOps loadIdle RAMLicenceIdentityBackup shape
Caddy1, Set and forget25 MBApache-2.0Not applicableFile copy
AdGuard Home2, Light60 MBGPL-3.0Local accounts onlyEmbedded key-value store
Nginx Proxy Manager2, Light100 MBMITLocal accounts onlySQLite backup
Pi-hole2, Light100 MBEUPL-1.2Local accounts onlySQLite backup
wg-easy2, Light90 MBAGPL-3.0-onlyNative OIDCSQLite backup
Headscale3, Moderate40 MBBSD-3-ClauseNative OIDCSQLite backup
Technitium DNS Server3, Moderate130 MBGPL-3.0Native OIDCFile copy
Traefik3, Moderate60 MBMITAuth proxy onlyFile copy

Profiles#

Head to head in this category#

Guides for this category#

Questions#

Do I need to open ports on my router?

No, and usually you should not. A WireGuard tunnel or a mesh VPN gets you to your services from anywhere without any inbound port, which removes the entire class of attacks that start with a scanner finding your IP. Use wg-easy if you have a public IP and one admin handing out configs, or Headscale if devices sit behind CGNAT on both ends and need NAT traversal. Open 80 and 443 only for services you genuinely intend strangers to reach, and put a reverse proxy plus an auth gate in front of those.

Is Pi-hole a security tool?

No. Pi-hole and AdGuard Home only see queries that reach them on port 53. Firefox's DNS-over-HTTPS, Chrome's secure DNS, iOS and Android private DNS profiles, and smart TVs with hardcoded resolvers walk straight past, and the dashboard just shows a suspiciously quiet device. They block ads and telemetry domains, which is worth doing. They do not stop an exploit, they do not authenticate anyone, and a device that already has malware will resolve whatever it likes over 443. Treat DNS filtering as hygiene, not as a control.

Which reverse proxy should I run?

Caddy, unless your containers change weekly. Caddy idles near 25 MB, scores 1 on ops load, and a working HTTPS site is three lines with automatic certificate renewal. Traefik idles near 60 MB and scores 3, and earns that when routes should appear from Docker labels without you editing anything. Nginx Proxy Manager is the honest answer if you want a web form instead of a text file, at the cost of a project that is effectively one maintainer with a v3 rewrite open since 2021.

Why does my wildcard certificate fail with Caddy?

Because wildcards need the DNS-01 challenge and the official Caddy binary and Docker image ship zero DNS provider modules. You have to rebuild with xcaddy build --with github.com/caddy-dns/cloudflare or use the caddy:builder image in a two-stage Dockerfile, and rebuild on every Caddy upgrade. caddy add-package exists but is experimental and swaps the binary on disk, which a container recreate throws away. Also mount a persistent /data volume before you point real DNS at it, or repeated redeploys will get you rate limited by Let's Encrypt.

Can I run two reverse proxies for redundancy?

Not with Traefik and Let's Encrypt. The documentation says plainly that multiple instances cannot share the file-based ACME store, because nothing coordinates which instance receives a given challenge. Two replicas on a shared volume clobber each other's acme.json. The documented answers are cert-manager or Traefik's commercial distributed store. For a home network, the more useful redundancy is a second DNS resolver, since name resolution failing takes down everything at once while a proxy restart takes seconds.

Should I put my apps' own login pages on the internet?

No. Application logins are the weakest link you own: Nginx Proxy Manager ships admin@example.com / changeme on port 81 with no rate limiting, Technitium defaults to admin / admin, Calibre-Web to admin / admin123. Those defaults are published, and scanners find new hosts within hours. Put a VPN or a forward-auth gate in front, publish only the services that must be public, and change every default credential during the first session rather than after DNS is pointed at the box.

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.