Docker is great for tinkering, slap containers together and off you go. Then you add Vaultwarden or JumpServer and start worrying about security. Don’t panic: small, sensible tweaks close the easy doors.
1) Don’t run everything as root
Most containers run as root by default. Use an unprivileged UID/GID and chown your data directories first.
sudo chown -R 1000:1000 /path/to/data
Then, in your Docker Compose file:
user: "1000:1000"
2) Lock down the filesystem
Set the container’s root filesystem to read-only and only allow writes to specific folders.
read_only: true volumes: - ./data:/data:rw - ./config.yml:/etc/app/config.yml:ro
3) Drop capabilities and forbid privilege escalation
By default, containers inherit a set of Linux capabilities that they usually don’t need. Drop them all and prevent new ones from being added.
cap_drop: - ALL security_opt: - no-new-privileges:true
4) Keep ports local
Only expose ports where necessary. Bind them to a specific interface, not all interfaces.
ports: - "192.168.0.22:8080:80"
5) Store secrets securely
Never hard-code credentials in your Compose file. Use an .env file.
env_file:
- .env
environment:
- DB_PASSWORD=${DB_PASSWORD}6) Limit container resources
Prevent any single container from consuming all your RAM or CPU.
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M7) Avoid mounting the Docker socket
Mounting /var/run/docker.sock is like handing over root access to the container. Avoid it unless you fully trust the container.
Example secure Compose snippet:
services:
myapp:
image: ghcr.io/example/foss-app:latest
container_name: my_safe_app
user: "1000:1000"
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
volumes:
- ./data:/data:rw
- ./config.yml:/etc/app/config.yml:ro
ports:
- "192.168.0.22:8080:8080"
deploy:
resources:
limits:
memory: 512MHardening isn’t about perfection — it’s about closing the easy doors. Apply these changes gradually. You’ll feel a little safer and your homelab will survive the occasional midnight upgrade without combusting.
