Last year I moved my homelab from a single Docker Compose stack to a K3s cluster. It took a weekend, broke half my services, and taught me more about container security than any course I’ve taken. Here’s what I learned about when each tool actually makes sense—and the security traps in both.
The real question: how big is your homelab?
I ran Docker Compose for two years. Password manager, Jellyfin, Gitea, a reverse proxy, some monitoring. Maybe 12 containers. It worked fine. The YAML was readable, docker compose up -d got everything running in seconds, and I could debug problems by reading one file.
Then I hit ~25 containers across three machines. Compose started showing cracks—no built-in way to schedule across nodes, no health-based restarts that actually worked reliably, and secrets management was basically “put it in an .env file and hope nobody reads it.”
That’s when I looked at Kubernetes seriously. Not because it’s trendy, but because I needed workload isolation, proper RBAC, and network policies that Docker’s bridge networking couldn’t give me.
Docker Compose security: what most people miss
Compose is great for getting started, but it has security defaults that will bite you. The biggest one: containers run as root by default. Most people never change this.
Here’s the minimum I run on every Compose service now:
version: '3.8'
services:
app:
image: my-app:latest
user: "1000:1000"
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
networks:
- isolated
logging:
driver: json-file
options:
max-size: "10m"
networks:
isolated:
driver: bridge
The key additions most tutorials skip: read_only: true prevents containers from writing to their filesystem (mount specific writable paths if needed), no-new-privileges blocks privilege escalation, and cap_drop: ALL removes Linux capabilities you almost certainly don’t need.
Other things I do with Compose that aren’t optional anymore:
- Network segmentation. Separate Docker networks for databases, frontend services, and monitoring. My Postgres container can’t talk to Traefik directly—it goes through the app layer only.
- Image scanning. I run Trivy on every image before deploying. One
trivy image my-app:latestcatches CVEs that would otherwise sit there for months. - TLS everywhere. Even internal services get certificates via Let’s Encrypt and Traefik’s ACME resolver.
Scan your images before they run—it takes 10 seconds and catches the obvious stuff:
# Quick scan
trivy image my-app:latest
# Fail CI if HIGH/CRITICAL vulns found
trivy image --exit-code 1 --severity HIGH,CRITICAL my-app:latest
Kubernetes: when the complexity pays off
I use K3s specifically because full Kubernetes is absurd for a homelab. K3s strips out the cloud-provider bloat and runs the control plane in a single binary. My cluster runs on a TrueNAS box with 32GB RAM—plenty for ~40 pods.
The security features that actually matter for homelabs:
RBAC — I can give my partner read-only access to monitoring dashboards without exposing cluster admin. Here’s a minimal read-only role:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: monitoring
name: dashboard-viewer
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: viewer-binding
namespace: monitoring
subjects:
- kind: User
name: reader
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dashboard-viewer
apiGroup: rbac.authorization.k8s.io
Network policies — This is the killer feature. In Compose, network isolation is coarse (whole networks). In Kubernetes, I can say “this pod can only talk to that pod on port 5432, nothing else.” If a container gets compromised, lateral movement is blocked.
Namespaces — I run separate namespaces for media, security tools, monitoring, and databases. Each namespace has its own resource quotas and network policies. A runaway Jellyfin transcode can’t starve my password manager.
The tradeoff is real though. I spent a full day debugging a network policy that was silently dropping traffic between my app and its database. The YAML looked right. Turned out I had a label mismatch—app: postgres vs app: postgresql. Kubernetes won’t warn you about this. It just drops packets.
Networking: the part everyone gets wrong
Whether you’re on Compose or Kubernetes, your reverse proxy config matters more than most security settings. I use Traefik for both setups. Here’s my Compose config for automatic TLS:
version: '3.8'
services:
traefik:
image: traefik:v3.0
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "[email protected]"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
volumes:
- "./letsencrypt:/letsencrypt"
ports:
- "80:80"
- "443:443"
Key detail: that HTTP-to-HTTPS redirect on the web entrypoint. Without it, you’ll have services accessible over plain HTTP and not realize it until someone sniffs your traffic.
For storage, encrypt volumes at rest. If you’re on ZFS (like my TrueNAS setup), native encryption handles this. For Docker volumes specifically:
# Create a volume backed by encrypted storage
docker volume create --driver local \
--opt type=none \
--opt o=bind \
--opt device=/mnt/encrypted/app-data \
my_secure_volume
My recommendation: start Compose, graduate to K3s
If you have fewer than 15 containers on one machine, stick with Docker Compose. Apply the security hardening above, scan your images, segment your networks. You’ll be fine.
Once you hit multiple nodes, need proper secrets management (not .env files), or want network-policy-level isolation, move to K3s. Not full Kubernetes—K3s. The learning curve is steep for a week, then it clicks.
I’d also recommend adding Falco for runtime monitoring regardless of which tool you pick. It watches syscalls and alerts on suspicious behavior—like a container suddenly spawning a shell or reading /etc/shadow. Worth the 5 minutes to set up.
The tools I keep coming back to for this:
- Kubernetes in Action, 2nd Edition — best K8s book I’ve read. Goes deep on security chapters. (affiliate link)
- Hacking Kubernetes — threat-focused analysis of K8s attack surfaces. Changed how I think about cluster security. (affiliate link)
- GitOps and Kubernetes — if you want ArgoCD or Flux for your homelab deployments. (affiliate link)
Related posts you might find useful:
- Docker Container Security Best Practices for 2026
- Kubernetes Security Checklist for Production
- Home Network Segmentation with OPNsense
- Secure Remote Access for Your Homelab
📧 Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.

Leave a Reply