Most modern Linux distributions configure virtual memory management for resource-constrained hardware. Modern releases—including Fedora 44, 45, or newer, alongside Ubuntu 24.04 LTS, 26.04 LTS, and Linux Mint 22/23—default to compressed in-RAM swap (zram-generator) or an aggressive swappiness value of 60. On modern workstations and development environments equipped with physical RAM and high-speed NVMe or SSD storage, these defaults introduce unwanted latency:
- zRAM consumes uncompressed RAM: Compressing memory on the fly trades CPU cycles and physical RAM capacity for a volatile swap layer that cannot support disk-based hibernation.
- Aggressive Swappiness (
vm.swappiness = 60): The kernel begins paging anonymous process memory out to disk or compressed blocks long before physical RAM is exhausted, causing background applications to stall when refocused. - Under-tuned VFS Cache Pressure (
vm.vfs_cache_pressure = 100): The Linux Virtual File System (VFS) reclaims cached directory and inode structures at a balanced rate relative to page cache. For users requiring maximum physical memory for running applications, directory and inode metadata linger in RAM longer than necessary. - Volatile Dirty Page Accumulation: In the Linux virtual memory subsystem, “dirty” memory refers to modified data held in RAM that has not yet been committed to persistent disk storage. By default, the kernel allows dirty pages to linger for up to 30 seconds and accumulate up to 20% of total RAM before flushing, tying up memory that could otherwise serve active workloads.
- Speculative Read Bloat: Stock readahead parameters aggressively pre-fetch sequential file data into the page cache ahead of application requests. On high-speed NVMe drives, completely zeroing readahead eliminates unrequested speculative caching entirely.
This guide details purging distribution zRAM generators, creating properly structured swap files on both ext4 and Btrfs, deploying an automated dynamic sweep to eliminate conflicting drop-in configuration fragments, setting NVMe readahead to zero, and enforcing deterministic, byte-bounded virtual memory limits across Fedora, Ubuntu, Linux Mint, Arch Linux, and Debian.
Execution Note: Unless otherwise noted, run all system setup and configuration commands with elevated privileges (as
rootor prefixed withsudo).
1. The Kernel Optimization Architecture
Adjusting /etc/sysctl.d/ directs the Linux kernel memory subsystem to prioritize running applications in physical RAM, reclaim metadata caches immediately, and flush dirty data directly to fast NVMe storage using deterministic byte boundaries:
| Kernel Parameter | Distribution Default | Tuned Target | Operational Impact |
vm.swappiness | 60 | 1 | Prohibits swapping until available memory is virtually depleted. Physical RAM is fully prioritized; swap serves exclusively as a safety net against out-of-memory (OOM) kernel panics. |
vm.vfs_cache_pressure | 100 | 1000 | Aggressively reclaims cached dentry and inode objects from memory, freeing memory capacity for application workloads. |
vm.dirty_background_bytes | 0 (ratio active) | 33554432 (32 MB) | Fixed NVMe Target: Wakes kernel background flusher threads the moment 32 MB of modified, unsaved data accumulates in RAM. Automatically unsets dirty_background_ratio to 0. |
vm.dirty_bytes | 0 (ratio active) | 67108864 (64 MB) | Fixed NVMe Target: Hard synchronous throttle ceiling. Caps dirty memory at 64 MB, forcing processes to flush directly to NVMe storage to keep the RAM cache footprint minimal. Automatically unsets dirty_ratio to 0. |
vm.dirty_background_ratio | 10 | 0 (Unset) | Automatically zeroed by the kernel when dirty_background_bytes is assigned. |
vm.dirty_ratio | 20 | 0 (Unset) | Automatically zeroed by the kernel when dirty_bytes is assigned. |
vm.dirty_expire_centisecs | 3000 (30 sec) | 300 (3 sec) | Limits dirty page residency in RAM to a maximum of 3 seconds before marking them eligible for immediate disk writeout. |
vm.dirty_writeback_centisecs | 500 (5 sec) | 200 (2 sec) | Wakes kernel writeback flusher threads every 2 seconds to purge expired dirty pages. |
fs.file-max | Dynamic (~800k–2M) | 2000000 | Sets an explicit high ceiling of 2,000,000 concurrent file descriptors for containers, IDEs, and heavy local multitasking. |
queue/read_ahead_kb | 128 – 512 KB | 0 | Completely disables speculative read-ahead caching on NVMe storage. |
Hardware Requirement: Setting explicit writeback thresholds to 32 MB background and 64 MB ceiling is an aggressive profile engineered strictly for fast SSD and PCIe NVMe storage. Never use these tight byte limits on mechanical hard drives (HDDs) or low-speed USB flash media, as synchronous write throttling will induce noticeable desktop stalls.
2. Step 1: Remove zRAM Generators Across Distros
Distributions like Fedora ship zram-generator active out of the box, creating a /dev/zram0 block device that claims 50% to 100% of physical memory as compressed swap. Modern Ubuntu and Linux Mint releases also pull in zram-tools or distribution generators.
Before creating an on-disk swap file, disable and mask these generators.
A. Fedora (44, 45, and Newer)
Bash
# Disable active zRAM devices
sudo swapoff /dev/zram0 2>/dev/null || true
# Stop and mask systemd-zram-setup units
sudo systemctl stop [email protected] 2>/dev/null || true
sudo systemctl mask [email protected] 2>/dev/null || true
# Override zram-generator configuration
sudo mkdir -p /etc/systemd/zram-generator.conf.d
sudo tee /etc/systemd/zram-generator.conf.d/disable.conf > /dev/null << 'EOF'
# Completely disable automatic zRAM generation
EOF
# Remove generator packages via dnf5 / dnf
sudo dnf remove -y zram-generator zram-generator-defaults 2>/dev/null || true
B. Ubuntu (24.04, 26.04, and Later) & Linux Mint (22, 23, and Later)
Bash
# Disable active zRAM devices
sudo swapoff /dev/zram0 2>/dev/null || true
# Stop, disable, and purge zram configurations
sudo systemctl stop zramswap.service 2>/dev/null || true
sudo systemctl disable zramswap.service 2>/dev/null || true
sudo apt purge -y zram-config zram-tools 2>/dev/null || true
C. Arch Linux
Bash
# Disable active zRAM
sudo swapoff /dev/zram0 2>/dev/null || true
sudo systemctl disable --now zram-generator.service 2>/dev/null || true
sudo pacman -Rns --noconfirm zram-generator 2>/dev/null || true
Verify that zRAM is completely removed:
Bash
lsblk | grep zram
swapon --show
Verification: If lsblk prints no output and swapon --show returns empty, zRAM has been completely deactivated.
3. Step 2: Create a Dedicated NVMe/SSD Swap File
Select the storage configuration corresponding to your root partition (ext4 or Btrfs). Confirm your root filesystem type with:
Bash
findmnt -no FSTYPE /
Method A: Standard ext4 Filesystem
Allocating a swap file on ext4 requires preallocating contiguous disk blocks using fallocate.
Bash
SWAP_SIZE="16G"
echo "==> Creating ${SWAP_SIZE} swapfile on ext4..."
sudo fallocate -l "$SWAP_SIZE" /swapfile
# Lock down permissions (mandatory: must only be readable by root)
sudo chmod 600 /swapfile
# Format as swap area
sudo mkswap /swapfile
# Activate swap immediately
sudo swapon /swapfile
# Make swap persistent across reboots in /etc/fstab
if ! grep -q '/swapfile' /etc/fstab; then
echo '/swapfile none swap defaults 0 0' | sudo tee -a /etc/fstab
fi
Method B: Btrfs Filesystem (Fedora & Modern Ubuntu/Mint Default Installs)
CRITICAL WARNING FOR BTRFS: You CANNOT run standard
fallocateormkswapinside a copy-on-write (CoW) Btrfs directory. Doing so causes filesystem errors, fragmentation, and kernel activation refusal (swapon: /swapfile: swapon failed: Invalid argument).On Btrfs, swap files require a dedicated directory or subvolume with zero Copy-on-Write (
+C) attributes and no compression.
Bash
SWAP_SIZE="16G"
# 1. Create a dedicated directory on the root Btrfs volume
sudo mkdir -p /swap
# 2. Disable Copy-on-Write (CoW) on the directory
sudo chattr +C /swap
# 3. Use btrfs filesystem mkswapfile (handles non-CoW, no-compression, zeroing automatically)
if command -v btrfs &>/dev/null && btrfs filesystem mkswapfile --help &>/dev/null; then
echo "==> Utilizing native btrfs mkswapfile..."
sudo btrfs filesystem mkswapfile --size "$SWAP_SIZE" --uuid clear /swap/swapfile
else
echo "==> Manual Btrfs allocation fallback..."
sudo truncate -s 0 /swap/swapfile
sudo chattr +C /swap/swapfile
sudo btrfs property set /swap/swapfile compression none 2>/dev/null || true
sudo dd if=/dev/zero of=/swap/swapfile bs=1M count=16384 status=progress
sudo chmod 600 /swap/swapfile
sudo mkswap /swap/swapfile
fi
# 4. Activate the swap file
sudo swapon /swap/swapfile
# 5. Make persistent in /etc/fstab
if ! grep -q '/swap/swapfile' /etc/fstab; then
echo '/swap/swapfile none swap defaults 0 0' | sudo tee -a /etc/fstab
fi
4. Step 3: Dynamic Conflict Purge, Daemon Neutralization & VM Tuning
Because systemd-sysctl reads files in strict alphabetical order across /etc/sysctl.d/, leftover single-purpose fragments (such as 99-dirty.conf, 99-swappiness.conf, or 99-vfs.conf) will overwrite custom parameters if they evaluate later in the alphabet. Furthermore, daemon services like Fedora’s tuned enforce runtime profiles that overwrite parameters dynamically.
A. Disable Fedora’s tuned Profile Daemon
Bash
# If tuned is active, disable and mask it to prevent runtime sysctl overrides
if systemctl is-active tuned &>/dev/null; then
sudo systemctl stop tuned
sudo systemctl disable tuned
sudo systemctl mask tuned
fi
B. Automated Dynamic Sweep: Scan and Purge Conflicting Drop-Ins
This script scans /etc/sysctl.d/ and /etc/sysctl.conf dynamically, purges rogue drop-ins while protecting your master configuration file, and comments out matching legacy lines in /etc/sysctl.conf:
Bash
sudo bash -c '
TARGET_FILE="/etc/sysctl.d/99-performance-memory-tuning.conf"
PATTERN="swappiness|vfs_cache_pressure|dirty_"
echo "==> Scanning /etc/sysctl.d/ and /etc/sysctl.conf for conflicting VM settings..."
# 1. Find all matching files in /etc/sysctl.d/ except the master configuration
CONFLICTING_FILES=$(grep -rlE "$PATTERN" /etc/sysctl.d/ 2>/dev/null | grep -vF "$TARGET_FILE" || true)
if [ -n "$CONFLICTING_FILES" ]; then
echo "Found conflicting drop-in files to purge:"
echo "$CONFLICTING_FILES"
echo "$CONFLICTING_FILES" | xargs -r rm -v
else
echo "No rogue drop-in files found in /etc/sysctl.d/."
fi
# 2. Clean up /etc/sysctl.conf if directives exist directly inside it
if [ -f /etc/sysctl.conf ] && grep -qE "$PATTERN" /etc/sysctl.conf; then
echo "==> Commenting out conflicting legacy entries inside /etc/sysctl.conf..."
sed -i -E "s/^([[:space:]]*(vm\.(swappiness|vfs_cache_pressure|dirty_[a-zA-Z_]+)|fs\.file-max)[[:space:]]*=)/# [purged-for-tuning] \1/g" /etc/sysctl.conf
fi
echo "==> Conflict purge complete."
'
C. Deploy the Unified Byte-Based Configuration
In the Linux kernel, dirty_bytes and dirty_ratio are mutually exclusive. By writing explicit non-zero byte values into /proc/sys/vm/dirty_background_bytes and /proc/sys/vm/dirty_bytes, the kernel automatically unsets and forces dirty_background_ratio and dirty_ratio to 0 without needing redundant or invalid explicit assignments.
Write the single authoritative configuration file:
Bash
sudo tee /etc/sysctl.d/99-performance-memory-tuning.conf > /dev/null << 'EOF'
# ====================================================================
# Ultra-Low RAM Footprint & Fixed NVMe Flushing Profile (32M / 64M)
# Purpose: Free physical RAM for apps, flush dirty buffers rapidly to NVMe
# ====================================================================
# 1. Swap as absolute last resort (only to prevent hard OOM panic)
vm.swappiness = 1
# 2. Aggressively reclaim VFS directory entries and inode metadata to maximize free RAM
vm.vfs_cache_pressure = 1000
# 3. Fixed NVMe writeout boundaries (unsets dirty_background_ratio and dirty_ratio to 0)
# Wake kernel background flusher at 32 MB (32 * 1024 * 1024)
vm.dirty_background_bytes = 33554432
# Synchronous process throttling ceiling at 64 MB (64 * 1024 * 1024)
vm.dirty_bytes = 67108864
# 4. Rapid expiration intervals
# Age out dirty memory after 3 seconds (300 centisecs)
vm.dirty_expire_centisecs = 300
# Wake kernel flusher threads every 2 seconds (200 centisecs)
vm.dirty_writeback_centisecs = 200
# 5. Maximum open file handles ceiling for heavy multitasking, containers, and IDEs
fs.file-max = 2000000
EOF
Apply the configuration immediately:
Bash
sudo sysctl --system
5. Step 4: Disable NVMe Readahead Completely (0 KB)
To eliminate unrequested speculative read caching on your NVMe storage drives, set device readahead to 0 and enforce persistence via a custom udev rule:
Bash
# Apply immediately to active NVMe devices
sudo sh -c 'for d in /sys/block/nvme[0-9]*n[0-9]*; do echo 0 > "$d/queue/read_ahead_kb"; done'
# Create persistent udev rule
sudo tee /etc/udev/rules.d/65-nvme-readahead.rules > /dev/null << 'EOF'
# Completely disable readahead on all NVMe drives
ACTION=="add|change", KERNEL=="nvme[0-9]*n[0-9]*", ATTR{queue/read_ahead_kb}="0"
EOF
# Reload udev rules
sudo udevadm control --reload-rules
sudo udevadm trigger --action=change
6. Verification & Health Audit
Run this diagnostic check to verify that all parameters are active, that ratio parameters have been cleanly unset to 0, that readahead is zeroed, and that on-disk swap has replaced zRAM:
1. Confirm Active Swap File & Purged zRAM
Bash
swapon --show
free -h
Expected Output:
Plaintext
NAME TYPE SIZE USED PRIO
/swap/swapfile file 16G 0B -2
(Notice: No /dev/zram0 appears in the active swap table).
2. Confirm Active Kernel Sysctl Values & Readahead
Bash
sysctl vm.swappiness vm.vfs_cache_pressure vm.dirty_background_bytes vm.dirty_bytes vm.dirty_background_ratio vm.dirty_ratio vm.dirty_expire_centisecs vm.dirty_writeback_centisecs fs.file-max
cat /sys/block/nvme*/queue/read_ahead_kb
Expected Output:
Plaintext
vm.swappiness = 1
vm.vfs_cache_pressure = 1000
vm.dirty_background_bytes = 33554432
vm.dirty_bytes = 67108864
vm.dirty_background_ratio = 0
vm.dirty_ratio = 0
vm.dirty_expire_centisecs = 300
vm.dirty_writeback_centisecs = 200
fs.file-max = 2000000
0
Notice that vm.dirty_background_ratio = 0, vm.dirty_ratio = 0, and readahead returns 0. This confirms the kernel is operating strictly under fixed byte limits with zero speculative read bloat.
3. Monitor Real-Time Dirty Memory Flushing (Optional)
To verify that unwritten data is being rapidly dispatched to your NVMe drive under write loads (compiling code, downloading packages, saving assets), monitor live dirty page eviction:
Bash
watch -n 0.5 "grep -E '^(Dirty|Writeback):' /proc/meminfo"
7. Required Final Step: System Reboot
To release any residual zRAM compressed memory blocks from kernel space, terminate legacy generator daemon threads, and verify that /etc/fstab swap mounts and /etc/sysctl.d/99-performance-memory-tuning.conf persist across a clean boot, reboot your system now:
Bash
sudo reboot
After your system boots back up, execute the verification check once more to confirm the values remain locked:
Bash
sysctl vm.swappiness vm.vfs_cache_pressure vm.dirty_background_bytes vm.dirty_bytes vm.dirty_background_ratio vm.dirty_ratio fs.file-max
cat /sys/block/nvme*/queue/read_ahead_kb