Security & Best Practices
#Security & Best Practices
After this lesson you'll know:
- why running as root in a container is dangerous
- how to run containers with minimal privileges
- what secrets are and how to use them safely
- how resource limits prevent DoS from within a container
#Don't run as root
Containers run as root by default. An attacker who breaks into the container has root privileges on the host.
# Better: Create your own user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Everything now runs as appuser
#Reduce capabilities
Linux capabilities are permissions for specific system calls. Docker gives containers many by default. You don't need most — and every open capability is an attack vector.
# Only allow the capability you actually need: binding to ports under 1024
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
Even with --cap-drop ALL most containers run fine. If something breaks (e.g. ping), add specific capabilities instead of allowing everything.
#No secrets in the image!
Once baked into the image, anyone with registry access can extract the secret — docker history shows it in plain text.
# WRONG — API key is baked into the image forever
ENV API_KEY=sk-12345
# RIGHT — pass at runtime
docker run -e API_KEY=sk-12345 my-app
# Or with Docker Secrets (recommended for Swarm)
docker run --secret id=api-key my-app
#Read-Only Root Filesystem
A read-only filesystem prevents attackers from persisting malware. Temporary data goes to RAM (--tmpfs).
docker run --read-only --tmpfs /tmp nginx
# The container can't write to the filesystem (except /tmp)
#Resource Limits as a Security Measure
A bug or attack that eats 100% CPU — without limits, every other container suffers. This is a denial of service from within.
# Limit memory (most important DoS protection)
docker run --memory=256m nginx
# Limit CPU
docker run --cpus=1.5 nginx
# Auto-restart on crashes
docker run --restart=unless-stopped nginx
# All together — secure setup
docker run -d \
--name web \
--memory=512m \
--cpus=1 \
--restart=unless-stopped \
nginx
#✋ Try it out
- Add USER to your Dockerfile and rebuild. Start the container and check with
whoami - Start a container with
--cap-drop ALLand tryping google.com— why doesn't it work? - Start a container with
--read-only --tmpfs /tmpand try writing a file to / vs /tmp docker run --memory=64m alpine sh -c "dd if=/dev/zero of=/dev/null bs=1M"— start with 64 MB limit. Check withdocker inspect --format='{{.State.OOMKilled}}'if the container was killed
#📌 Summary
- Always set USER in Dockerfile — root in a container is a security risk
- Minimize capabilities with --cap-drop ALL
- Secrets don't belong in images, use environment variables or --secret
- Resource limits (--memory, --cpus) prevent DoS from individual containers