This post is part of the Wayland Wayfinders series.
Melding systemd and foot for reduced memory consumption in sway
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 2 for_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 } 9 bindsym F1 [app_id = "mS"] scratchpad show; move position 0 0, resize set 50 ppt 35 ppt 10 exec_always foot --app-id = "mS" -e tmux new -A -s mS 11 12 # The secondary scratchpad in the center 13 for_window [app_id = "subFloat"] { 14 move scratchpad 15 move position center 16 floating enable 17 border pixel 1 18 } 19 bindsym 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 called mS
and resize it to a long thin window Put another scratchpad in the center, bound to F5 Attach or start a tmux session called subFloat
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:
1 set $term_server foot -s 2 set $term footclient 3 exec_always $term_server 4 bindsym $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:
1 exec_always foot --app-id = "mS" -e tmux new -A -s mS 2 # ... 3 exec_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.
Figure 1: Snapshot of memory consumption (from btop ), clients take around 10x less memory
On its own, given that I mostly use relatively high-end machines, this was not really a problem, more an aesthetic annoyance.
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 2 include = ~/.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.
exec_always doesn’t prevent process failure
Nor should it, as the documentation explicitly mentions, it is basically like exec but it also gets re-executated 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 just because 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 2 systemctl --user daemon-reload 3 # Start and enable a service 4 systemctl --user --now foot-server.service 5 # Reload or restart, also when files are changed 6 systemctl --user reload-or-restart foot-server.service 7 # Check the status 8 systemctl --user status foot-server.service
There are a few common stanzas which are needed for a minimal service file:
1 [Unit] 2 Description = 3 [Service] 4 ExecStart = 5 Restart = on-failure 6 [Install] 7 WantedBy = default.target
With much more extensive documentation found in the man pages (or online, here).
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 [[Complete configurations][the next section]].
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 configurations
Figure 2: Final single server many client view (via btop )
Sway Scratchpads and Config
The final sway scratchpad setup is essentially:
1 # Settings 2 set { 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 9 for_window [app_id = "mS"] { 10 move to scratchpad 11 floating enable 12 $ms_pos 13 } 14 bindsym F1 exec swaymsg [app_id = "mS"] scratchpad show || $start_ms, $ms_pos 15 16 # The secondary scratchpad in the center 17 for_window [app_id = "subFloat"] { 18 move to scratchpad 19 floating enable 20 border pixel 1 21 } 22 bindsym 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 2 set $term footclient --server-socket = $XDG_RUNTIME_DIR/foot-server.sock
Systemd services
1 [Unit] 2 Description = Foot terminal server 3 4 [Service] 5 ExecStart = /usr/bin/foot --server=/run/user/%U/foot-server.sock 6 Restart = on-failure 7 8 [Install] 9 WantedBy = default.target
1 [Unit] 2 Description = Foot client for tmux session mS 3 After = foot-server.service 4 Requires = foot-server.service 5 6 [Service] 7 ExecStart = /usr/bin/footclient --server-socket=/run/user/%U/foot-server.sock --app-id="mS" -e tmux new -A -s mS 8 Restart = on-failure 9 10 [Install] 11 WantedBy = default.target
1 [Unit] 2 Description = Foot client for tmux session subFloat 3 After = foot-server.service 4 Requires = foot-server.service 5 6 [Service] 7 ExecStart = /usr/bin/footclient --server-socket=/run/user/%U/foot-server.sock --app-id="subFloat" -e tmux new -A -s subFloat 8 Restart = on-failure 9 10 [Install] 11 WantedBy = 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.
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.