Learn to set memory, CPU, and I/O limits for Docker containers using both Docker runtime constraints and systemd service limits. Master cgroups v2 integration for bulletproof resource management in production environments.
Prerequisites
- Root or sudo access
- Docker installed and running
- systemd-based Linux distribution
- Basic familiarity with container concepts
What this solves
Without proper resource limits, a single container can consume all available system memory or CPU, causing performance degradation or system crashes. This tutorial shows you how to configure both Docker-native resource constraints and systemd service limits to ensure containers stay within their allocated resources, preventing resource starvation and improving system stability.
Understanding container resource management
Resource limits work at multiple layers in a containerized environment. Docker applies constraints through the container runtime, while systemd manages the Docker daemon itself. Modern Linux systems use cgroups v2 for unified resource control, providing better accounting and enforcement than the previous cgroups v1 implementation.
The key difference: Docker limits constrain individual containers, while systemd limits constrain the entire Docker service. You need both for comprehensive protection. A runaway container might respect Docker limits but still overwhelm the system if the Docker service itself has no limits.
Step-by-step configuration
Verify cgroups v2 support
Check that your system uses cgroups v2, which provides unified resource management and better container integration.
mount | grep cgroup
cat /proc/cgroupsYou should see cgroup2 mounted at /sys/fs/cgroup. If you see separate cgroups v1 mounts, you may need to enable cgroups v2.
Enable cgroups v2 if needed
For systems still using cgroups v1, enable v2 by updating the kernel boot parameters.
sudo nano /etc/default/grubGRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=1 cgroup_no_v1=all"sudo update-grub
sudo rebootConfigure Docker daemon resource limits
Set systemd limits for the Docker service to prevent the daemon from consuming excessive resources.
sudo systemctl edit docker[Service]
MemoryAccounting=true
MemoryLimit=8G
CPUAccounting=true
CPUQuota=400%
TasksAccounting=true
TasksMax=4096This limits Docker service to 8GB RAM, 4 CPU cores worth of processing time, and maximum 4096 tasks. Adjust values based on your system capacity.
Reload systemd and restart Docker
Apply the new service limits by reloading systemd configuration and restarting Docker.
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl status dockerConfigure Docker container memory limits
Set memory limits when running containers to prevent individual containers from consuming excessive RAM.
docker run -d --name webapp \
--memory="512m" \
--memory-swap="1g" \
--oom-kill-disable=false \
nginx:latestThis container gets 512MB RAM, 1GB total memory including swap, and will be killed if it exceeds limits rather than causing system OOM.
Configure CPU limits for containers
Control CPU usage with CPU shares, quotas, and core affinity to prevent CPU monopolization.
docker run -d --name database \
--cpus="1.5" \
--cpu-shares=1024 \
--cpuset-cpus="0-1" \
postgres:15This limits the container to 1.5 CPU cores maximum, gives it normal priority (1024 shares), and restricts it to CPU cores 0 and 1.
Set I/O limits for containers
Control disk I/O to prevent containers from saturating storage bandwidth and affecting other workloads.
docker run -d --name logserver \
--device-read-bps /dev/sda:50mb \
--device-write-bps /dev/sda:30mb \
--device-read-iops /dev/sda:1000 \
--device-write-iops /dev/sda:800 \
elasticsearch:8.11.0This container can read max 50MB/s, write max 30MB/s, with IOPS limits of 1000 read and 800 write operations per second.
Create Docker Compose with resource limits
Define resource constraints in Docker Compose files for consistent multi-container deployments.
version: '3.8'
services:
web:
image: nginx:latest
deploy:
resources:
limits:
memory: 512M
cpus: '1.0'
reservations:
memory: 256M
cpus: '0.5'
db:
image: postgres:15
deploy:
resources:
limits:
memory: 2G
cpus: '2.0'
reservations:
memory: 1G
cpus: '1.0'
environment:
POSTGRES_PASSWORD: secure_password_123Configure systemd container service limits
For containers managed by systemd services, add resource limits to the service unit files.
sudo nano /etc/systemd/system/my-container.service[Unit]
Description=Production Web Container
After=docker.service
Requires=docker.service
[Service]
Type=simple
ExecStart=/usr/bin/docker run --rm \
--name production-web \
--memory=1g \
--cpus=1.5 \
nginx:latest
ExecStop=/usr/bin/docker stop production-web
Restart=always
RestartSec=10
Systemd resource limits
MemoryAccounting=true
MemoryLimit=1.5G
CPUAccounting=true
CPUQuota=200%
TasksAccounting=true
TasksMax=512
[Install]
WantedBy=multi-user.targetEnable and start the container service
Activate the systemd service with dual-layer resource protection from both systemd and Docker.
sudo systemctl daemon-reload
sudo systemctl enable my-container.service
sudo systemctl start my-container.service
sudo systemctl status my-container.serviceMonitor resource usage
Use these commands to monitor how containers use system resources and verify limits are working.
# Monitor Docker container resource usage
docker stats
Check systemd service resource usage
systemctl status docker --no-pager -l
View cgroup resource accounting
sudo systemd-cgtop
Check specific container cgroup usage
sudo cat /sys/fs/cgroup/system.slice/docker-$(docker inspect --format='{{.Id}}' webapp).scope/memory.currentAdvanced cgroups v2 configuration
For fine-grained control, configure cgroups v2 parameters directly through systemd slice units.
Create custom container slice
Define a systemd slice with specific resource allocations for container workloads.
sudo nano /etc/systemd/system/container-workloads.slice[Unit]
Description=Container Workloads Slice
Before=slices.target
[Slice]
MemoryAccounting=true
MemoryLimit=6G
MemoryHigh=5G
CPUAccounting=true
CPUWeight=200
CPUQuota=300%
IOAccounting=true
IOWeight=200Assign containers to the custom slice
Run containers within the custom slice for coordinated resource management across multiple containers.
sudo systemctl start container-workloads.slice
docker run -d --name web1 \
--cgroup-parent=/system.slice/container-workloads.slice \
--memory=1g \
--cpus=1.0 \
nginx:latestProduction optimization strategies
These configurations help optimize resource utilization for production workloads while maintaining system stability.
Configure memory pressure handling
Set up memory pressure notifications to handle resource constraints gracefully before hitting hard limits.
docker run -d --name pressure-aware \
--memory=1g \
--memory-reservation=768m \
--kernel-memory=100m \
--oom-score-adj=500 \
nginx:latestMemory reservation creates a soft limit that triggers reclaim before the hard limit. Higher OOM score makes this container more likely to be killed in extreme memory pressure.
Implement CPU bandwidth control
Use CPU period and quota for precise CPU bandwidth allocation in multi-tenant environments.
docker run -d --name cpu-controlled \
--cpu-period=100000 \
--cpu-quota=150000 \
--cpu-shares=512 \
nginx:latestThis allows 1.5 CPU cores (150000/100000) with lower priority (512 shares vs default 1024) for background workloads.
Configure resource monitoring alerts
Set up monitoring to alert when containers approach resource limits, enabling proactive capacity management.
This approach works well with monitoring systems covered in Prometheus cgroup metrics monitoring for comprehensive resource oversight.
Verify your setup
# Check Docker service limits
sudo systemctl show docker.service | grep -E '(Memory|CPU|Tasks)'
Verify container resource usage
docker stats --no-stream
Check cgroups v2 is active
ls -la /sys/fs/cgroup/system.slice/ | grep docker
Monitor resource pressure
sudo systemd-cgtop --delay=2Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Container killed with exit code 137 | Memory limit exceeded (OOMKilled) | Check docker logs container_name and increase memory limit or optimize application |
| Docker service consuming too much RAM | No systemd service limits set | Configure MemoryLimit in systemd override file |
| Resource limits not enforced | cgroups v1 or missing accounting | Enable cgroups v2 and set MemoryAccounting=true |
| Container CPU throttling | CPU quota too restrictive | Monitor with docker stats and adjust --cpus parameter |
| I/O limits not working | Wrong device specified | Use lsblk to find correct device and update --device-*-bps settings |
Next steps
- Advanced cgroups v2 configuration for container isolation
- Monitor container resource usage with Prometheus alerts
- Enhance container security with AppArmor profiles
- Scale resource management with Kubernetes quotas