Two IDEs, Chrome with too many tabs, and Steam updating a game in the background. That's a normal afternoon on my Lenovo LOQ, and it's exactly when the laptop starts to feel like it's wading through mud. The RAM meter sits at 90%. Clicking a tab takes a second. Switching back to the IDE takes three.
Nothing crashes. It just gets slow, and then slower.
The machine has 16 GB of RAM, one SODIMM, and an RTX 3050 with 6 GB of its own memory. That GPU memory was doing almost nothing. On this laptop the screen is driven by the Intel GPU, so unless I start a game, the NVIDIA card sits there holding a few hundred megabytes and waiting.
So I built USM, which makes that idle VRAM the first place Linux swaps to. The code is on GitHub, MIT licensed, in beta.
Why 90% Full Feels So Bad
Linux doesn't wait for RAM to hit 100%. Somewhere around 90% it starts swapping, moving memory pages nobody touched lately out to the disk. Every app that later touches one of those pages has to wait while it's read back.
On my laptop, the swap file lives on a Lexar NM620, a DRAM-less NVMe drive that was 84% full. A 4 KiB page comes back from it in roughly 55 µs plus fault overhead on a good day. On a bad day it's worse. While Steam was reading and writing 150 to 370 MB/s during an update, single reads from that drive took 1.3 to 2.4 seconds, and the whole desktop froze for 9.5 seconds. The swap and the update were fighting over the same disk, and everything else waited behind both of them.
You can't fix that with a faster SSD. Even a great one is hundreds of times slower than memory.
And buying more RAM stopped being the easy answer this year. AI data centers have been buying DRAM faster than factories make it, and prices for normal PCs doubled or tripled through 2026. OpenAI's CEO even said on a podcast that it's "quite terrible to have everybody have a bunch of the world's limited supply of DRAM sitting in their own computer", then renamed himself "Ram Altman" on X when the internet reacted. I dedicated the project's README to him.
What USM Actually Does
The VRAM tier is a small user-space daemon, usm-vramd. It borrows GPU memory through the NVIDIA driver's CUDA driver API (it loads libcuda.so.1 at run time, so no CUDA toolkit), and exposes it to the kernel as a block device through ublk, the kernel's user-space block driver. Then it's just a swap device with the highest priority:
$ cat /proc/swaps Filename Type Size Used Priority /swap.img file 17825788 583748 10 /swap-hibernate.img file 16777212 0 1 /dev/ublkb0 partition 17301500 7470364 200 That /dev/ublkb0 is 5.5 GiB of VRAM. It shows up as 16.5 GiB because the daemon compresses pages with lz4 before copying them to the GPU. Real app memory compresses well; mine is stored at about 35% of its size. At the moment I took that snapshot, it held 7.1 GiB of my apps' memory, none of it on the SSD.
The order memory goes in looks like this:
apps → RAM → zswap (10% of RAM) → GPU VRAM (lz4, 3×) → a swap file on the SSD zswap stays in front as a small cache because a page from zswap comes back in a few microseconds. The VRAM catches the rest. The swap file on the SSD is still there, only touched when everything above it is full.
No kernel module, no patches. It works with Secure Boot on, because ublk ships with the stock kernel.
The Numbers
Here's one page fault, measured through the real kernel path with every page verified. These ran on my second test laptop, a Legion with an RTX 4060:
| Where the page comes back from | Time per fault |
|---|---|
| zram (compressed RAM) | 2.0 µs |
| VRAM tier | 8.8 µs |
| NVMe swap file | ~55 µs + fault overhead |
zram is faster per page. I'm not going to pretend otherwise. But zram keeps its compressed pages in RAM, which is the thing you're short of, and it burns CPU compressing every page on the way out. Under real overload that difference shows up. I ran 8 apps of 1.5 GiB each on 8 GiB of RAM, with USM's guard on in both cases:
- with zram, 3 of 6 runs "hung" (a login took longer than 10 seconds), and the desktop's worst response was 75 to 320 ms
- with the VRAM tier, 0 of 4 runs hung, and the worst response was 0.5 to 61 ms
On my own laptop a quick benchmark run gave 11 µs per fault and about 370,000 faults per second with 8 threads. A 512 MiB app pushed out to swap came back in 0.6 seconds.
The Guard, and What I Got Wrong First
Fast swap helps, but under heavy overload Linux still thrashes. So USM also has a guard. It watches memory pressure (PSI) and, when apps really stall, briefly freezes one busy background app with the cgroup freezer so everything else can breathe.
My first version was bad at this. It treated bursts of page faults as overload, which they aren't when swap is fast. On the first real afternoon it paused Chrome 10 times in 6 minutes, every time I switched to the IDE. One pause lasted over 3 minutes with 9 GB of RAM free. I had to switch it off to get work done.
The rewrite only reacts to a real stall: apps waiting on memory at least 30% of the time for 3 seconds straight. A pause ends once pressure settles, 20 seconds at most. The app you're using, anything you used in the last 5 minutes, anything playing sound, and terminals, editors and games are never touched. Switch to a paused app and it runs again within half a second. When I replayed that afternoon's logs against the new rules, they would have paused nothing.
The guard also knows when it can't help. If the disk is the bottleneck (that Steam update), it stays out of the way.
Giving the VRAM Back
Sometimes you want the GPU. Before a game:
usm off # VRAM is free in seconds usm on # afterwards There's also a toggle in GNOME's Quick Settings. The interesting case is switching off when RAM is already full. A plain swapoff has to pull every page into RAM first, which is the one place there's no room. In a VM test with a slow disk, that froze the machine for 298 seconds. USM's daemon "spills" instead. It copies its data straight from VRAM to a file on disk while swapping keeps working, releases the VRAM, and moves the data back into RAM later. Same test: 36 seconds, and the desktop's worst stall was 1.1 ms.
Suspend and hibernate needed the same care. Linux freezes every program before sleeping, the daemon included, and a page stuck in VRAM can't be read by a frozen daemon. So a systemd hook empties the tier and switches it off before sleep, and turns it back on after waking. If it can't empty it, the sleep is refused rather than risking a hang. There's another project, nbd-vram, that also uses NVIDIA VRAM as swap, over NBD and a Unix socket. Its README warns that suspend can hang the machine. That's the exact failure this hook exists for.
When NOT to Use This
Don't install it on anything critical. It's a beta.
If the daemon crashes or the GPU driver resets, the pages in VRAM are gone, and the apps they belonged to crash. It's the same as losing a swap disk. The daemon is hardened against it: the OOM killer can't pick it, its memory is locked, it never waits on its own swap, and it refuses to stop while in use. It's also brand new.
Here's what brand new looked like in practice. The first beta had a bug that could freeze the whole machine. When the VRAM filled up in compressed mode, the daemon wrote the overflow to a file itself, while it was serving swap, and under heavy memory pressure that write could wait forever for memory that only swap could free. My own release test found it the same day. Beta 2 fixes it with a standby swap file that Linux writes to itself whenever the VRAM is nearly full, so the daemon never waits on the disk while the kernel depends on it.
Skip it, or switch it off, if:
- you game, render or run models on the GPU most of the day (that VRAM isn't idle for you)
- battery life matters more than speed: with the tier on, the GPU stays awake and idles around 3 W instead of sleeping
- you don't have an NVIDIA GPU. AMD and Intel aren't supported yet. A Vulkan backend is the plan, and help is welcome
- you need hibernation to be bulletproof. Suspend is tested on real hardware, hibernation so far only in a VM
One more thing, since it matters for trust. Most of USM's code was written by Claude Code, an AI coding agent, while I set the goals, ran it on my own laptop every day and decided what stayed. The README says so too. Read it like code from a new contributor, and report what breaks.
Trying It
On a recent Ubuntu with the NVIDIA driver installed (I run 26.04; 24.04 should work but isn't tested yet), this installs the newest release:
V=$(curl -fsSL "https://api.github.com/repos/kha333n/usm/releases?per_page=1" | grep '"tag_name"' | cut -d'"' -f4) curl -fLO https://github.com/kha333n/usm/releases/download/$V/usm-${V#v}-linux-x86_64.tar.gz tar xzf usm-${V#v}-linux-x86_64.tar.gz && cd usm-${V#v}-linux-x86_64 sudo ./install.sh usm status The first line asks GitHub for the newest version, so these commands stay right as new versions come out. To pick a version yourself, the releases page lists them all.
usm check tells you what will work on your machine before you commit to anything, and sudo usm uninstall puts everything back. If you try it, I'd love a test report, especially from other GPUs and distributions. The repo has a benchmark script that compares your machine with and without USM in about 10 minutes.
The One-Paragraph Version
When a Linux laptop runs low on RAM, it slows to a crawl long before anything crashes, because swap lives on a disk that is hundreds of times slower than memory. If that laptop has an NVIDIA GPU sitting idle, its VRAM makes a far better swap device: about 9 µs per page instead of 55 µs and more, compressed to fit roughly three times its size, and handed back in seconds when you want to game. That's USM. It isn't more RAM, but when you can't buy more, it's the next best thing.
This post first appeared on my blog.