I recently converted all my machines from zram swap to zswap. In this post I go over the differences between the two and why zswap is almost certainly better for any general use-case.
Contents ↑ top ↑
Prior work
This excellent post by Chris Down is what got me to change the zram swap setup I’ve been using for 5 years on both desktops and servers. Specifically, the discussion about behaviour under memory pressure buried in the middle of the post is what convinced me.
That said, I think the above post errs into being too technical. I suspect the author was trying to be comprehensive in order to establish ethos and preempt questions, but the result is an argument cluttered by details.
I am going to make the same argument here—that basically everybody should use zswap instead of zram swap—using simpler language and glossing over technical nuances. Read Chris Down’s post for the nitty gritty details.
Linux memory
Linux has several kinds of memory:
- Free memory. This is unused memory that can be allocated to programs right now.
- Anonymous/program memory. This is memory allocated by programs and the kernel on the stack and on the heap. This is what people mean by Used memory.
-
Cache. These are bytes that apps have recently read from disk, bytes that are buffered before being written to disk, but also the executable code of programs and
mmaps. So, basically everything for which a copy exists on the disk. The cache can be “evicted” to free up memory. - Swap. These are pages of Anonymous/program memory that the kernel decided aren’t being used at the moment, so it has “swapped” them out. If the system has a swap partition or swap file, swapping out means writing to disk and swapping in means reading from disk. If the system has
zramswap, then swapping just means (de)compressing pages in memory.
We can see the different kinds of memory graphically in a system monitor like btop:

