Skip to content

Instantly share code, notes, and snippets.

@hrishikeshrt
Last active November 17, 2023 17:27
Show Gist options
  • Save hrishikeshrt/dab05d42fc60144909cdbde2cc3f4d2c to your computer and use it in GitHub Desktop.
Save hrishikeshrt/dab05d42fc60144909cdbde2cc3f4d2c to your computer and use it in GitHub Desktop.
cgroups - Limit memory usage of firefox

Limit Memory Usage using cgroups

Example: firefox

Limiting memory per process with cgroups

cgroups (abbreviated from control groups) is a Linux kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O, network, etc.) of a collection of processes. You can use it to make sure specific processes and their children cannot allocate more than a given amount of RAM.

On Ubuntu you must install the following package:

apt install cgroup-tools

Create a cgroup

sudo cgcreate -t hrishirt:hrishirt -a hrishirt:hrishirt -g memory:/cgBrowser

Replace hrishirt:hrishirt by your username and group and cgBrowser by the desired name for the control group

Set the maximum amount of RAM for the newly created cgroup.

(Again, replace cgBrowser with your desired name)

echo 3G | sudo tee /sys/fs/cgroup/memory/cgBrowser/memory.limit_in_bytes # 3 GB RAM

(Optional) If you have swap enabled, you can set a limit on that as well:

echo 3G | sudo tee /sys/fs/cgroup/memory/cgBrowser/memory.memsw.limit_in_bytes # 3 GB Swap

Launch your browser in the freshly created cgroup:

cgexec -g memory:cgBrowser firefox

Activate cgroups at boot with cgconfigparser

When this is all working you can make it permanent by placing this configuration in the file /etc/cgconfig.conf:

Contents: (Replace hrishirt with your own username)

group cgBrowser {
    perm {
        admin {
            uid = hrishirt;
        }
        task {
            uid = hrishirt;
        }
    }
    memory {
        memory.limit_in_bytes = "3G";
    }
}

Test the file for syntax errors:

sudo cgconfigparser -l /etc/cgconfig.conf

The command should not give any output.

systemd service for cgconfigparser

On Ubuntu, there is no systemd service to start cgconfigparser at boot. Here is a relatively simple service file to start cgconfigparser:

sudo vim /lib/systemd/system/cgconfigparser.service

Contents:

[Unit]
Description=cgroup config parser
After=network.target

[Service]
User=root
Group=root
ExecStart=/usr/sbin/cgconfigparser -l /etc/cgconfig.conf
Type=oneshot

[Install]
WantedBy=multi-user.target

Enable and start the service:

systemctl enable cgconfigparser
systemctl start cgconfigparser

On boot the cgroups will then be created via this service.

You can continue on with the next section to automatically place specific processes into specific cgroups via cgrulesengine but, for a simpler solution, you can edit your desktop launcher to run the command prefixed with cgexec. Or a simple cronjob every minute that runs cgclassify -g memory:cgBrowser $(pidof firefox) to put every running teams process in that cgroup.

Automatically put processes into a specific cgroup with cgrulesengined

Edit the following file:

sudo vim /etc/cgrules.conf

Contents:

hrishirt:firefox        memory          cgBrowser/

Replace hrishirt by your username, the processes (firefox) and the cgroups (cgBrowser) by your own. Save and check the file with:

/usr/sbin/cgrulesengd -vvv

No output means no errors.

You can omit :processname to limit everything by a user, memory can be replaced by other cgroup categories like cpu, but in our situation that is not applicable. I want to limit the memory of a few specific applications, not CPU cores or other resources.

systemd service for cgrulesengined

Just as with cfconfigparser, there is no default service on Ubuntu, but adding one is just as simple as the previous one.

Copy one of the configuration files:

cp /usr/share/doc/cgroup-tools/examples/cgred.conf /etc/cgred.conf

Add a systemd unit file:

sudo vim /lib/systemd/system/cgrulesgend.service

Contents:

[Unit]
Description=cgroup rules generator
After=network.target cgconfigparser.service

[Service]
User=root
Group=root
Type=forking
EnvironmentFile=-/etc/cgred.conf
ExecStart=/usr/sbin/cgrulesengd
Restart=on-failure

[Install]
WantedBy=multi-user.target

Enable and start the service:

systemctl enable cgrulesgend
systemctl start cgrulesgend

After a reboot you should have the processes you configured in.

To check if the process actually launches in the correct cgroup after a reboot you need to use the cgroup filesystem. The file /sys/fs/cgroup/memory/cgBrowser/tasks lists all the process ID's that run in that cgroup.

You can verify that it is working for firefox:

for pid in $(cat /sys/fs/cgroup/memory/cgBrowser/tasks); do pgrep $pid; done

@mbarnes
Copy link

mbarnes commented Nov 17, 2023

If MemoryMax is reached, OOM Killer will start killing processes within the unit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment