Fixing Linux Freezes During Steam Downloads
Steam hits full speed and the whole Linux desktop locks up. A root-cause walkthrough of dirty page writeback, dm-crypt workqueues and QLC SSD cache exhaustion — and the exact sysctl and cryptsetup fixes.
BY ASHLIN DARIUS GOVINDASAMY
The symptom is brutal and easy to describe: start a large Steam download on Linux, watch it climb to 600–700 Mbps, and a minute later the entire desktop stops responding. The mouse lags, windows grey out, GNOME reports "your system is too slow", and the only way out is the power button. Nothing is logged as an error. The SSD is healthy. The CPU is mostly idle.
This happened on an Intel i7-13620H laptop with 16 GB of RAM, a 512 GB QLC NVMe drive and full-disk encryption (LUKS2 on top of LVM) running Ubuntu with a 7.x kernel. It is not a Steam bug. It is the Linux storage stack doing exactly what it was configured to do — and the default configuration is wrong for this hardware.
Step 1: prove it is I/O, not CPU or RAM
Before tuning anything, measure. Linux exposes Pressure Stall Information (PSI) in /proc/pressure. The "full" line tells you the percentage of time in which every runnable task was stalled waiting on a resource. On a responsive desktop it sits near zero.
cat /proc/pressure/io /proc/pressure/cpu /proc/pressure/memory
# During the download:
# io full avg10=7.06 avg60=38.36 avg300=18.80
# cpu full avg10=0.00 avg60=0.00 avg300=0.00
# memory full avg10=0.00 avg60=0.00 avg300=0.00That is the whole diagnosis in three lines. For 38% of the previous minute, the entire system was blocked on I/O. CPU and memory pressure were zero. The kernel log had no NVMe resets, timeouts or hung-task warnings, and the drive's own average wait was a few milliseconds — so the hardware was not failing. Something between the application and the drive was serialising writes and starving everything else.
The three bottlenecks stacked on top of each other
- Dirty page writeback. By default vm.dirty_ratio=20 lets roughly 20% of RAM (~3 GB here) fill with unwritten data before the kernel forces writers to stop and flush. A download at ~80 MB/s fills that in under a minute, then everything that touches the disk waits behind a multi-gigabyte flush.
- dm-crypt workqueues. Every write to a LUKS volume is encrypted, and by default dm-crypt hands that work off to kernel workqueues. On fast NVMe this extra queueing adds latency and lets encryption work pile up behind bulk writes, so small interactive I/O from the desktop waits in line behind Steam.
- QLC SSD cache exhaustion. QLC drives write fast into an SLC cache, then fall to a fraction of that speed once the cache is full. With the filesystem at 92% capacity, the dynamic SLC cache is tiny, so sustained writes hit the slow path almost immediately.
None of these is a bug on its own. Combined, they turn a 20 GB download into a system-wide freeze.
Fix 1: cap dirty memory in bytes, not percent
Percent-based dirty limits scale with RAM, which is exactly backwards for desktops with slow-to-flush storage. Switching to absolute byte limits makes the kernel start background writeback early and keeps any single flush small. Setting the *_bytes variants automatically zeroes the *_ratio variants.
# Start background writeback at 64 MB, block writers at 256 MB
vm.dirty_background_bytes = 67108864
vm.dirty_bytes = 268435456
# Apply without rebooting:
sudo sysctl --systemFix 2: disable dm-crypt workqueues on NVMe
Since Linux 5.9, dm-crypt supports no_read_workqueue and no_write_workqueue, which perform encryption inline instead of bouncing every bio through a workqueue. Cloudflare upstreamed this after measuring the latency cost on their own NVMe fleet. With cryptsetup 2.3.4+ and LUKS2 you can enable both on a live, mounted root volume and persist them in the LUKS header — no initramfs changes required.
# Find the mapping name (e.g. dm_crypt-0, luks-<uuid>, nvme0n1p3_crypt)
lsblk -o NAME,TYPE | grep crypt
# Apply now and store the flags in the LUKS2 header (prompts for passphrase)
sudo cryptsetup refresh \
--perf-no_read_workqueue --perf-no_write_workqueue \
--persistent dm_crypt-0
# Verify: both the live table and the header should show the flags
sudo dmsetup table dm_crypt-0 | grep -o 'no_.*workqueue.*'
sudo cryptsetup luksDump /dev/nvme0n1p3 | grep -i flagsA note on why the passphrase is needed: the volume key lives in the kernel keyring as a logon key that userspace cannot read back, so a raw dmsetup reload fails with "Required key not available". cryptsetup refresh re-derives the key from your passphrase and swaps the table atomically.
Fix 3: give the SSD room to breathe
On QLC and many TLC drives, the SLC write cache is carved out of free space. A nearly full drive has almost no cache left, so sustained writes collapse to native QLC speed. Keeping at least 15–20% of the drive free, and making sure fstrim.timer is enabled so the controller knows which blocks are free, is a hardware fix you cannot tune your way around.
df -h /
systemctl is-enabled fstrim.timer # should print: enabled
sudo fstrim -av # run once nowWatching it happen: a tiny I/O monitor
Freezes are hard to debug because the tool you would use to look at them is frozen too. The answer is a background logger that writes one line per second — disk write MB/s, dirty memory, I/O full-stall percentage, CPU iowait and NVMe temperature from hwmon — so that after a hard reboot you can read the last two minutes before the hang. All of it comes straight from /proc/diskstats, /proc/meminfo, /proc/pressure and /sys/class/hwmon; no extra packages.
time write_MBs dirty_MB io_full_pct nvme_temp_C
00:26:40 82.1 256.0 4.2 58.9 # healthy: dirty capped, low stall
00:26:41 79.6 255.8 3.9 59.4
# Bad signs:
# dirty_MB climbing into the thousands -> writeback limits not applied
# io_full_pct > 30 while write_MBs drops -> SSD cache exhausted / encryption queueing
# nvme_temp_C > 70 and falling MB/s -> thermal throttlingIf it still stutters
- Throttle the download in Steam (Settings → Downloads → Limit download speed) to just below the drive's sustained post-cache write speed.
- Move the Steam library to a second drive that is not encrypted or not the root filesystem, so desktop I/O never queues behind game data.
- Check NVMe temperature under load; laptop M.2 slots without a heatsink commonly throttle QLC drives.
- Try the BFQ I/O scheduler on the NVMe device for better interactive fairness at some CPU cost: echo bfq | sudo tee /sys/block/nvme0n1/queue/scheduler.
The general lesson applies far beyond Steam: any large sequential write — a Docker image pull, a VM disk copy, a dataset download — can freeze an encrypted Linux desktop with default settings. Measure with PSI first, then fix the layer that is actually stalling.
Frequently asked questions
Why does my whole Linux system freeze when Steam downloads at full speed?
Usually it is not the network or the CPU but disk writeback. Linux buffers gigabytes of downloaded data in RAM, then flushes it in one burst; on encrypted or QLC NVMe drives that flush blocks every other process waiting on the disk. Check /proc/pressure/io — high "full" values during the download confirm it.
What are good vm.dirty_bytes values for a desktop?
A common, safe starting point is vm.dirty_background_bytes=67108864 (64 MB) and vm.dirty_bytes=268435456 (256 MB). This keeps flushes small so the desktop stays responsive, at a negligible cost to throughput on NVMe.
Is it safe to enable no_read_workqueue and no_write_workqueue on LUKS?
Yes. They only change where encryption work runs, not the encryption itself or the on-disk format. They require Linux 5.9+ and, to persist in the header, LUKS2 with cryptsetup 2.3.4+. They can be removed again with cryptsetup refresh --persistent without the flags.
Does a full SSD really make writes slower?
On QLC and most consumer TLC drives, yes. The fast SLC write cache is dynamically sized from free space, so a drive above roughly 85–90% full has very little cache and sustained writes drop to native NAND speed.
Will limiting the Steam download speed fix the freeze?
It hides the symptom by keeping writes below what the drive can sustain. Fixing writeback limits and dm-crypt queueing addresses the cause, so you keep full download speed without the freeze.
NEWSLETTER
Get the engineering notes in your inbox.
Kernels, GPUs, networks, AI infrastructure — and the things we broke along the way. No spam, one click to leave.
We store only your email address, to send you new articles. See our privacy policy.
TAGS
RELATED
Writing a Linux Character Driver
A field guide to the character-device interface: registering a device, wiring up file_operations, and talking to it from userspace.
Running Local LLM Infrastructure
What changes when inference moves off a hosted API and onto GPUs you control — model serving, memory budgeting and the operational tradeoffs.