runapp is a Linux desktop application runner that launches the given application in an appropriate systemd user unit, according to systemd recommendations.
By running each application in its own systemd service, you avoid the risk of a single memory-hungry
application taking down your entire compositor session, and you gain the
ability to easily inspect applications via e.g.
systemctl --user status <my-app>.
A great complement to runapp is uwsm, which provides an easy way to run your Wayland compositor as well as user services and applications under systemd.
In fact, runapp is inspired by uwsm app, which has a similar feature set. However, compared to
it and other alternatives, runapp is very fast (native executable written in modern C++) and has minimal
dependencies (only systemd, no Python, binary is <200K).
- Arch Linux: Get runapp from the AUR (Arch User Repository).
- NixOS: runapp is being added to nixpkgs.
- Other: Run
make install.- This requires that you have GCC 15 or later with C++ compiler and GNU Make.
- You may be prompted for your
sudopassword. - To uninstall again, run
make uninstall.
Your systemd user instance must include some key environment variables needed to run graphical applications.
To verify this, check that the output of systemctl --user show-environment includes
WAYLAND_DISPLAY (if using Wayland) or DISPLAY (if using Xorg), as well as anything else your
compositor may require (e.g. Sway needs SWAYSOCK, i3 needs I3SOCK).
If something is missing, review your compositor setup; I recommend using uwsm and following its setup instructions, which will take care of everything.
Running an application via runapp is as simple as e.g. runapp firefox to run Firefox.
Or if you want to inspect the systemd unit, the -v/--verbose option is useful:
$ runapp -v zathura
Resolved executable zathura to /usr/bin/zathura
Launching app-sway-zathura@7c715bb4dcb4e28d.service: ["zathura"].
Success.
$ systemctl --user status app-sway-zathura@7c715bb4dcb4e28d.service
● app-sway-zathura@7c715bb4dcb4e28d.service - zathura
Loaded: loaded (/run/user/1000/systemd/transient/app-sway-zathura@7c715bb4dcb4e28d.service; transient)
Transient: yes
Active: active (running) since Sun 2025-10-12 19:46:49 UTC; 1min 27s ago
Invocation: b39d29d98b684eed8e7a900fdb3fd86f
Main PID: 49494 (zathura)
Tasks: 8 (limit: 18644)
Memory: 26.7M (peak: 32.7M)
CPU: 334ms
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/app-graphical.slice/app-sway-zathura@7c715bb4dcb4e28d.service
└─49494 zathura
Oct 12 19:46:49 localhost systemd[655]: Starting zathura...
Oct 12 19:46:49 localhost systemd[655]: Started zathura.
Oct 12 19:46:49 localhost zathura[49494]: info: Opening plain database via sqlite backend.
Oct 12 19:46:49 localhost zathura[49494]: info: No plain database available. Continuing with sqlite database.
$ systemctl --user stop app-sway-zathura@7c715bb4dcb4e28d.service
To see what else you can do, try runapp --help:
runapp [OPTIONS] COMMAND...
Run COMMAND as a systemd user unit, in a way suitable for typical applications.
Options:
-v, --verbose: Increase output verbosity.
-o, --scope: Run command directly, registering it as a systemd scope;
the default is to run it as a systemd service.
-i SLICE, --slice=SLICE:
Assign the systemd unit to the given slice (name must include
".slice" suffix); the default is "app-graphical.slice".
-d DIR, --dir=DIR:
Run command in given working directory.
-e VAR=VALUE, --env=VAR=VALUE:
Run command with given environment variable set;
may be given multiple times.
-c DESC, --description=DESC:
Set human-readable unit name (Description= systemd property)
to given value.
runapp --help
Show this help text.
Or you can read the man page via man runapp.
Probably you don't always want to type runapp firefox to run Firefox (for example).
A simple way to improve on this is to make a shell alias, function, or script with
a short and memorable name that runs runapp firefox for you.
In addition, there are some other ways that applications are often launched:
Tell your launcher to run the selected applications via runapp.
- Fuzzel:
fuzzel --launch-prefix=runappor addlaunch-prefix=runappto the config file. - Rofi:
rofi -show drun -show-icons -run-command 'runapp {cmd}'.
Tell your file manager to open a selected file in an application via runapp.
- Yazi: Configure openers
that run applications via
runapp, then use them in your[open]configuration. - LF: Define
cmd opento something that launches the application viarunapp. - Ranger: Configure rifle
to run each application via
runapp. - Vifm: Use the
:filetypesetting.
- Rifle (part of Ranger): see Ranger above.
Various others, including xdg-open, are
unfortunately not sufficiently customizable to allow interposing runapp.
- Sway: Configure e.g.
bindsym Mod4+Return exec runapp footto have it launch the foot terminal emulator under runapp onSuper+Return. - Hyprland: Configure e.g.
bind = SUPER, Return, exec, runapp footfor the same thing.
- Fast: native code (written in moden C++);
talks directly to systemd, via its private socket if available, the same way that
systemd-rundoes. - No dependencies beyond systemd.
- Run app either as systemd service
(recommended, default) or as systemd scope.
- The latter means
runappdirectly executes the application, after registering it with systemd.
- The latter means
- If run from Fuzzel, or any other launcher that passes the same environment variables (see man page for details):
- Generate systemd unit name from
.desktopname, per systemd recommendations. - Take "friendly name" (
Description=systemd unit property) from.desktopfile'sName=value.
- Generate systemd unit name from
- Options to customize several aspects of the execution context:
- Working directory
- Environment variables
Description=systemd property- systemd slice (defaults to systemd-recommended
app-graphical.slice)
- On error, show desktop notification (unless run from interactive terminal).
I don't consider these very important to have, but I'm open to discussions (feel free to open an issue!).
- Support custom unit name.
- Support being given a Desktop File ID (only).
- With optional Action ID
- Support running
.desktopfiles with args, using field codes.- See https://specifications.freedesktop.org/desktop-entry-spec/latest/exec-variables.html
- Note that
%fand%uentail running multiple app instances - Alternatively, this could be done by the launcher, e.g. Fuzzel: https://codeberg.org/dnkl/fuzzel/issues/346
- Support running app connected to terminal, like
systemd-run --pty.
uwsm app: the original; somewhat slow due to being written in Python.uwsm-app: shell script that ships with uwsm; spawns a background daemon.app2unit: self-sufficient and feature-complete shell script by the author of uwsm.
Prerequisites: GCC 15 or later with C++ compiler, and GNU Make.
make debug: create debug build.make compile_commands.json: generatecompile_commands.jsonfile, useful for language servers likeclangd; requiresbear.make release: create release build.make clean: delete all build artefacts.make install: install release build into/usr/local(or override viaprefixvariable).make uninstall: delete installed release build.