I have a startup script that sometimes runs on startup and sometimes doesn't (I don't know why). This is a problem for me because the script swaps keys on my keyboard so I need the keys swapped after my desktop loads. I wrote a systemd service but it won't work. Here's my service file.

# /etc/systemd/system/keySwap.service

Description=Swaps right Option key with Right CMD key



When I run the service manually in terminal in graphical environment it errors out.

sudo systemctl start keySwap
sudo systemctl status keySwap

● keySwap.service - Swaps right Option key with Right CMD key
     Loaded: loaded (/etc/systemd/system/keySwap.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Tue 2022-04-05 18:57:24 CEST; 4s ago
    Process: 12028 ExecStart=/home/jedrek/keyboard_keys_swapper.sh (code=exited, status=1/FAILURE)
   Main PID: 12028 (code=exited, status=1/FAILURE)

kwi 05 18:57:24 Home systemd[1]: Started Swaps right Option key with Right CMD key.
kwi 05 18:57:24 Home keyboard_keys_swapper.sh[12029]: xmodmap:  unable to open display ''
kwi 05 18:57:24 Home keyboard_keys_swapper.sh[12030]: xmodmap:  unable to open display ''
kwi 05 18:57:24 Home systemd[1]: keySwap.service: Main process exited, code=exited, status=1/FAILURE
kwi 05 18:57:24 Home systemd[1]: keySwap.service: Failed with result 'exit-code'.

The shell script simply swaps the keys:

xmodmap -e "keycode 108 = Super_R"
xmodmap -e "keycode 134 = ISO_Level3_Shift"

I know that xmodmap requires graphical desktop to be loaded so I don't understand why it doesn't work. How can I solve my problem?

2 Answers 2


All X programs need to know which display to communicate with, and they do it via the DISPLAY environmental variable. When you have a terminal running in your graphical desktop, you can see what that value is: echo $DISPLAY

It's typically just :0 which translates as the first display on the current host, so you could set this in your script by adding a line saying export DISPLAY=:0, and it may work.

So that might get you going...

However, you may want to rethink this plan. The systemd unit file, as written, will only start over & over, since the script exits as soon as your xmodmap actions are taken. This would be better written as a Type=oneshot service along with RemainAfterExit=true. Since you do want to have it restart on the next startup, you'll want to kill it off by having it conflict with the display manager shutdown target (plymouth-quit.service on my gnome-based system). And you can see this is getting kinda complicated.

If you're insistent on staying with the systemd approach, the easiest way may be to add an additional ExecStartPost action to display-manager.service via systemctl edit display-manager.service.

But I think the best approach is to figure out which files your display manager uses on start up and modify those to run your script. I think that systemd is too heavy a hammer for such a local change.

  • You're right, using systemd may be an overkill for such a task but I haven't come up with a better plan and I am not insistent on staying with the systemd approach, I am open to other ideas.
    – jedi
    Apr 7, 2022 at 8:06

In general, systemd services are the wrong place to do anything with X11. This should be done either in the scripts that inititalize the display prior to offering the login prompt, or as part of the user's session start scripts.

In addition, xmodmap appears to be an obsolete way to change key bindings, as the internationalization tools also remap the keyboard, and any attempts to use xmodmap will be temporary at best, as the I18n tools will remap the keyboard and wipe xmodmap changes any time the keyboard is changed, when the screen is locked or unlocked, and when the users GUI session manager reinitializes.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .