WEDC Team 6 min read

Hardening Your Self-Hosted Stack: 7 Practical Steps

Running services on your own hardware is liberating, but it shifts the security burden entirely onto you. These seven concrete steps dramatically reduce your attack surface without adding operational complexity.

The Security Reality of Self-Hosting

When you self-host, there is no vendor patching things for you at 2 AM. There is no WAF absorbing probes. The good news: most real-world compromises exploit basic hygiene failures, not novel zero-days. Fixing the basics puts you ahead of 90% of exposed services.

Here are seven actionable steps, ordered by impact-to-effort ratio.


Step 1: Never Expose Services Directly — Use a Web Gateway

Put every service behind a single Caddy or Nginx web gateway. The gateway handles:

  • TLS termination (auto-certificates via ACME)
  • HTTP security headers (HSTS, CSP, X-Frame-Options)
  • Rate limiting
  • Access control by source IP or authentication

Direct exposure of application ports (5432, 8080, 3000) to the internet is the single most common mistake self-hosters make.

# Caddy snippet — add to every internal service
app.example.com {
  header {
    Strict-Transport-Security "max-age=31536000; includeSubDomains"
    X-Content-Type-Options "nosniff"
    X-Frame-Options "DENY"
    Referrer-Policy "strict-origin-when-cross-origin"
  }
  forward_to localhost:8080
}

Step 2: SSH Key Authentication Only

Disable password-based SSH immediately after your first key login.

# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

Use Ed25519 keys — smaller and faster than RSA:

ssh-keygen -t ed25519 -C "homelab-key-2026"

Step 3: Automatic Security Updates

Enable unattended-upgrades on Debian/Ubuntu to auto-apply security patches:

apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

For container images, use Watchtower in monitor-only mode: it alerts you when upstream images have updates without auto-pulling (avoiding surprise breaking changes).


Step 4: Principle of Least Privilege for Containers

Never run application containers as root. Most official images support a non-root user:

services:
  app:
    image: myapp:latest
    user: "1000:1000"   # map to a non-root UID
    read_only: true      # make the container filesystem read-only
    tmpfs:
      - /tmp             # writable temp space only
    cap_drop:
      - ALL              # drop all Linux capabilities
    cap_add:
      - NET_BIND_SERVICE # add back only what you need

Step 5: Network Isolation with Docker Networks

By default, all Docker containers share a bridge network and can reach each other. Segment them:

networks:
  frontend:        # web gateway <-> web apps
  backend:         # web apps <-> databases only
  monitoring:      # prometheus + exporters

services: db: networks: - backend # db is NOT reachable from frontend web: networks: - frontend - backend


Step 6: Audit Open Ports Monthly

# What's listening?
ss -tlnp

# Cross-check with firewall rules ufw status verbose

# Scan yourself from outside (use a VPS or friend's machine) nmap -sV -p 1-65535 your.public.ip

Remove anything you don't recognize. If a service doesn't need to be reachable from the internet, add a ufw deny rule.


Step 7: Encrypted, Off-Site Backups

A compromised or failed server should cost you data only from the last backup window, not permanently.

# restic backup to Backblaze B2 (encrypted client-side)
restic -r b2:mybucket:homelab init
restic -r b2:mybucket:homelab backup /var/lib/docker/volumes /home
restic -r b2:mybucket:homelab snapshots   # verify

Schedule this daily via cron or a systemd timer. Test restoration quarterly — untested backups are just hopeful archives.


Summary Checklist

StepPriorityEffort Web gateway for all servicesCriticalLow SSH keys onlyCriticalMinimal Auto security updatesHighMinimal Non-root containersHighLow Docker network isolationMediumLow Monthly port auditMediumLow Encrypted off-site backupCriticalMedium

Self-hosting security is not about exotic tools — it is about consistent application of fundamentals. The WEDC member library provides Ansible roles that automate steps 1–5 across fleets of machines with a single command.

Enjoyed this article?

WEDC members get access to the full library of tutorials, downloadable utility applications, and monthly configuration bundles — plus new content every week.