Resource Limits & DoS Prevention
If you don’t limit your containers, a single bug (memory leak) or an attacker (fork bomb) can crash your entire server.
Control Groups (Cgroups) are the Linux kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O, network) of a collection of processes.
1. The Threat: Resource Exhaustion
- Memory Leaks: A container consumes all 32GB of RAM. The OS starts killing other processes (SSH, Database) to survive.
- CPU Starvation: A crypto-mining malware spins 100% CPU, making the API unresponsive.
- Fork Bomb: A script creates infinite processes, filling the process table.
2. Interactive: The OOM Killer Game
When RAM is full, the Linux Kernel’s Out of Memory (OOM) Killer wakes up. It calculates a score for every process and kills the “worst” offender to save the system.
3. Setting Limits
1. Docker CLI
Hard limits enforce a strict ceiling. If exceeded, the container is throttled (CPU) or killed (Memory).
# Limit to 0.5 CPU cores and 512MB RAM
docker run -d \
--cpus="0.5" \
--memory="512m" \
--memory-swap="512m" \
nginx
[!WARNING] Disable Swap: Always set
--memory-swapequal to--memoryto disable swap. Swapping to disk kills performance and bypasses memory limits.
2. Docker Compose
Version 3+ uses the deploy key (originally for Swarm, now supported in Compose V2).
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
4. Language Awareness
Old versions of Java and Go did not respect Cgroup limits. They saw the Host’s RAM (e.g., 64GB) instead of the Container’s limit (512MB), causing immediate OOM crashes.
Java (JVM)
Java 10+ is container-aware by default. For Java 8 (u191+), use:
java -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -jar app.jar
Go (Golang)
Go 1.19+ automatically sets GOMAXPROCS to match the CPU quota if automaxprocs is used.
import _ "go.uber.org/automaxprocs"
func main() {
// Automatically sets GOMAXPROCS to match Linux container CPU quota
}
5. Best Practices
- Always set limits: Never run a container without a memory limit.
- Leave headroom: If your app needs 200MB, set the limit to 256MB.
- Monitor OOM Kills: Check
docker inspectto see if a container was OOM killed.docker inspect -f '{{.State.OOMKilled}}' my-container