Managing Emacs Server as Systemd Service

Using Emacs Server Without Systemd

I live in Emacs entirely apart from using browser for googling. Having an Emacs server running on the background makes Emacs available all the time. So I won't worry about closing it accidental.

It is not hard to do that, just run

in command line to start the Emacs server. It will load user configuration file as usual. Then run

to open an Emacs GUI instance that uses the Emacs server. That's how I have been doing for a while.

An better approach is using systemd. It is the services manager of Linux. Whenever my Debian 11 laptop boot up, systemd would start a bunch of services in parallel, for example, Networking manager connects WIFI, Bluetooth connects wireless keyboard so everything would be ready after I login. And I want Emacs to be ready as well.

I can achieve that by simply having an shell script automatically running after login. But there are benefits of using systemd. It has bunch of sub-commands for managing services, for example, checking logs, status etc.

It's a nice tool to have, I can use it for example Jupyter Notebook server.

That's why I pulled the trigger and spent 2 hours in implementing and testing it. Here's the technical bit.

How to Implement As Systemd Service

In order to use systemd to manage Emacs server, I firstly need a configuration file (which is called unit file). Debian Wiki provides a short description of the syntax and parameter of unit file.

I found an simple one in Emacs Wiki. It looks like this

[Unit]
Description=Emacs text editor
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/

[Service]
Type=forking
ExecStart=/usr/bin/emacs --daemon
ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"
Environment=SSH_AUTH_SOCK=%t/keyring/ssh
Restart=on-failure

[Install]
WantedBy=default.target


The important parameters are

ExecStart
It tells systemd what to do when starting Emacs

service, in this case it runs /usr/bin/emacs --daemon command.

ExecStop
it tells systemd what to do when shutting down Emacs

service, in this case it runs /usr/bin/emacsclient --eval "(kill-emacs)" command.

If you are using an Emacs built in a difference directory, you have to change /usr/bin/emacs to wherever your Emacs is located.

Then save the configuration file as ~.config/systemd/user/emacs.service/.

After that run

so systemd would copy the configuration file into central places and it would start Emacs service at boot time.

To run Emacs service right now, use

This is what I see in my console

emacs.service - Emacs text editor
Active: active (running) since Mon 2021-06-14 09:12:26 BST; 24h ago
Docs: info:emacs
man:emacs(1)
https://gnu.org/software/emacs/
Main PID: 5222 (emacs)
Memory: 154.7M
CPU: 3min 25.049s
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/emacs.service
├─ 5222 /usr/bin/emacs --daemon
└─16086 /usr/bin/aspell -a -m -d en_GB -p /home/yitang/git/.emacs.d/local/ispell-dict --encoding=utf-8

Jun 14 09:11:57 7270 emacs[5222]: No event to add
Jun 14 09:11:57 7270 emacs[5222]: Package dash-functional is obsolete; use dash 2.18.0 instead
Jun 14 09:12:26 7270 emacs[5222]: Starting Emacs daemon.
Jun 14 09:12:26 7270 systemd[4589]: Started Emacs text editor.


Enhance User Experience

For far, I have the following two tweaks to make the usage of systemd more pleasant.

sudo Privilege

The Emacs server is started using my own account, so it doesn't have the sudo privilege. In order to edit files that requires sudo permission, simple open the file in Emacs, or in command line with

then type M-x sudo inside Emacs, type the sudo password. If the password is correct, I can edit and save the file as sudo user.

Environment Variables

The customised shell configuration in .bashrc are loaded when opening an interactive shell session. So the Emacs server managed by systemd would not have the environment variables, alias, functions or whatever defined in .bashrc.

This stackoverflow post provides the rationale and how to tweak the unit file so systemd would load .bashrc.

This problem can solved a lot easier on the Emacs side, by using exec-path-from-shell package. It will ensure the environment variables inside Emacs are the same as in the user's interactive shell.

Simply put the following in your .emacs would do the trick.