8 minutes
Written: 2024-06-02 15:00 +0000
Updated: 2024-08-06 00:53 +0000
Lowering resource usage with foot and systemd
This post is part of the Wayland Wayfinders series.
Melding
systemd
andfoot
for rapid configuration updates and reduced memory consumption insway
with persistenttmux
state
Background
Since I often work in strong sunlight, I often want to reconfigure my terminal
to use a light or a dark scheme depending on the time of day. In the process, I
decided to fiddle around with custom systemd
targets, since sway
isn’t
really good at managing long running processes like my terminal (foot
) server.
Starting point
Some years ago I ended up with a rather ad-hoc scratchpad configuration, reproduced for clarity (current config here):
1# The main scratchpad in the upper left
2for_window [app_id="mS"] {
3 move scratchpad
4 move position 0 0
5 resize set 50 ppt 35 ppt
6 floating enable
7 border pixel 1
8}
9bindsym F1 [app_id="mS"] scratchpad show; move position 0 0, resize set 50 ppt 35 ppt
10exec_always foot --app-id="mS" -e tmux new -A -s mS
11
12# The secondary scratchpad in the center
13for_window [app_id="subFloat"] {
14 move scratchpad
15 move position center
16 floating enable
17 border pixel 1
18}
19bindsym F5 exec swaymsg [app_id="subFloat"] scratchpad show || exec foot --app-id="subFloat" -e tmux new -A -s subFloat
Which essentially boils down to:
- Put a scratchpad in the upper left bound to
F1
and resize it to a long thin window- Attach or start a
tmux
session here calledmS
- Attach or start a
- Put another scratchpad in the center, bound to
F5
- Attach or start a
tmux
session calledsubFloat
- Attach or start a
When everything works (e.g. at start-up) it essentially leads to a scratchpad setup like so:
Additionally, I also have some additional setp for managing terminals:
1set $term_server foot -s
2set $term footclient
3exec_always $term_server
4bindsym $mod+Return exec $term
Which ideally starts a foot
server, and subsequently spawns footclient
instances on demand.
Annoyances
This setup served me quite well, for several years I had no major issues whatsover, though there were minor annoyances, some of which eventually broke the camel’s back.
Memory consumption
The problem is perhaps immediately evident in the narrowed down specification:
1exec_always foot --app-id="mS" -e tmux new -A -s mS
2# ...
3exec_always foot --app-id="subFloat" -e tmux new -A -s subFloat
Right of the bat, it is rather evident that this configuration will spawn two
instances of foot
, one for each scratchpad.
On its own, given that I mostly use relatively high-end machines, this was not really a problem, more an aesthetic annoyance.
Applying foot
configurations
If the main (sway
) server is restarted to apply a configuration update it will
not propagate to the scratchpad unless those are also restarted, and sway
isn’t meant to handle long running processes. Essentially, applying a change of
theme via foot.ini
(crrent config here):
1# include = ~/.config/foot/foot.d/theme-acario-light.ini
2include = ~/.config/foot/foot.d/theme-solarized-dark-normal-brights.ini
Would entail a whole killall -9 foot
song and dance which was also rather
annoying, especially under the current configuration, the tmux
sessions would
also die with the foot
instances.
exec_always
doesn’t restart failed processes
Nor should it, as the documentation explicitly mentions, it is basically like
exec
but it also gets re-executed after a reload. For something like a
terminal server service, this isn’t exactly what is required at all. Afterall:
- It is independent of the
sway
configuration
Or it really should be. On the other hand, some of the settings for the
scratchpads were baked into the configuration. Having to reload the sway
configuration (and redo the tmux
state) just because aesthetic changes needed
to be propagated is asinine, and finally annoyed me enough to make the switch to
a real service manager.
Using systemd
to offload management
The solution is to abstract the configuration for the main and helper
scratchpads out of the configuration and also offload management of the process
lifetime to systemd
. User configurations for systemd
live in
$HOME/.config/systemd/user
and require some standard commands for interacting
with them:
1# Reload user units, every time files are changed
2systemctl --user daemon-reload
3# Start and enable a service
4systemctl --user --now foot-server.service
5# Reload or restart, also when files are changed
6systemctl --user reload-or-restart foot-server.service
7# Check the status
8systemctl --user status foot-server.service
There are a few common stanzas which are needed for a minimal service file:
1[Unit]
2Description=
3[Service]
4ExecStart=
5Restart=on-failure
6[Install]
7WantedBy=default.target
With much more extensive documentation found in the man
pages (or online, here).
Terminal Server
The ordering of the services are also fairly obvious, both the scratchpads (and
indeed, the sway terminals) need one primary foot-server
instance to be
running at all times. The easiest approach is to reuse XDG_RUNTIME_DIR
which
is typically the same as /run/user/$(id -u $USER)/
, so the server runs via:
1/usr/bin/foot --server=/run/user/%U/foot-server.sock
Since foot -s
doesn’t fork itself, the default Type=simple
will suffice.
Additionally, we will need to ensure that all the footclient
instances we call
use the same server socket (as shown in Sway Scratchpads and Config).
Scratchpad clients
The clients themselves are equally simple, except that the [UNIT]
stanza will
specify they require foot-server.service
and will be run after
foot-server.service
.
1/usr/bin/footclient --server-socket=/run/user/%U/foot-server.sock --app-id="mS" -e tmux new -A -s mS
The full configurations are reproduced in the next section.
tmux
sessions
Additionally, since the goal is to reload foot
often via simple killall -9 foot
to apply new configurations, it makes sense to add a tmux
server into
the mix, which will also be part of the requires and after keys for the
scratchpad clients.
For this we need to consider some additional options:
1[Service]
2Type= # simple (default), forking, oneshot
3ExecStop=
4RemainAfterExit=yes
Some notes on service types:
- The server will be a
forking
service, sincetmux start-server
essentially puts itself in the background. - The clients should be
oneshot
, since they should complete before other dependent services start1 - We need to set remain after exit since we have a “rollback” for our service
(
ExecStop
) and since we modify system state, i.e. we don’t want to re-run the service actions without stopping2
False starts
I thought I wanted to use sockets (online man-page), but for a constantly
running set of terminals there is very little point. On my machine, the foot
server takes around 20MB-31MB while each client instance is below 1.5MB, so
having a a socket for either the server or the clients made little to no sense.
Complete configurations3
Sway Scratchpads and Config
The final sway
scratchpad setup is essentially:
1# Settings
2set {
3 $ms_pos border none, move position 0 0, resize set 50 ppt 35 ppt
4 $sf_pos border pixel 1, move position center
5 $start_ms systemctl --user start foot-ms.service
6 $start_sf systemctl --user start foot-subfloat.service
7}
8# The main scratchpad in the upper left
9for_window [app_id="mS"] {
10 move to scratchpad
11 floating enable
12 $ms_pos
13}
14bindsym F1 exec swaymsg [app_id="mS"] scratchpad show || $start_ms, $ms_pos
15
16# The secondary scratchpad in the center
17for_window [app_id="subFloat"] {
18 move to scratchpad
19 floating enable
20 border pixel 1
21}
22bindsym F5 exec swaymsg [app_id="subFloat"] scratchpad show || $start_sf, $sf_pos
With the bindings changed to point to the same socket as the process:
1# Remove exec_always $term_server, infact get rid of it all together, since systemd handles it now
2set $term footclient --server-socket=$XDG_RUNTIME_DIR/foot-server.sock
Systemd services
foot
setup
1[Unit]
2Description=Foot terminal server
3
4[Service]
5ExecStart=/usr/bin/foot --server=/run/user/%U/foot-server.sock
6Restart=on-failure
7
8[Install]
9WantedBy=default.target
1[Unit]
2Description=Foot client for tmux session mS
3After=foot-server.service tmux-session-ms.service
4Requires=foot-server.service tmux-session-ms.service
5
6[Service]
7ExecStart=/usr/bin/footclient --server-socket=/run/user/%U/foot-server.sock --app-id="mS" -e tmux new -A -s mS
8Restart=on-failure
9
10[Install]
11WantedBy=default.target
1[Unit]
2Description=Foot client for tmux session subFloat
3After=foot-server.service tmux-session-subfloat.service
4Requires=foot-server.service tmux-session-subfloat.service
5
6[Service]
7ExecStart=/usr/bin/footclient --server-socket=/run/user/%U/foot-server.sock --app-id="subFloat" -e tmux new -A -s subFloat
8Restart=on-failure
9
10[Install]
11WantedBy=default.target
tmux
setup
1[Unit]
2Description=Tmux server
3After=network.target
4
5[Service]
6Type=forking
7ExecStart=/usr/bin/tmux start-server
8ExecStop=/usr/bin/tmux kill-server
9RemainAfterExit=yes
10
11[Install]
12WantedBy=default.target
1[Unit]
2Description=Tmux session mS
3After=tmux-server.service
4Requires=tmux-server.service
5
6[Service]
7Type=oneshot
8RemainAfterExit=yes
9ExecStart=/usr/bin/tmux new-session -s mS -d
10ExecStop=/usr/bin/tmux kill-session -t mS
11
12[Install]
13WantedBy=default.target
1[Unit]
2Description=Tmux session subFloat
3After=tmux-server.service
4Requires=tmux-server.service
5
6[Service]
7Type=oneshot
8RemainAfterExit=yes
9ExecStart=/usr/bin/tmux new-session -s subFloat -d
10ExecStop=/usr/bin/tmux kill-session -t subFloat
11
12[Install]
13WantedBy=default.target
Conclusions
Perhaps on modern machines, with many gigabytes of RAM, there is no real benefit
to this approach, however, given that I use a lot of terminals and don’t always
reach for tmux
, for me this works out very nicely. It also helps handling
configuration updates to foot
, since all instances use the same server.
Moreover, this setup greatly facilitates changing themes, a feature that is
particularly useful for those who work in varying lighting conditions, like
moving between indoor and outdoor environments. For people who consistently work
in the same environment, this might not be a significant advantage. However, the
persistent tmux
services is likely to be useful in any case.
Nevertheless, the minor annoyances have been quelled, and I had never really
worked with systemd
services before barring a few backup setups, so this was
pretty gratifying.
There is an excellent visual explanation of the differences b/w simple and oneshot types here by Thomas Stringer ↩︎
Aside from the official documentation, this SO answer is quite nice ↩︎
for now, updates will probably appear only on my Dotfiles repo ↩︎
Series info
Wayland Wayfinders series
- Revisiting Wayland for ArchLinux
- Lowering resource usage with foot and systemd <-- You are here!