The appeal of self-hosting at home is real: lower costs, full control, no vendor lock-in. But the moment you expose a service to the public internet, you’ve opened a door — and what matters is making sure only the right people can walk through it.
This isn’t a guide about making your setup impenetrable. It’s about the practical steps that cover the vast majority of real-world risk, without turning your homelab into a full-time security job.
Start with the Right Mental Model
Your home server is not a cloud server. The threat model is different.
On a cloud VPS, the provider handles physical security, network isolation, and a lot of the underlying infrastructure. At home, you’re running services on a machine that shares a network with your phone, your laptop, your TV, and probably your neighbors’ Wi-Fi signal.
This means two things matter above all else:
- What you expose to the internet — every public service is an attack surface
- What can reach your internal network — if something gets in, how far can it go
Keep these two questions in mind as you build your stack.
Your IP is the Front Door — Keep It Behind Cloudflare
The first practical step is making sure your real home IP address is never visible in public DNS records.
If you route your domains through Cloudflare and enable proxying (the orange cloud icon), your DNS records will resolve to Cloudflare’s IP addresses, not yours. Attackers scanning the internet have no direct path to your machine — all traffic goes through Cloudflare first.
This alone eliminates a significant category of risk: opportunistic port scans, direct volumetric attacks, and anyone trying to enumerate what’s running at your IP.
A few things to keep in mind:
- Enable the proxy for all public-facing records
- Use Cloudflare’s firewall rules to block traffic from regions you have no reason to serve
- The free tier includes basic DDoS protection and rate limiting — use them
The Dynamic IP Problem — And Why It Matters for Security Too
If you’re hosting at home, your ISP almost certainly assigns you a dynamic public IP. It changes without warning, and when it does, your DNS records become stale — your services become unreachable, or worse, they start pointing at someone else’s IP entirely.
The solution is a DDNS (Dynamic DNS) tool that monitors your public IP and automatically updates your DNS records when it changes.
There are shell scripts that do this, but a more robust approach is a dedicated tool with visibility: logs, a dashboard, and support for multiple DNS providers. DriftDNS handles this — it runs as a Docker container alongside your services and keeps your DNS records accurate without manual intervention.
From a security standpoint, stale DNS records aren’t just an availability problem. If your IP changes and the old IP gets reassigned to someone else, traffic intended for your services might reach an unintended destination. Keeping DNS current is part of running a clean setup.
Only Expose What Needs to Be Public
This sounds obvious, but it’s where most homelab setups accumulate unnecessary risk over time.
As you add services, it’s tempting to make everything accessible from the internet — convenient for you, but each public service is one more thing that needs to be kept patched and monitored.
A useful mental framework: put services in one of three categories.
Fully public — services you actively want accessible from anywhere (a personal blog, a public API, a portfolio site). These go behind your reverse proxy with HTTPS and Cloudflare.
Authenticated public — services you want to reach remotely, but only you (dashboards, file sync, private tools). These go behind your reverse proxy and behind an additional authentication layer.
Internal only — services that have no reason to be on the internet at all (database UIs, internal monitoring, development environments). These should never be exposed, period.
If you’re using Docker Compose, this last category is straightforward: don’t publish the port. A container with no published port is completely unreachable from outside the host.
Add an Authentication Layer with Authelia or Similar
For services in the “authenticated public” category, putting them behind a login form isn’t enough on its own — especially if the service has its own built-in authentication that you can’t fully trust.
A better approach is to add a forward authentication middleware at the reverse proxy level. Authelia is the most commonly used option in homelab setups: it integrates with Nginx Proxy Manager and adds a login screen in front of any service you choose, regardless of whether that service has its own auth.
This means even if a service has a vulnerability in its authentication logic, attackers still have to get past Authelia first. It also gives you a single place to manage access — one password, optionally with TOTP two-factor authentication.
HTTPS Everywhere, Automatically
Every service that’s accessible over the network should use HTTPS. Without it, credentials and session tokens are transmitted in plaintext — on your local network too, not just on the internet.
Nginx Proxy Manager handles this cleanly: it integrates with Let’s Encrypt and automatically issues and renews certificates for any domain you configure. You don’t need to think about certificate expiry or renewal scripts.
A few things worth configuring explicitly:
- Force HTTPS redirects — any HTTP request should automatically redirect to HTTPS
- Set HSTS headers — tells browsers to never connect to your domain over plain HTTP
- Use a wildcard certificate if you have many subdomains, to avoid hitting Let’s Encrypt rate limits
Keep Things Updated
This is unglamorous but probably the most impactful thing you can do.
The majority of successful attacks against self-hosted services exploit known vulnerabilities in outdated software. Not zero-days — vulnerabilities with published CVEs and available patches, where the fix exists but wasn’t applied.
For a Docker-based homelab, the update workflow is simple:
docker compose pull
docker compose up -d
Running this regularly is enough to stay ahead of most public vulnerabilities. Some people automate this with tools like Watchtower, which can pull and restart containers automatically when new images are available. Whether you automate it or do it manually, the key is doing it consistently.
Credentials and Secrets
A few practical rules that are easy to follow and meaningfully reduce risk:
Use strong, unique passwords for every service. If you’re self-hosting anyway, run Vaultwarden (a self-hosted Bitwarden-compatible server) and use it to generate and store all credentials. No shared passwords across services.
Never put secrets in Docker Compose files that are version-controlled. Use a .env file alongside your Compose file for API keys, passwords, and tokens — and make sure .env is in your .gitignore.
Use DNS API tokens with minimum required permissions. If your DDNS tool or Cloudflare integration needs API access, create a scoped token that can only modify specific DNS zones. Not your full account API key.
Change default credentials immediately. Every service you install has default admin credentials. They’re public knowledge. Change them before the service is accessible from the network.
Backups
The honest truth about homelab security is that hardware fails, containers get misconfigured, and sometimes things break in ways that aren’t easily reversible.
At minimum, back up your Docker Compose files and configuration volumes to somewhere that isn’t the same machine. A simple rsync to an external drive or a cloud storage bucket is enough. The goal isn’t disaster recovery at enterprise scale — it’s making sure a hard drive failure doesn’t mean losing everything you’ve built.
For application data specifically (databases, file sync, document management), automate the backup. Manual backups don’t get done consistently.
A Working Checklist
To summarize the practical steps:
- Route all public domains through Cloudflare with proxying enabled
- Use a DDNS tool to keep DNS records current as your IP changes
- Run a reverse proxy (Nginx Proxy Manager) to handle routing and SSL termination
- Add forward authentication (Authelia) in front of any private services exposed to the internet
- Never publish ports for services that don’t need to be public
- Force HTTPS everywhere, configure HSTS
- Update container images regularly
- Store secrets in
.envfiles, not in Compose files - Use scoped API tokens with minimum required permissions
- Back up configuration and application data off-machine
None of these steps are particularly complex. Together, they cover most of the realistic risk that comes with running services at home.
Final Thought
Security in a homelab isn’t about achieving perfection — it’s about not being the easiest target. Most attacks are opportunistic. They’re scanning for exposed default credentials, unpatched vulnerabilities, and services that shouldn’t be public.
If you follow the steps above, you’re not in that category. And you can run a surprisingly capable stack at home without lying awake worrying about it.