Once I started using Emacs more frequently, I wanted to use it on every machine I owned. But I found it a pain to keep the configuration synchronized between machines; there were always some little changes I'd need on my laptop and not my desktop (and vice versa). Preventing these changes from affecting the wrong machine became a headache, but thankfully there are ways around it with a little bit of elisp.

My current setup now uses the same configuration directory, but with custom setup files that only affect certain users and operating systems.

Here's how it all fits together.

no-littering

no-littering is a small package that keeps the ~/.emacs.d directory clean and organized. This makes it easier to exclude machine-specific files during synchronization.

It's available on melpa and doesn't require any configuration to get started; a single line is all that's required to use it:

;; Standard load.
(require 'no-littering)

;; or with use-package
(use-package no-littering)

Synchronization

I sync my ~/.emacs.d directory between machines using Dropbox. The following directories are excluded:

  • ~/.emacs.d/auto-save-list/
  • ~/.emacs.d/backup/
  • ~/.emacs.d/etc/
  • ~/.emacs.d/server/
  • ~/.emacs.d/var/

These directories often contain things that are tied to the current machine's directory layout, which can cause issues when they are synchronized. The backup directory in particular generates a lot of files and excluding that cut out a lot of headaches.

Per-user configuration

All of my per-user configuration lives in the ~/.emacs.d/users/{username}/ directory. All files in that directory are loaded last so that they override any default settings.

The following snippet is placed just before (provide 'init) in my ~/.emacs.d/init.el file.

;; Configure the user directory path.
(defvar user-settings-dir
  (concat user-emacs-directory "users/" user-login-name)
  "Settings directory for the current user.")

(defun pn/load-user-init-file (file)
  "Load an init file from FILE."
  (load (format "%s/%s" user-settings-dir file)))

;; Load all elisp files in the current user directory.
(when (file-exists-p user-settings-dir)
  (mapc #'pn/load-user-init-file
	(directory-files user-settings-dir nil "^[^#].*el$")))

Per-OS configuration

The final piece of the puzzle is setting up configuration that only runs on a specific operating system. I use this for setting fonts, paths, and themes.

I put this code in a user-specific init.el file, although it can also go at the end of the main init.el file.

(when (string= "windows-nt" system-type)
  ;; Windows only configuration.
  )

(when (string= "darwin" system-type)
  ;; Mac OS only configuraiton.
  )

(when (string= "gnu/linux" system-type)
  ;; Linux only configuration.
  )

Using all of these techniques together allows me to keep my Emacs configuration in sync with a minimum of fuss.