btop showing 30.9 GiB of main memory and 24.2 GiB of swap. Of the main memory, 9.8 GiB are used, 21.1 GiB are available, 9.5 GiB are cached, and 10.3 GiB are free.Of swap, 7.6 GiB are used and 16.6 GiB are free.
The above is a screenshot from my laptop which has been running for 3 days. The important observation is that swap is being used even though the system has plenty of free memory.
We can see what processes are currently being swapped with smem:
$ smem --autosize -k -c "swap command" -s swap -r | sed -e 's#/nix/store/[^/]*/##' | head -n 20
Swap Command
2.8G bin/rust-analyzer
418.0M hx
193.9M usr/lib/zotero-bin-7.0.27/zotero-bin -app /nix/store/8i6idm7gf4wccmna246a004b8q7wa3z5-zotero-7.0.27/usr/lib/zotero-bin-7.0.27/app
177.0M /run/current-system/sw/bin/harper-ls --stdio
162.1M bin/node /nix/store/5kfn1vqwxr11micrqxw2klvl0bz6f9zg-tailwindcss-language-server-0.14.28/bin/tailwindcss-language-server --stdio
161.6M /run/current-system/sw/bin/firefox
158.8M bin/.thunderbird-wrapped_ --name thunderbird
150.8M /run/current-system/sw/bin/birdtray
135.8M ./target/release/dis dev
118.6M bin/elisa
116.0M libexec/electron/electron /nix/store/wfjrp90gxm2jcfq0vwbza81lywwjs6wq-signal-desktop-8.6.1/share/signal-desktop/app.a
115.1M opt/Discord/.Discord-wrapped --type=zygote --no-zygote-sandbox
100.0M bin/plasmashell --no-respawn
98.6M lib/marksman/marksman server
91.9M /proc/self/exe --type=renderer --crashpad-handler-pid=3340 --enable-crash-reporter=c8233cca-57ab-49cf-a7c9-87ac7d5730bc,no_channel --user-data-dir=/home/scvalex/.config/discord --standard
81.7M /run/current-system/sw/bin/neochat
79.5M bin/tailwindcss --minify --no-autoprefixer -o styles_out/r/screen3.css -i styles/screen3.css --watch
61.5M lib/firefox/firefox -contentproc -isForBrowser -prefsHandle 0:36709 -prefMapHandle 1:294101 -jsInitHandle 2:156120 -parentBuildID
61.1M /run/current-system/sw/bin/Discord --enable-speech-dispatcher
Half the swap usage comes from my editor (Helix, rust-analyzer, TailwindCSS, marksman, and harper-ls). I think what’s going on is that when I opened my blog to write this post, I also opened some Rust source file. This triggered rust-analyzer to load all the type information into memory, but since I’m not doing any Rust coding, that memory was never touched again, the kernel noticed, so it swapped it out.
This is the behaviour we want. There’s no point in keeping stuff in Used memory if it isn’t being actively used. It’s better to swap out cold pages, so that the system has more Free memory it could use for other programs or for Cache. The last bit is particularly important. Since disk IO is 1000x slower than memory, it’s better to fill memory with disk cache than with cold program data.
The above is true under normal circumstances, but it’s even more important if the system is under memory pressure. If I started a big build that needed 15 GB of memory, the kernel would first use the 10 GB of Free memory, then evict 5 GB of Cache to make room.
However, if I didn’t have any Swap, then I’d only have 3 GB of Free, so the kernel would evict the entire 10 GB of Cache, then OOM kill another 2 GB of programs. Having programs OOM’d is bad, but not having any Cache and forcing the build to fully write every build artifact to disk only for it to be immediately read back is worse.
When under memory pressure, something is going to get evicted to disk. It’s better to swap out cold program memory than to reduce disk cache.
If you’re like me and remember the 2000’s when memory was scarce and “swapping” meant that your system would crawl to a halt while the disk made angry noises, the world has changed. Running out of memory and putting the kernel in a situation where it has to swap out the big process that’s using 100% of the CPU is still bad, but that’s not what happens most of the time. Usually, the kernel will be swapping out massive Electron apps like Signal and Discord which slowly leak memory or programs that haven’t been touched in a while. Also, SSD’s are 20x faster than spinning disks, so the penalty for swapping is a lot lower. Also also, disk space is really cheap now, so there’s genuinely no reason not to just give the system a few tens of gigs of swap.
zram swap
We’ve established that swap is good, but what kind of swap is better? Let’s consider swap on zram first. To quote the kernel docs:
The zram module creates RAM-based block devices named /dev/zramN (N = 0, 1, …). Pages written to these disks are compressed and stored in memory itself. These disks allow very fast I/O and compression provides good amounts of memory savings. Some of the use cases include
/tmpstorage, use as swap disks, various caches under/varand maybe many more. :)
To use normal swap, we have a swap partition /dev/sda2 and we call swapon /dev/sda2 to activate it. To use swap on zram, we first create a /dev/zram0 device with modprobe zram, then we call swapon /dev/zram0.
Swap on zram is the closest real thing to the Download More RAM joke. However, zram is not RAM because programs can’t use it directly. As in, there’s no way to run Firefox directly from zram. Firefox always runs from normal memory, but just like how some of its pages can be swapped out to disk, they can also be swapped out to a different part of memory which happens to be compressed. The pages then need to be copied and decompressed if Firefox accesses them.
The benefits of swap on zram over swap on disk is that zram is much faster since it’s in memory and is always available since we don’t need a special partition or file. But being fast isn’t very useful because this memory is by definition infrequently accessed. For example, being able to swap in my dormant 3 GB rust-analyzer half a second faster isn’t really valuable to me. And not needing a pre-existing file or partition is a feature, but we can just create the file or partition in most cases.
The big downside of swap on zram is that it still uses memory. Assuming an optimistic compression ratio of 2:1, my laptop would now be using 4 GB more memory with zram. That’s paying 12% of my total memory for benefits I don’t find particularly compelling.
So, just because swap is good, swap on zram is better than no swap, but we can easily do better with swap on disk.
zswap
As we’ve seen, swap on zram has gotchas. By contrast, zswap is just normal swap fronted by a compressed in-memory cache. To quote the kernel docs:
Zswapis a lightweight compressed cache for swap pages. It takes pages that are in the process of being swapped out and attempts to compress them into a dynamically allocated RAM-based memory pool.Zswapbasically trades CPU cycles for potentially reduced swap I/O. This trade-off can also result in a significant performance improvement if reads from the compressed cache are faster than reads from a swap device.
This is just the swap on zram trick, except the kernel knows that the compressed things are swap pages, so it can do specialized logic like evict the pages to disk when they’ve been cold for long enough.
In other words, zswap implements a proper memory hierarchy:
- Frequently accessed pages are stored in RAM,
- Infrequently accessed pages are stored in
zswap‘s’ compressed RAM, and - Cold pages are swapped to disk.
So, zswap has the benefits of both swap on disk and swap on zram. When we have swap on disk, we should enable zswap as well.
Conclusion
TL;DR For any general use-case, have a swap partition or swap file and enable zswap. Don’t bother with swap on zram unless you really can’t have swap on disk.
I’ve been saying “general use-case” in this post, but I think it’s basically every use-case in 2026. The usual example where swap on zram is better is an embedded device running on flash memory that degrades quickly if thrashed. As Chris Down’s post points out, you can still get disk thrashing from the cache being evicted under memory pressure, so zram is not a panacea. I would add that the Raspberry Pi 5 has been out since 2023 and has NVMe support, so I don’t think the “crappy flash memory” use-case is going to be a concern for much longer even in the embedded space.