Sat 29 October 2022

Isolating Tmux windows to prevent systemd-oomd from killing the server

Posted by ankur in Tech (468 words, approximately a 2 minute read)

I run a number of Tmux sessions, one for each project or context, (via Byobu) to do my work on a daily basis. Tmux uses a client-server architecture, so there's a Tmux server running that all of these sessions connect to. Some time ago, I began noticing that all my Tmux sessions were being killed while I worked. I knew this wasn't a random occurrence. A look at the logs told me that systemd-oomd was killing my Tmux server, and all my sessions and their windows with it---all my vim sessions, all of it.

This, of course, is far from ideal. What's happening here is that one of the processes occupying a Tmux window consumes lots of CPU/memory and systemd-oomd needs to kill it. However, systemd-oomd does not work on a per-process level. It works on a cgroup level. So, it kills the whole cgroup, taking the Tmux server down.

I've been looking at ways of preventing this from happening. One option is to disable systemd-oomd completely. I think I've been fine before these OOM tools came along, but I do see the advantages of having them. So I'd rather keep them around and configure them if possible.

The other, perhaps simpler, option is to somehow isolate each of my Tmux windows so that they're all killed independently without affecting each other. I looked into this latter option first. After some discussion on Ask Fedora, I came up with a solution: run every Tmux window in isolation using systemd-run.

So, when we start a new Tmux session, we want to start our first window using systemd-run. Easiest to do this using a bash function that one can put in their ~/.bashrc file:

# create new session, and ensure first window runs in a separate systemd
# scope
byobu-new-session ()
    byobu new -s $1 systemd-run --user --scope bash

Next, to open a new window in an existing session using systemd-run, we can use the new window Tmux command:

new-window systemd-run --user --scope bash

This can be bound to a keyboard shortcut:

bind-key c new-window systemd-run --user --scope bash

That's it. Using systemd-cgls one can see that each new Tmux window starts in a new systemd scope. To test that this now isolates each window, one can run something like tail /dev/zero to cause it to get killed by systemd-oomd while leaving other windows, sessions, and the server untouched.

This seems to be working very well for me. If you have a better solution, do let me know, though.

Edit: a simpler way

As it happens, there's a much easier way to do this. Instead of modifying ~/.bashrc and then re-binding the new-window key, all one needs to do is redefine the default-command parameter that Tmux uses so that it runs the systemd-run command:

# in tmux.conf
set-option -g default-command 'systemd-run --user --scope bash'