Unlock the Power of the Daemon with emacsclient

What is the Emacs daemon?

Emacs can be run in a server mode:

  • Pay Emacs startup cost only once per boot/login!
  • Buffers persist across Emacs frames, can close Emacs window and reopen later
  • Execute arbitrary commands and expressions from the command line

Manual: https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server

If you find this guide helpful, please consider supporting System Crafters via the links on the How to Help page!

Starting the daemon

The easiest way to get started is to use the following command inside of a running Emacs session

;; Enable server mode (daemon) for this Emacs session

However, this is very different in practice than running Emacs as a real daemon! We’ll show why in a bit.

emacs --daemon

# OR run as a foreground process (can be helpful to diagnose errors)

emacs --fg-daemon

You can also have independent daemons:

# Start daemon named 'my-other-daemon'
emacs --daemon=my-other-daemon

Trying it out

Let’s try running the Emacs daemon and see how it differs from running Emacs normally.

Run Emacs normally first to get a sense of the startup time.


Now run it as a daemon and notice how fast emacsclient creates a new frame:

emacs --fg-daemon

emacsclient -c

Notice anything different about the UI?

TIP: You can find the list of active daemon names (sockets) by looking in the directory stored in the server-socket-dir variable in Emacs!

Killing the Emacs daemon

To kill the Emacs daemon, send the (kill-emacs) command to it:

emacsclient -e "(kill-emacs)"

Adjusting configuration for the daemon

We have to make some tweaks to ensure

Don’t use window-system to check for your OS

Use the system-type variable instead! window-system will be nil when your configuration loads in the daemon.

(pcase system-type
  ('gnu/linux "It's Linux!")
  ('windows-nt "It's Windows!")
  ('darwin "It's macOS!"))

Checking if config is being loaded in the daemon

If you want to check if your config is being loaded up in the daemon, use the daemonp function. Useful tip: daemonp will return the name of the daemon if you gave it one with the --daemon=name parameter! This can allow you to skip loading certain sections of your config for a particular daemon name.

(if (daemonp)
    (message "Loading in the daemon!")
    (message "Loading in regular Emacs!"))

Configuring the UI for new frames

One common issue when using Emacs as a daemon is that fonts seem to not work correctly when starting a new frame using emacsclient. If you run set-face-attribute in the frame, the font will be applied to future frames too.

Fixing this requires some tweaks to configuration:

(defun efs/set-font-faces ()
  (message "Setting faces!")
  (set-face-attribute 'default nil :font "Fira Code Retina" :height efs/default-font-size)

  ;; Set the fixed pitch face
  (set-face-attribute 'fixed-pitch nil :font "Fira Code Retina" :height efs/default-font-size)

  ;; Set the variable pitch face
  (set-face-attribute 'variable-pitch nil :font "Cantarell" :height efs/default-variable-font-size :weight 'regular))

(if (daemonp)
    (add-hook 'after-make-frame-functions
              (lambda (frame)
                ;; (setq doom-modeline-icon t)
                (with-selected-frame frame

In Emacs 27 you can use server-after-make-frame-hook so that your function only gets called once!

Another useful pair of variables:

  • initial-frame-alist: Frame parameters to set on the first frame
  • default-frame-alist: Frame parameters to apply to every frame

These can both be used to configure UI elements that don’t work well when configuration is loaded in the daemon.

Manual: https://www.gnu.org/software/emacs/manual/html_node/emacs/Frame-Parameters.html

Fixing doom-modeline icons

doom-modeline needs some help to figure out that icons can be used. Add this to your configuration:

(setq doom-modeline-icon t)

Using emacsclient

Important arguments

  • -c / --create-frame - Create a new frame (don’t pass this if you want to reuse the same open frame)
  • -n / --no-wait - Don’t wait for the Emacs frame to close
  • -e / --eval - Evaluate an Emacs Lisp expression within the daemon
  • -u / --suppress-output - Suppress output from Emacs (useful when running in a script)
  • -s / --socket-name=name - Use a named daemon (emacs --daemon=name)
  • -a / --alternate-editor=name - If Emacs daemon isn’t running, use this command instead
  • filename - Open a file in the current frame (or a new one if -c is passed)

Opening files from the command line

To open a new Emacs frame for a file without waiting for emacsclient to exit:

emacsclient -c -n ~/.emacs.d/Emacs.org

Set EDITOR to emacsclient in your shell’s profile (.bash_profile, .zsh_profile, etc)

export EDITOR="emacsclient -c -a emacs"

Test this by using git commit (use C-x # to confirm your edit and close the frame!)

Evaluating expressions

This makes it easy to integrate other programs with Emacs!

emacsclient -e "(buffer-name)"

You can also run interactive commands to cause something to happen in the active Emacs frame:

emacsclient -e "(counsel-switch-buffer)"

emacsclient -e "(read-string \"Enter a string: \")"

Automating Emacs in shell scripts

Example: My sync-dotfiles script (Web)

emacsclient -u -e "(org-save-all-org-buffers)" -a "echo 'Emacs is not currently running'"

Offloading tasks to another daemon

I don’t necessarily recommend this approach, but it is possible!

emacs --daemon=worker
emacsclient -f worker -u -e "(org-babel-tangle-file \"~/.emacs.d/Emacs.org\")"

I’d recommend checking out the async package if you want to do things like this, though:


We’ll cover it in another video.

Running Emacs at Startup

Emacs comes with a systemd unit file:

sudo systemctl --user enable emacs

If you’re allergic to systemd (or just want another way to run at login), you can possibly add it to the startup configuration for your desktop environment, profile script, etc.

Subscribe to the System Crafters Newsletter!
Stay up to date with the latest System Crafters news and updates! Read the Newsletter page for more information.
Name (optional)
Email Address