I want to configure cgroups V2 resource limitation on a Docker-Compose container. How do I do this?
1 Answer
INTRO:
I eat my own dog food and have been using this solution for a few years on publicly-facing docker-compose containers. So it's sufficiently battle-tested and the test evidence at the end proves that it does.
This 2-step solution takes only 2 minutes to config Cgroup V2 on your Docker-Compose containers
REQUIREMENTS:
Just (2) requirements:
- Docker Host OS: A SystemD host which supports Cgroup V2. I use Ubuntu 24.04.1 LTS with this solution
- docker-compose version: A docker-compose file formatted using v3.9. But any version which supports the
croup_parentdirective should work
Validated on earlier versions than those above, but should be portable without modification on any SystemD Linux distro supporting Cgroup v2
Note that config is container in-specific!!!: Although I run the solution on Alpine Linux & Ubuntu AARCH64 containers, it could be on other arch Docker containers- there is NO config or dependencies at the container level.
STEP 1: HOST Config:
Create a file called "docker.slice" in the following path:
/etc/systemd/system/docker.slice
Below is a general-purpose config; tweak to sensible values for your specific use-case:
[Unit]
Description=Slice that limits docker resources
Before=slices.target
[Slice]
MemoryAccounting=true
CPUAccounting=true
TasksMax=30%
AllowedCPUs=1-3
# NOTE: If slice restricted to 3 of 4 cores, max avail. CPUQuota= 300%, NOT 400%
CPUQuota=300%
# CPUWeight value range: 1-10000
CPUWeight=8000
MemoryLow=1G
MemoryHigh=2G
MemoryMax=2G
# IOWeight value range: 1-10000
IOWeight=5000
Read your new changes:
systemctl daemon-reload
STEP 2 DOCKER-COMPOSE Config:
In the docker-compose file, in the cgroup_parent directive, specify the name of the systemd file created in Step 1: docker.slice
cgroup_parent: docker.slice
Jason formatting: I'm inserting (4) spaces before adding the cgroup_parent directive on the 5th space in my own docker-compose file.
Restart the docker-compose containers.
Now when you execute:
systemctl status docker.slice
You'll see your container running under Cgroups resource control. YourCgroups V2 config for your docker-compose container is finished. That's it!
(OPTIONAL STEP) CONFIG- CONTAINER:
Rebuild container with stress-ng to validate resource limitation is working. Delete stress-ng from your container build process after testing completed.
But if you're happy the solution works as proposed after reviewing my test evidence below, you can just skip this step.
TESTING:
Stress Testing:
We'll use stress-ng inside the container to prove our Cgroup limits are being enforced. There are a gazillion stress-ng commands we can use to test various things, but I'll just offer a single test as thin't meant to be a 'stress-ng` HowTo.
Enter the container :
docker exec -it <Container ID from "docker ps"> sh
Or if bash is available inside your container (need to expressly add it in Alpine Linux):
docker exec -it <Container ID from "docker ps"> bash
Once inside the container we're testing for Cgroup resource limitation, execute the stress test:
stress-ng --cpu 0 --iomix 4 --vm 16 --vm-bytes 4G --timeout 180s --metrics
Note that we set our resource limitation for memory to a value of a max of 2G in docker.slice. Thus, we'll cane the memory by using a value of 4G in the stress-ng command above. If all is configured correctly, we should never see anything above 2G.
Monitoring Resource Limitation:
In a new terminal window/tab, execute the following monitoring commands to validate limitations are being enforced correctly.
NOTE: Below commands are to be executed from THE HOST - NOT inside the container!
systemctl status docker.slice
systemd-cgtop
Some screen grabs are offered below to evidence our Cgroups V2 config is working correctly. Please note however that the output after all the stress-ng commands is truncated for illustrative clarity.
systemctl status docker.slice:
systemd-cgtop:
dmesg |grep -i oom Output:
Remark oom-kill:constraint=CONSTRAINT_MEMCG. Cgroups is clobbering stress-ng when it tries to pass our memory threshold:

CONCLUSION:
It took me a while to get Cgroups working in docker-compose due to the lack of any comprehensive HowTo. Since I was porting OpenLDAP to AARCH64, I figured it was worth taking a moment to document the process I follow to save others from pulling their hair out of their heads getting it working. ;-)
Anyhoo, HTH other enterprise folks who were searching in vain for some config help-
-Terrence Houlahan

