Skip to main content

Runtime Performance Optimization

By default, a single container can consume all host CPU and memory, starving other containers. Setting resource limits prevents one misbehaving container from taking down your entire host.

Resource Limits

Memory Limits

# Limit container to 512 MB of RAM
docker run -d --memory=512m my-app:1.0.0

# Memory + swap (set equal to disable swap)
docker run -d --memory=512m --memory-swap=512m my-app:1.0.0

In Compose:

services:
api:
image: my-api:1.0.0
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
OOM Killer

If a container exceeds its memory limit, Docker kills it immediately (OOM). Check with:

docker inspect CONTAINER --format '{{.State.OOMKilled}}'

CPU Limits

# Limit to 1.5 CPU cores
docker run -d --cpus=1.5 my-app:1.0.0

# CPU shares (relative weight, default 1024)
docker run -d --cpu-shares=512 my-app:1.0.0

In Compose:

services:
api:
image: my-api:1.0.0
deploy:
resources:
limits:
cpus: "1.5"
reservations:
cpus: "0.5"
FlagBehaviorUse Case
--cpus=NHard limit to N coresPrevent CPU monopoly
--cpu-shares=NRelative weight (only under contention)Fair sharing
--memory=NHard limit, OOM kill if exceededPrevent memory monopoly
--memory-reservation=NSoft limit, best-effortPlanning, not enforcement

Monitoring Resources

Live Monitoring

# Live resource usage for all containers
docker stats

# Custom format
docker stats --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}'

# One-shot snapshot (no streaming)
docker stats --no-stream

Historical Data

# Check if container was OOM-killed
docker inspect CONTAINER --format '{{.State.OOMKilled}}'

# Check restart count (high = crash loop)
docker inspect CONTAINER --format '{{.RestartCount}}'

# Check current resource config
docker inspect CONTAINER --format '{{json .HostConfig.Memory}}'
docker inspect CONTAINER --format '{{json .HostConfig.NanoCpus}}'

Process Limits

Prevent fork bombs and runaway process spawning:

docker run -d --pids-limit=256 my-app:1.0.0

In Compose:

services:
api:
image: my-api:1.0.0
pids_limit: 256

Restart Policies

Configure how Docker handles container crashes:

PolicyBehaviorUse Case
noNever restart (default)One-off tasks
on-failureRestart only on non-zero exitMost services
on-failure:5Restart on failure, max 5 attemptsPrevent infinite loops
unless-stoppedAlways restart unless explicitly stoppedProduction services
alwaysAlways restart, even after rebootCritical infrastructure
services:
api:
image: my-api:1.0.0
restart: unless-stopped

Optimizing Container Startup

Use Health Checks to Signal Readiness

services:
api:
image: my-api:1.0.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s

The start_period gives the container time to initialize before health checks begin failing.

Graceful Shutdown

Ensure your application handles SIGTERM properly. Docker sends SIGTERM on stop, then SIGKILL after the timeout (default 10 seconds):

# Give the app 30 seconds to shut down gracefully
docker stop -t 30 my-app

Key Takeaways

  • Set memory and CPU limits to prevent any single container from monopolizing host resources.
  • Monitor with docker stats and check OOMKilled after unexplained restarts.
  • Use --pids-limit to prevent fork bombs.
  • Set restart: unless-stopped for production services.
  • Use start_period in health checks to avoid false failures during startup.

What's Next