I've tried the procedures from:

but they don't work on Ubuntu 22.04, presumably because of the update to cgroups v2:

sudo cgcreate -a $USER:$USER -g memory:myGroup -t $USER:$USER
sudo cgset -r memory.max=500M myGroup
sudo cgset -r memory.swap.max=0 myGroup
cgexec -g memory:myGroup id

fails with:

cgroup change of group failed

It works if I run with sudo

sudo cgexec -g memory:myGroup id

but then the command runs as root, and I want it to run as the current user instead.

4 Answers 4


I read the Linux Kernel documentation and find the following (emphasis mine):


A read-write new-line separated values file which exists on all cgroups.
A PID can be written to migrate the process associated with the PID to the cgroup. The writer should match all of the following conditions.

  • It must have write access to the “cgroup.procs” file.
  • It must have write access to the “cgroup.procs” file of the common ancestor of the source and destination cgroups.


In this scenario, the common ancestor is /. So I make cgroup.procs of the root group writable:

sudo chmod o+w /sys/fs/cgroup/cgroup.procs

And now I can use cgexec as any non-root user. Not knowing if there is any security implication though.


To resolve it you need to boot your host system into CGroupV1 mode by modifying your kernel’s boot arguments to include: systemd.unified_cgroup_hierarchy=false

sudo nano /etc/default/grub

GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=false"

sudo update-grub
sudo reboot

I used to run a few resource-intensive tools with cgexec in cgroups v1. I'm not so sure this is supported in cgroups v2; at least I couldn't get it to work.

After too much time spent looking into it this is how I managed to get things going for me in Ubuntu 22.04 + cgroups v2.

1. Create a cgroup europe and (in my case) tweak permissions

$ cd /sys/fs/cgroup/
$ sudo mkdir europe create

$ ls -ld europe
drwxr-x--- 2 root root 0 out 28 22:50 europe
$ sudo chmod a+rx europe
$ ls -ld europe
drwxr-xr-x 2 root root 0 out 28 22:50 europe

Without changing permissions two things happen to me: can't cat stuff inside group without sudo, and systemd-cgtop --depth 1 returns Failed to refresh: Permission denied.

2. Limit CPU to 3 cores and RAM to 16G

I had multiple controllers available (cgroup.controllers), but the only ones being passed down the subtree were memory and pids so I added the missing cpu controller.

$ cat cgroup.controllers                       // all available
cpuset cpu io memory hugetlb pids rdma misc
$ cat cgroup.subtree_control                   // for subgroups
memory pids
$ sudo bash -c 'echo '+cpu' > cgroup.subtree_control'
$ cat cgroup.subtree_control                   // now includes CPU
cpu memory pids

$ cat europe/cgroup.controllers                // can confirm in subgrp
cpu memory pids

Now limit CPU and RAM:

$ cat europe/memory.max
$ cat europe/cpu.max
max 100000

$ echo '16G' | sudo tee europe/memory.max
$ echo '300000 100000' | sudo tee europe/cpu.max
300000 100000

$ cat europe/memory.max
$ cat europe/cpu.max
300000 100000

3. Run some stuff

We can add PIDs to to europe/cgroup.procs that will then be executing in group europe.

Instead of starting a process and then adding its PID what I do is add the PID of a terminal window. Then everything run in that shell will be executed in the group.

Open another terminal:

$ cat /sys/fs/cgroup/europe/cgroup.procs

$ echo $$ | sudo tee /sys/fs/cgroup/europe/cgroup.procs

$ cat europe/cgroup.procs
$ cat europe/cgroup.procs
$ cat europe/cgroup.procs

The first PID is the one referring to the terminal (matches echo $$ in bash) and the second PID is the command cat that's why everytime I ran the command the second PID was different.

4. Remove group when not needed

$ cat /sys/fs/cgroup/europe/cgroup.procs
$ sudo rmdir /sys/fs/cgroup/europe

If there are still processes in europe/cgroup.procs we get an error. In this example close the terminal that was added to the group in the previous step.


You can combine cgexec with su like this:

sudo cgexec -g memory:myGroup -- su -c 'exec id' $USER

or simply with -s:

sudo cgexec -g memory:myGroup -- su -s /usr/bin/id $USER

But the latter will rewrite $SHELL variable as the program itself.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .