Portable emacs config without crazy load time

Imagine you can just use package-install to install any Emacs package and have a painless way to reinstall the same packages when you move to a new system.
usually what happens is when you install some package, Emacs modifies your custom-set-variables block to include the newly installed package in the package-selected-packages variable. But this does not anything special to install these packages on a new system (as far as I know). So when you are on a new system you still have to manually install all the packages present in this block to get your Emacs running without any issues.
So I wrote a script that I can just run on a new system which would read the package-selected-packages variable and install all the relevant packages for me.
Let's go over how to get this set up.
Step 1: have a separate custom-file
Add the following to your Emacs config to make sure the auto-generated custom-set-variables block is saved to a separate file.
(setq custom-file "~/.emacs.d/custom.el")
(load custom-file)
Step 2: Create the eslip script to do the package installation
Create a file called sync.el in your Emacs configuration folder with the following contents.
(require 'package)
(load-file "~/.emacs.d/custom.el")
(print package-selected-packages)
;; Add melpa
(add-to-list 'package-archives
'("melpa-stable" . "https://melpa.org/packages/") t)
(package-initialize)
;; refresh packages
(setq my/package-refreshed-p nil)
(defun my/refresh-contants ()
(unless my/package-refreshed-p
(progn
(package-refresh-contents)
(setq my/package-refreshed-p t))))
(dolist (pkg package-selected-packages)
(condition-case err
(progn
(let ((pkg-name (symbol-name pkg)))
(eval (if (package-installed-p pkg)
(print (concat pkg-name " already installed!"))
(progn
(print (concat "Installing: " pkg-name))
(my/refresh-contants)
(package-install pkg))))))
(error
(message "Failed to install package: %s. Error: %s" pkg err))))
This script will go through all the packages listed in package-selected-packages and install any that aren’t already on the system.
The key thing here: we don’t call this script during regular Emacs startup. That means package-refresh-contents is never called when Emacs is starting up normally. So no extra load times! You only run this sync.el script when setting up a new system or installing your packages. The rest of the time, Emacs stays fast as usual.
Step 3: Create a shell file to run the elisp script without launching Emacs
- Create an empty file with the following contents for example in ~/.emacs.d/sync
#!/bin/bash
emacs -q --script ~/.emacs.d/sync.el
- Make it executable:
$ sudo chmod +x ~/.emacs.d/sync
That’s it! Now, running ~/.emacs.d/sync will install any packages listed in your custom-set-variables block.
Conclusion
This technique is actually inspired by Doom Emacs. Alternatively, you could use use-package with :ensure t to handle package installation automatically. But honestly, I find that approach makes your config pretty beefy when you have a lot of packages. Adding a use-package block for everything feels clunky. And yeah, it might avoid the need for this script, but I like the simplicity of just listing the packages and not having to write a bunch of boilerplate.
This setup works perfectly for me—it's lightweight, avoids the usual slowdowns, and gets my Emacs ready in no time!





