Running multiple emacs daemons
I have been using Emacs for several years, and these days I'm using it both for writing code and for working with my email (another post on that soon).
As commonly suggested, I run Emacs in daemon-mode to keep things fast and snappy, with an alias to auto-start the daemon if it's not started, and connect to it if started:
alias e='emacsclient -a "" -c'
But, this has some problems:
- The buffers for email and code projects get mixed together
- Restarting the emacs server for code (for example) kills the open mail buffers as well
- Emacs themes are global β they cannot be set per frame. For code, I prefer a dark theme (most of the time), but for email, a light theme works better for me (specially for HTML email).
To solve this, I searched for a way to run multiple emacs daemons, selecting which one to connect to using shell aliases, and automatically setting the theme based on the daemon name. Here's my setup to achieve this:
Custom run_emacs function in zshrc:
run_emacs() {
if [ "$1" != "" ];
then
server_name="${1}"
args="${@:2}"
else
server_name="default"
args=""
fi
if ! emacsclient -s ${server_name} "${@:2}";
then
emacs --daemon=${server_name}
echo ">> Server should have started. Trying to connect..."
emacsclient -s ${server_name} "${@:2}"
fi
}
This function takes an optional argument β the name to be used for the daemon. If not provided, it uses default
as the name. Then, it tries to connect to a running daemon with the
name. And if it's not running, it starts the daemon and then connects to
it. It also passes any additional arguments to emacsclient
.
Custom aliases in zshrc:
# Create a new frame in the default daemon
alias e='run_emacs default -n -c'
# Create a new terminal (TTY) frame in the default daemon
alias en='run_emacs default -t'
# Open a file to edit using sudo
es() {
e "/sudo:root@localhost:$@"
}
# Open a new frame in the `mail` daemon, and start notmuch in the frame
alias em="run_emacs mail -n -c -e '(notmuch-hello)'"
The first 3 aliases use the default
daemon. The last one creates a new frame in the mail
daemon and also uses emacsclient
's -e
flag to start notmuch (the email package I use in Emacs).
Emacs config:
(cond
((string= "mail" (daemonp))
(setq doom-theme 'modus-operandi)
)
(t
(setq doom-theme 'modus-vivendi)
)
)
This checks the name of the daemon passed during startup, and sets the doom theme accordingly. The same pattern can be used to set any config based on the daemon name.
Note that I'm using doom emacs, but the above method should work with or without any framework for Emacs. Tested with Emacs 27 and 28.
Interactions
I've just found that
if ! emacsclient -s ${server_name} "${@:2}";
doesn't always 'do what I want it to' (i.e. returntrue
on exit), especially when I've entered with no arguments. I changed to usingpgrep
, based partially on something in emacswiki, with a zsh alias using pgrep containing