Sitemap

How I Fixed Emacs Performance Issues on Raspberry Pi Zero 2 or a weak PC.

3 min readMay 9, 2025

I frequently use various Raspberry Pi models for my work. The Raspberry Pi Zero 2 is an excellent choice for many tasks — a tiny microcomputer roughly half the size of a credit card. I also rely on Emacs, which is far more than a text editor. It’s like a Swiss Army knife that provides a simple yet powerful development environment combined with UNIX philosophy practices. That’s why I often run it on devices like these.
However, starting with Emacs 28, I encountered issues on the Raspberry Pi Zero 2: Emacs became extremely slow, consumed 100% CPU, and eventually caused the device to freeze completely.

Emacs native compilation fully loads the CPU, causing the Raspberry Pi Zero 2 to freeze.

The issue was noticeable but less severe on more powerful models, like my Raspberry Pi 5.

The Culprit.

The problem was traced to the following process:

emacs -no-comp-spawn -Q --batch --eval '(setq w32-disable-abort-dialog t)' -l /tmp/emacs-async-comp-org.el

Emacs’ core is written in C, but much of its functionality is implemented in Emacs Lisp. Starting with version 28, Emacs introduced the ability to compile Lisp code into native machine code for better performance. To avoid compiling everything at once, Emacs compiles only what’s needed on demand if pre-compiled modules are missing. The process above handles this compilation. During this time, two processes run: the main Emacs instance (where you edit code) and the compilation process it spawns.

Solutions.

There are two ways to address this issue:
1. Disable Native Compilation
This can be done before loading the main configuration by adding the following line to ~/.emacs.d/early-init.el:

(setq native-comp-deferred-compilation nil) ;; Disables automatic native compilation

Then, restart Emacs. The downside is that Emacs will still compile Lisp code into bytecode during interpretation. While bytecode compilation is fast, it results in slower performance compared to native compilation, which is particularly noticeable on a low-powered device like the Raspberry Pi Zero 2. For this reason, I opted for the second approach.

2. Allow Emacs to Compile Everything Natively
I couldn’t find a straightforward single command to achieve this. There is an option using native-compile-async:

(mapc (lambda (dir)
(when (file-directory-p dir)
(native-compile-async dir 'recursively)))
load-path)

This can be run for all directories in load-path. However, I didn’t like this approach for two reasons. First, it doesn’t always compile everything needed. Second, and more importantly, it compiles my custom modifications, which I frequently update, and I wanted to avoid discrepancies between my edits and the compiled code. So, I decided to let Emacs handle the compilation itself.
The challenge was that compiling org-mode (which I use often) caused the Zero 2 to freeze. This happens because the Zero 2 has only 512 MB of RAM and, by default, just 200 MB of swap. When compilation exhausts memory, the device crashes. To fix this, increase the swap size to 1024 MB (512 MB is insufficient) and gradually open the modes you use frequently while monitoring CPU usage.
For example, I started by opening a C file, letting Emacs compile everything related to that mode, and waiting for the compilation to finish. Then, I opened an Org file and waited again. I repeated this for magit, the package manager, js-mode, python-mode, and so on.
Compiled files are stored in `~/.emacs.d/eln-cache/emacs-version/`. I have over 300 .eln files in mine. If you update a package, Emacs recompiles that package and any uncompiled dependencies. If you update Emacs itself, it recompiles everything for the new version.

Is It Worth the Effort?

In my opinion, yes. The natively compiled version of Emacs runs noticeably faster and smoother. Some delays persist, mainly due to SD card access, but the improvement is significant.

--

--

No responses yet