Linux uses spare RAM to cache disk data. That’s why a server that’s barely busy can show almost no “free” memory — and why that’s usually a good sign. Understanding the cache helps you read memory statistics correctly and tune I/O-heavy workloads.
Reads: the page cache
When a process reads a file, the kernel loads the data into the page cache in RAM. The next read of the same data comes from memory, which is orders of magnitude faster than disk.
free -h
total used free shared buff/cache available
Mem: 3.8Gi 1.1Gi 210Mi 12Mi 2.5Gi 2.4Gi
- buff/cache — memory holding cached file data.
- available — what applications can actually use: free memory plus cache the kernel can drop instantly.
Watch available, not free. Low “free” with high “available” is healthy.
Writes: dirty pages and write-back
Writes normally go to the page cache first. The modified (“dirty”) pages are written to disk later by kernel flusher threads. This makes writes fast and lets the kernel combine many small writes into larger ones.
grep -E 'Dirty|Writeback' /proc/meminfo
Key settings:
| Setting | Default | Meaning |
|---|---|---|
vm.dirty_background_ratio |
10 | % of memory dirty before background flushing starts |
vm.dirty_ratio |
20 | % of memory dirty before writing processes are blocked until data is flushed |
vm.dirty_expire_centisecs |
3000 | Age (30 s) after which dirty data is flushed |
On servers with lots of RAM, the percentages allow many gigabytes of unwritten data, which can cause long stalls when it’s finally flushed. Byte-based limits smooth this out:
# /etc/sysctl.d/90-dirty.conf
vm.dirty_background_bytes = 268435456 # 256 MB
vm.dirty_bytes = 1073741824 # 1 GB
Durability: fsync
Data in the page cache is lost if the server crashes or loses power. Applications that need durability — databases, mail servers — call fsync() (or open files with O_DIRECT/O_DSYNC) to force data to disk before confirming a write.
- Databases handle this themselves; don’t disable their fsync settings in production (
innodb_flush_log_at_trx_commit=1for MySQL/MariaDB,fsync=onfor PostgreSQL). - Your own scripts can call
syncafter writing important files.
Seeing the cache in action
# Drop caches (testing only — makes the next reads slow)
sync; echo 3 > /proc/sys/vm/drop_caches
time cat largefile > /dev/null # first read: from disk
time cat largefile > /dev/null # second read: from cache, much faster
Don’t drop caches on a production server “to free memory”; it only makes the system slower until the cache refills.
Tools to see which files are cached: vmtouch or fincore (from util-linux):
fincore /var/lib/mysql/ibdata1
I/O monitoring
iostat -xz 1 # per-device utilization, latency (r_await/w_await)
iotop -o # which processes are doing I/O
vmstat 1 # 'bi'/'bo' blocks in/out, 'wa' I/O wait
cat /proc/pressure/io # time tasks spent waiting for I/O
Practical tuning tips
- Give the cache room. Don’t size application memory so tightly that nothing is left for caching.
- Use
noatimeon data filesystems to avoid writes on every read. - Databases manage their own cache (InnoDB buffer pool, PostgreSQL shared buffers) on top of the page cache. Size them deliberately — see Debian performance tuning.
- Use a lower
vm.swappinessso the kernel drops cache before swapping out application memory.
Related: Linux file system caching covers the dentry and inode caches, and Linux disk performance covers benchmarking with fio.