Skip to main content
2 of 5
add note about use-package

You can get as elaborate as you want with ensuring the portability of your setup. (I, personally, go for the "complete overkill" solution.)

Install packages automatically on Emacs startup

(Note: while I quite like my current setup, use-package looks extremely nice, and I'll probably be switching to it someday. See the other answers to this question.)

This is what I do in my init.el (full version here):

;;; Here we are using the defvar-nil-setq pattern described in [1],
;;; which makes it so that changes to this list will be picked up by a
;;; reload of init.el (M-RET r).
;;;
;;; [1]: http://ergoemacs.org/emacs/elisp_defvar_problem.html
(defvar radian-packages nil "The packages required by Radian.")
(setq radian-packages
      '(
        ace-jump-mode ; quickly jump to words, characters, or lines onscreen
        aggressive-indent ; keep code correctly indented at all times
         .
         .
         .
        projectile ; quickly jump to files organized by project
        undo-tree ; more intuitive and powerful undo/redo
        ))

;; The cl-lib library provides `cl-every', which is used in the code
;; for determining if any packages need to be installed.
(require 'cl-lib)

;;; The following code will install any packages specified in
;;; `radian-packages' that are not already installed. It will only
;;; call `package-refresh-contents' if it is necessary to install a
;;; new package. The main logic is based on [1].
;;;
;;; [1]: http://batsov.com/articles/2012/02/19/package-management-in-emacs-the-good-the-bad-and-the-ugly/

;; Since `package-archives', `package-pinned-packages', and
;; `package-installed-p' are not autoloaded, we must explicitly load
;; package.el before using any of them.
(require 'package)

;; Add package repositories. GNU is the default repository; MELPA is
;; necessary to get most of the packages we are interested in.
(setq package-archives
      '(("gnu" . "http://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")))

;; Initialize package.el. This is necessary to use
;; `package-installed-p'.
(package-initialize)

;; If any of the packages in `radian-packages' are not installed...
(unless (cl-every 'package-installed-p radian-packages)
  ;; ... then make sure that we know about the latest versions of all
  ;;     the packages...
  (package-refresh-contents)
  ;; ... and install any packages that aren't already installed.
  (dolist (package radian-packages)
    (unless (package-installed-p package)
      (package-install package))))

Keep your init.el file under version control and publish it to Github (or Dropbox, or ...). For example, you can see my repository here. When you switch to a new machine, clone the repository and link ~/.emacs.d/init.el to the file in the repository. This is what I do (except that the linking part is automated, more on that later).

Alternatively, you can clone the repository directly to your ~/.emacs.d folder. The disadvantage of this is that you have to make sure to put everything Emacs might create in your .gitignore.

Either way, with this code installed, you can just drop your init.el file on another machine and start Emacs. All of your packages will be installed automatically. Instead of using M-x package-install, you simply add the package name to the list defined in init.el and reload the file. Then the package is not only installed, but is guaranteed to always be installed on any machine where you are using that init.el file.

Alternatively, you can simply keep your entire ~/.emacs.d folder under version control (or at least most of it). This way you can have 100% control over your setup, instead of relying on the MELPA servers to be able to download packages when they need to be installed. The disadvantage of this is that you need to manually update all of your packages and create a commit every time you update anything.

You mention you have other .el files you've written yourself. Personally, I prefer to keep everything in a single file (namely, init.el). I think this makes things simpler. But if you have other files, then you can just keep these under version control as well, and you will get them when you clone from another machine.

Automate deployment on another machine

This is the overkill part. You can see my script here. Given an arbitrary OS X machine, it will install the correct versions of all dependencies, back up any existing dotfiles, and make sure everything is symlinked correctly.

I actually go a step farther, and separate my dotfiles into two groups: public and private. My public dotfiles are published on Github as Radian, and they are meant to be usage by a wide range of people with different preferences from me. I host my private dotfiles in Dropbox: these are for settings specific to me and my filesystem, weird hacks, and other random things.

To get started on a new machine, then, I install Dropbox, clone Radian, and run the setup script. I'll be prompted for the location of my private dotfiles folder, and everything else will be set up automatically. I can then hit the ground running immediately.