LVM and Linux Filesystem Comparison
Storage on Linux is a deep subject that rewards careful study. The raw partition model that new users learn first — carve up a disk with fdisk, format with mkfs, mount it — works fine for a single machine with a single disk and a static workload. The moment your requirements become even slightly dynamic, that model starts working against you. You need more space on /var/lib/docker but the partition is at 95% and the adjacent one is nearly empty. You need a consistent backup of a live database. You want to provision fifty virtual machines without pre-allocating fifty separate disk images. Partitions, as a concept, cannot help you here.
LVM solves these problems at the block layer, below the filesystem. The filesystems — ext4, XFS, Btrfs, ZFS — solve complementary problems at the layer above: checksums, snapshots, compression, RAID, and varying levels of operational maturity. Understanding both layers, and the interaction between them, is fundamental to operating Linux storage infrastructure seriously.
This post works through LVM from first principles, covers all four major filesystems in depth, and ends with a decision guide that cuts through the hype in both directions.
Why LVM exists
A traditional partition is a contiguous range of sectors on a specific physical disk. Its size is fixed at creation time. If you need more space, you either delete the partition and recreate it (losing data), extend it if there happens to be unallocated space immediately after it on the same disk (fragile and unlikely in practice), or add a second disk and use a symlink or bind mount to overflow into it (architectural debt that compounds over time).
LVM introduces a virtualization layer between physical storage and the filesystems that consume it. Physical disks or partitions are registered as Physical Volumes (PVs). PVs are pooled into a Volume Group (VG). The VG is the flexible allocation pool from which Logical Volumes (LVs) are carved. LVs are what filesystems and applications actually see — they look like block devices (/dev/datavg/data), and everything above that layer is oblivious to the LVM machinery underneath.
The immediate practical consequences: an LV can span multiple physical disks transparently. You can extend an LV online while a filesystem is mounted and actively in use, without a maintenance window. You can take a point-in-time snapshot of a live LV for consistent backups. You can thin-provision a pool and overcommit physical storage against it. None of this requires reformatting, repartitioning, or taking the system down.
LVM concepts and setup
The three-layer hierarchy is worth internalising before touching any commands.
Physical Disks
+------------+ +------------+ +------------+
| /dev/sdb | | /dev/sdc | | /dev/sdd |
+------------+ +------------+ +------------+
| | |
v v v
Physical Volumes (PVs) -- labelled and managed by LVM
+------------+ +------------+ +------------+
| PV sdb | | PV sdc | | PV sdd |
| 200GB | | 200GB | | 200GB |
+------------+ +------------+ +------------+
|_________________|_________________|
|
v
Volume Group (VG) -- single unified pool
+-----------------------------------------------+
| datavg (600GB) |
+-----------------------------------------------+
| | |
v v v
Logical Volumes (LVs) -- what filesystems mount
+------------+ +------------+ +------------+
| lv_home | | lv_data | | lv_logs |
| 100GB | | 300GB | | 50GB |
+------------+ +------------+ +------------+
| | |
v v v
Filesystems / Block Device Users
+------------+ +------------+ +------------+
| ext4 | | XFS | | ext4 |
+------------+ +------------+ +------------+
Initializing Physical Volumes
Any block device — a raw disk, a disk partition, even a loop device — can become a PV:
|
|
pvcreate writes an LVM metadata header to the beginning of each device. You can inspect what it wrote:
|
|
The output shows the PV UUID, total size, and the PE (Physical Extent) size — the fundamental allocation unit. The default PE size is 4 MB. LVM allocates storage in whole PEs, so an LV’s size is always a multiple of 4 MB. You can set a larger PE size at VG creation time (vgcreate -s 32M), which reduces metadata overhead on very large VGs, but 4 MB is the right answer for almost every workload.
Creating and populating a Volume Group
|
|
vgdisplay shows total PE count, free PEs, the VG UUID, and the PE size. The VG is now a pool of PEs spread across both disks. LVM does not yet know or care how those PEs are distributed — it tracks individual PE allocations per LV.
Creating Logical Volumes
|
|
The resulting devices appear at /dev/datavg/data and /dev/datavg/logs (symlinks to /dev/mapper/datavg-data and /dev/mapper/datavg-logs). Format and mount them as you would any block device:
|
|
lsblk shows the full device tree, making it easy to see which LVs are active and which filesystems are mounted where:
|
|
Striping across PVs
For performance-sensitive workloads where an LV spans multiple PVs, you can instruct LVM to stripe writes across them:
|
|
This distributes I/O across both PVs in 64 KB stripes, roughly doubling sequential throughput. The tradeoff is that striped LVs are less resilient — losing either PV loses the entire LV. For redundancy, use LVM mirroring or a filesystem-level RAID layer (or RAID under LVM via mdadm).
Resizing Logical Volumes online
This is one of LVM’s most operationally useful features. You can extend a mounted, active LV without unmounting it or interrupting the application.
Extending an LV
|
|
This extends the block device. The filesystem does not yet know the device grew — it must be told separately. For ext4:
|
|
For XFS (which can only grow online, never shrink):
|
|
Note that xfs_growfs takes a mount point, not a device path.
For Btrfs:
|
|
The -r flag on lvextend combines both steps into one command — it extends the LV and then calls the appropriate filesystem resize tool automatically:
|
|
-r detects the filesystem type and calls resize2fs, xfs_growfs, or btrfs filesystem resize as appropriate. Convenient for scripted provisioning.
Shrinking an LV
Shrinking is more delicate and only supported by ext4 and Btrfs. XFS cannot shrink — full stop. The correct procedure for ext4:
- Unmount the filesystem.
- Run
e2fsck -f /dev/datavg/datato ensure the filesystem is clean. Never skip this. - Shrink the filesystem:
resize2fs /dev/datavg/data 30G - Shrink the LV:
lvreduce -L 30G /dev/datavg/data - Remount.
The ordering is critical. If you shrink the LV before the filesystem, you truncate live filesystem data and will likely destroy it. The filesystem must always be smaller than or equal to the LV that contains it.
Adding a disk to an existing VG
When a VG starts running low on free PEs, add another disk:
|
|
This is instant. No data movement, no downtime. The VG simply gains more free PEs and any subsequent lvcreate or lvextend can use them.
Evacuating and removing a PV with pvmove
If you need to decommission a physical disk — for replacement, retirement, or to remove it from the VG — you can evacuate all its extents to other PVs in the same VG while the system remains live:
|
|
LVM moves all PEs from /dev/sdb to other PVs in the VG. This can be slow (it is moving data), and it runs in the background. Monitor it with pvdisplay /dev/sdb or lvs -a -o +devices. Once complete:
|
|
This is the correct way to hot-swap a disk out of an LVM VG without data loss.
LVM snapshots
How COW snapshots work
An LVM snapshot captures the state of an LV at the moment of creation. It does not copy any data immediately. Instead, LVM tracks which blocks in the origin LV are about to be overwritten — the first write to any block after snapshot creation causes LVM to copy the original block to the snapshot volume first, then allow the write to proceed. This is copy-on-write.
The snapshot therefore stores only the delta between its creation point and the current state. A snapshot of a 100 GB LV may consume only a few hundred megabytes if writes have been sparse since creation.
Creating a snapshot
|
|
The -s flag designates this as a snapshot of /dev/datavg/data. The -L 5G is the snapshot’s private COW allocation — not the size of the data being snapshotted, but the space reserved for storing changed blocks during the snapshot’s lifetime.
Mount it read-only to inspect or back up:
|
|
Sizing snapshots correctly
If the COW storage fills completely, the snapshot is invalidated. LVM marks it as invalid and the snapshot device becomes unusable — the original data is safe, but the snapshot is lost. Size the snapshot allocation generously relative to your expected write rate during the backup window. lvs shows current snapshot usage:
|
|
If snap_percent is climbing toward 100, either the snapshot was undersized or the backup is taking too long.
Using snapshots for consistent backups
The standard pattern for a consistent backup of a live database:
- Quiesce application writes, or use the database’s own snapshot/freeze mechanism (e.g.,
FLUSH TABLES WITH READ LOCKfor MySQL,pg_start_backup()for PostgreSQL), or usefsfreeze -f /mountpointto freeze filesystem I/O. lvcreate -L 5G -s -n data_snap /dev/datavg/data- Immediately resume writes:
fsfreeze -u /mountpointor release the database lock. - Mount the snapshot and run the backup tool.
lvremove /dev/datavg/data_snapwhen done.
The application is only paused for the milliseconds it takes to create the snapshot — essentially zero from the user’s perspective. The backup itself runs against the frozen snapshot and can take as long as it needs.
Snapshot performance impact
Every write to the origin LV while a snapshot exists triggers a COW read-modify-write to the snapshot device. On write-heavy workloads, this can impose 20–50% write latency overhead. Keep the snapshot alive only as long as necessary.
Rolling back with snapshot merge
If you took a snapshot before a risky change and want to revert:
|
|
The next mount of the origin LV will complete the merge, rolling the origin back to the snapshot state. This is destructive — all writes to the origin since the snapshot was taken are discarded.
LVM thin provisioning
The concept
Standard (thick) LVM LV creation reserves physical extents immediately. If you create a 100 GB LV, 100 GB of PEs are allocated from the VG at that moment, whether or not any data is ever written. Thin provisioning separates the allocation of the virtual size from the allocation of physical backing storage. A thin LV can be 100 GB from the filesystem’s perspective, but consume zero physical PEs until data is actually written to it.
This enables overprovisioning — creating more virtual storage than you have physical storage — which is standard practice in virtualization and container environments.
Creating a thin pool
A thin pool is a special LV that acts as the backing store for thin LVs:
|
|
LVM actually creates two internal LVs: the data LV (for the actual data blocks) and a metadata LV (for the thin provisioning bookkeeping). You can tune the metadata size with --poolmetadatasize on large pools.
Creating thin LVs
|
|
Three 100 GB virtual LVs exist, but the physical pool is only 200 GB. This is overprovisioned at 150%. As long as the three VMs don’t collectively write more than 200 GB of unique data, everything works.
Thin Pool: mypool (200GB physical)
+--------------------------------------------------+
| Physical blocks allocated as writes occur |
| [/////used/////][ free ] |
+--------------------------------------------------+
| | |
v v v
+----------+ +----------+ +----------+
| vm1disk | | vm2disk | | vm3disk |
| 100GB | | 100GB | | 100GB |
| virtual | | virtual | | virtual |
| (writes | | (writes | | (writes |
| trigger | | trigger | | trigger |
| physical | | physical | | physical |
| alloc) | | alloc) | | alloc) |
+----------+ +----------+ +----------+
Total virtual: 300GB | Total physical: 200GB
Overprovisioning: 150%
Monitoring thin pool usage
|
|
Watch data_percent carefully. If a thin pool fills to 100%, all thin LVs in it immediately go offline with I/O errors. The data is not lost — it is intact — but nothing can read or write until the pool has free space again. You can extend a thin pool online:
|
|
Set up monitoring alerts at 70% and 85% pool usage. The dmeventd daemon can auto-extend thin pools if configured in /etc/lvm/lvm.conf under activation.thin_pool_autoextend_threshold.
Thin snapshots
Thin LVs support a dramatically more efficient snapshot mechanism than thick LVM snapshots:
|
|
Because the thin pool is already a COW structure, thin snapshots are nearly instantaneous and consume no initial space — they share all existing blocks with the origin. Creating fifty snapshots of a 100 GB thin LV is trivially cheap in both time and space. This makes thin provisioning the right choice for VM disk image management.
The thin_dump and thin_delta tools from the thin-provisioning-tools package allow you to inspect the thin pool metadata and compute the changed blocks between two snapshots — useful for incremental backup schemes.
ext4 — the reliable workhorse
ext4 is the default filesystem on Debian, Ubuntu, and most general-purpose Linux distributions. It is the direct successor to ext3, adding extents (contiguous block ranges that replace the old indirect block chains), delayed allocation, online defragmentation support, and a 1 EB theoretical maximum volume size.
When to use ext4
Use ext4 when compatibility and predictability matter more than features. It is the right choice for boot partitions, system root volumes, any environment where you need e2fsck to be the most battle-tested repair tool available, and any scenario where the failure modes need to be completely understood. Ext4 has been in production for well over a decade. Its behavior in every failure scenario — power loss mid-write, kernel OOM during heavy I/O, disk errors during journal replay — is documented and understood.
Journaling modes
Ext4 supports three journal modes, set at mount time via data= in fstab:
ordered(default): metadata is journaled; data is written to disk before the metadata that references it is committed. Solid protection against metadata corruption with reasonable performance.writeback: metadata is journaled, data is not ordered relative to metadata. Fastest, but a crash can result in stale data appearing in files (not corruption of the filesystem structure, just wrong file contents).data: both metadata and data are journaled. Safest, but doubles write I/O and is noticeably slower. Rarely worth it given modern battery-backed RAID controllers and SSDs.
For most workloads, ordered is correct. The writeback mode is appropriate for workloads where the application provides its own data durability guarantees (e.g., databases using fsync).
tune2fs
tune2fs reads and modifies ext4 filesystem parameters on a live or unmounted filesystem:
|
|
This prints everything — block size, inode count, free inodes, journal size, last mount time, last check time, UUID, and the current error behavior setting.
Useful adjustments:
|
|
The reserved block default of 5% dates from when disks were smaller and running out of space on the root filesystem was a serious operational hazard. On a 10 TB data volume, 5% is 500 GB sitting idle for a purpose that may not apply at all. Drop it to 1%.
Limitations
Ext4 has no built-in checksums for data or metadata. A silent bit flip in the storage layer — from a faulty DIMM, a misbehaving HBA, or a flaky cable — will be written to disk and read back without any detection. Ext4 trusts the storage layer to be correct. For most environments this is fine; for high-value data, it is a reason to prefer ZFS or Btrfs. Ext4 also has no native snapshot mechanism — use LVM snapshots underneath it.
XFS — high-performance for large files
XFS originated at SGI in the early 1990s, designed for the demanding I/O requirements of graphics and media workloads. It became the default filesystem on RHEL 7 and has remained so through RHEL 9 and AlmaLinux 9. It is particularly well-suited to large files and high-throughput sequential I/O.
When to use XFS
XFS is the right choice for large filesystems (tens of terabytes), workloads dominated by large sequential reads and writes (video production, database tablespaces, backup targets, HPC scratch space), and environments running RHEL or its derivatives where XFS is the supported default. It handles parallel I/O well due to its allocation group architecture — the filesystem is divided into independent allocation groups that can process metadata operations concurrently.
Architecture and key features
XFS uses allocation groups to parallelize metadata operations. Each AG has its own free space B-trees and inode B-trees, so threads writing to different AGs do not contend on the same metadata locks. The number of AGs is determined at mkfs.xfs time based on filesystem size.
Delayed logging (-l version=2, the default since XFS v5) batches journal writes into a large in-memory log buffer, dramatically reducing journal I/O overhead on write-heavy workloads. The tradeoff is that after an unclean shutdown, log recovery can take meaningful time proportional to the amount of in-flight transactions.
|
|
This shows the filesystem geometry — block size, sector size, AG count, log size, and whether v5 features (checksums on metadata, self-describing metadata) are enabled. Note that XFS v5 adds metadata checksums, which protects filesystem structure but not user data.
Working with XFS
|
|
xfs_repair is robust but slow on large filesystems. On a multi-terabyte XFS filesystem, expect repair to take hours. Plan for this in your recovery time objectives.
Limitations
XFS cannot shrink. The filesystem and the underlying LV or partition can only be extended, never reduced. If you anticipate needing to shrink a filesystem, XFS is not the right choice. XFS also has no native snapshot mechanism — use LVM snapshots under XFS, and use xfs_freeze to quiesce before snapping:
|
|
XFS vs ext4 for databases: on large sequential I/O (full table scans, backup/restore, bulk inserts), XFS typically wins. For small random I/O (OLTP with many small transactions), ext4 with data=writeback is often comparable or slightly faster. Both are vastly inferior to ZFS with ARC or a properly tuned filesystem on NVMe for the highest-performance database workloads.
Btrfs — the modern Linux filesystem
Btrfs (B-tree filesystem) has been in the mainline kernel since 2.6.29 (2009). It is the default root filesystem on Fedora Workstation, openSUSE Tumbleweed, and openSUSE Leap. Synology uses it as their primary filesystem type on supported NAS units. Facebook ran it in production at scale for years. And yet it accumulated a reputation for data loss that still lingers — a reputation that was earned, partly, by the RAID 5/6 implementation, and that now overstates the risk for common single-disk and RAID 1 use cases.
What Btrfs brings to the table
The core architecture is copy-on-write. Every write allocates new blocks rather than overwriting existing ones, which is what makes snapshots nearly free, reflinks fast, and checksums practical. Built-in checksums cover both data and metadata — every block read from disk is verified against a stored checksum, and silent corruption is detected and reported. This is the most important single feature advantage Btrfs has over ext4 and XFS.
Additional major features: subvolumes (lightweight filesystem namespaces within a single volume), native snapshots (read-write or read-only, shared-block, instant), transparent compression (lzo, zlib, zstd), online balance (redistributes data across devices), send/receive for incremental replication, and RAID profiles at the filesystem level.
Subvolumes
A subvolume is a namespaced section of a Btrfs volume that behaves like an independent filesystem for most purposes — it can be mounted separately, have its own snapshot policy, and be assigned quota groups. Subvolumes are the foundation of good Btrfs layout:
|
|
The conventional @ naming for the root subvolume is used by Ubuntu’s installer and by snapper. Adopting it from the start keeps your layout compatible with standard tooling.
Btrfs Volume: /dev/datavg/data
+---------------------------------------------+
| [top-level subvolume] |
| | |
| +-- @ (root FS) |
| | | |
| | +-- snapshot: @-2026-05-01 (RO) |
| | +-- snapshot: @-2026-05-15 (RO) |
| | +-- snapshot: @-2026-05-30 (RO) |
| | |
| +-- @home (home dir) |
| | | |
| | +-- snapshot: @home-2026-05-30 (RO)|
| | |
| +-- @snapshots (snapshot store) |
| | |
| +-- @docker (container storage)|
+---------------------------------------------+
Snapshots
|
|
Snapshots are created instantly regardless of the subvolume’s size. They share all existing blocks with the origin — a snapshot of a 500 GB subvolume consumes no additional space at creation, and grows only as either the origin or the snapshot accumulates divergent writes.
btrfs send / receive
The killer feature for backup replication:
|
|
Incremental sends are efficient — they transmit only the blocks that changed between the parent and child snapshot. This is the Btrfs equivalent of ZFS send/receive, and it makes replication of large datasets practical even over slow links.
Compression
|
|
Zstd at level 3 is a good default — fast, good ratio. Check actual compression ratios with:
|
|
The output shows uncompressed data size vs. on-disk size. For source code, logs, and configuration files, expect 30–50% space savings. For already-compressed content (JPEGs, MP4s, zip files), expect near-zero savings and do not waste CPU cycles on it. The compress-force option forces compression attempts even on blocks that previously compressed poorly — avoid it.
Scrub
|
|
Scrub reads every block on the volume and verifies it against the stored checksum. It detects and (on RAID profiles with redundancy) corrects silent data corruption. Schedule scrubs monthly via systemd timer. This is functionality ext4 and XFS simply do not have at the data level.
RAID in Btrfs — what is safe and what is not
Btrfs RAID profiles are set per data type (data, metadata, system):
- RAID 1 (two copies of each block, can span two+ devices): production-safe, well-tested.
- RAID 10 (striped mirrors): production-safe.
- RAID 5 / RAID 6: do not use for data you care about. As of Linux kernel 6.x and current development in 2025–2026, the RAID 5/6 write hole remains an open issue. A partial-stripe write followed by a power failure can leave parity inconsistent with data, and subsequent scrubs will blindly repair data to match the wrong parity rather than detecting the corruption. The Btrfs kernel documentation and upstream developers themselves have historically warned strongly against RAID 5/6 for production data. This situation has not been fully resolved. The kernel added stronger warnings when creating RAID 5/6 arrays precisely because users kept ignoring the existing documentation.
If you need software RAID 5/6 under Btrfs, use mdadm for the RAID layer and run Btrfs in single profile on top of the md device. You lose Btrfs’s self-healing scrub for the RAID layer, but the RAID itself is managed by a more mature implementation.
Historical stability and current reality
Btrfs had genuine data loss bugs through roughly 2017–2018, particularly around balance operations, RAID, and specific edge cases with COW metadata. The kernel 5.x series resolved most of the serious issues for common configurations. As of kernel 6.x, Btrfs RAID 1 on a single disk or multi-disk is stable for everyday use. The caveats that remain: RAID 5/6 (as above), very fragmented filesystems (heavy random writes on nearly-full volumes can lead to ENOSPC before df shows 100%), and the need to monitor free space more carefully than with ext4.
If you are on a current kernel (6.1 LTS or newer) and not using RAID 5/6, Btrfs is a legitimate production filesystem for the use cases where its features matter.
ZFS on Linux (OpenZFS)
A full ZFS treatment is beyond this post’s scope — the LunarOps posts on TrueNAS SCALE and homelab NAS cover ZFS architecture, pool design, RAID-Z levels, and ARC tuning in depth. What matters here is the Linux-specific context and the ZFS vs. Btrfs decision.
OpenZFS on Linux
ZFS is not in the mainline Linux kernel. The OpenZFS project maintains the Linux port, distributed as a DKMS module. The reason it is not in-tree is a license conflict: OpenZFS is licensed under the CDDL, which is not compatible with the GPL that covers the kernel. This is not a technical problem — it is a legal one — but it has practical consequences: ZFS is not in the default kernel builds from most distributions and must be installed separately.
Ubuntu ships ZFS support as a first-class option. On Ubuntu 24.04 LTS, zfsutils-linux and the OpenZFS 2.2.x series are available directly from the main repository:
|
|
Ubuntu’s installer also supports native ZFS root with optional encryption. On RHEL/AlmaLinux, ZFS is available through the ZFS on Linux repository (zfsonlinux.org) but is not in the official repositories and requires DKMS with kernel headers.
The kernel module must be rebuilt when the kernel updates. DKMS handles this automatically, but it introduces a brief window after a kernel upgrade before the new module is compiled — plan reboots accordingly in production.
Why ZFS over Btrfs for production
ZFS’s most important advantage is maturity. It has been running in production on Solaris and FreeBSD in high-stakes environments (financial services, large-scale NAS appliances, OpenStack storage nodes) for over fifteen years. TrueNAS, Proxmox Backup Server, and the storage backends of many commercial NAS vendors use it. The RAID-Z implementation does not have the write-hole problem that plagues Btrfs RAID 5/6. The ARC (Adaptive Replacement Cache) is an effective read cache that can be sized to available RAM and dramatically accelerates repeated access patterns. ZFS send/receive for incremental snapshot replication is mature and widely used.
For a production file server, NAS, or any workload where data integrity is the primary concern and you are willing to manage the DKMS module and rebuild workflow, ZFS is the stronger choice. For a desktop Linux system or a developer workstation where the subvolume UX and snapper integration are attractive, Btrfs is entirely reasonable.
SSD alignment and TRIM
Why alignment matters
SSDs and modern hard drives with 4Kn (4096-byte native sectors) require that filesystem block boundaries align with physical sector boundaries. A misaligned filesystem that straddles two physical sectors on an SSD forces the drive to read two physical sectors, modify one logical sector, and write both back — a read-modify-write amplification penalty on every write.
Modern partitioning tools (fdisk in interactive mode since util-linux 2.17, parted, gdisk) default to 1 MB alignment for all partitions. 1 MB is a multiple of every common physical sector size (512B, 4096B) and of typical SSD erase block sizes. If you created your partitions with a modern tool, alignment is almost certainly correct.
Verify:
|
|
For LVM over correctly aligned partitions or whole disks, alignment propagates correctly — the PE size default of 4 MB is well-aligned to any reasonable physical sector size.
TRIM and discard
TRIM (the ATA command) and DISCARD (the NVMe/SCSI equivalent) tell the SSD controller which logical block addresses are no longer in use. Without this information, the SSD’s garbage collection algorithm must read pages before erasing them to determine if they contain live data, which degrades write performance over time as the drive fills.
Two approaches to issuing TRIM:
Continuous discard (discard mount option in /etc/fstab):
/dev/datavg/data /mnt/data ext4 defaults,discard 0 2
Every unlink or fallocate --punch-hole immediately sends a DISCARD to the SSD. The overhead is small but measurable on write-heavy workloads, since every delete generates additional I/O commands. Generally not recommended for HDDs (no effect) or for high-frequency delete workloads.
Periodic TRIM (fstrim):
|
|
The fstrim.timer systemd unit ships with util-linux and runs fstrim -av weekly. On Ubuntu 22.04+, Debian 12 (Bookworm) fresh installs, and most modern distributions, this timer is enabled by default. Verify:
|
|
Note: systems upgraded from earlier Debian versions may not have the timer enabled even if it ships enabled on fresh installs — check explicitly. Enable it if it is not running:
|
|
Periodic TRIM via fstrim.timer is the recommended approach for most environments. It is low-overhead, runs during a quiet period, and does not add per-operation latency.
LVM discard passthrough
If you are running filesystems on LVM, TRIM commands from the filesystem must be passed through the DM (device mapper) layer to reach the underlying block device. This is not enabled by default. Edit /etc/lvm/lvm.conf:
activation {
issue_discards = 1
}
Without this, fstrim on a filesystem mounted on an LV sends DISCARD to the logical volume but it is silently dropped before reaching the physical disk. With thin provisioning, discard passthrough also allows the thin pool to reclaim blocks when files are deleted, which is essential for controlling thin pool growth.
Filesystem comparison
| Feature | ext4 | XFS | Btrfs | ZFS |
|---|---|---|---|---|
| Data checksums | No | No (metadata only in v5) | Yes | Yes |
| Metadata checksums | No | Yes (v5) | Yes | Yes |
| Native snapshots | No | No | Yes | Yes |
| Transparent compression | No | No | Yes (lzo/zlib/zstd) | Yes (lz4/zstd/gzip) |
| Native RAID | No | No | Yes (0/1/10; avoid 5/6) | Yes (RAID-Z 1/2/3) |
| Online grow | Yes | Yes | Yes | Yes |
| Online shrink | Yes | No | Yes | No |
| Send/receive replication | No | No | Yes | Yes |
| Deduplication | No | No | Limited (offline) | Yes (inline) |
| Max volume size | 1 EB | 8 EB | 16 EB | 256 ZB |
| Max file size | 16 TB | 8 EB | 16 EB | 16 EB |
| Repair tool | e2fsck | xfs_repair | btrfs check | zpool scrub/zfs scrub |
| Shrink support | Yes | No | Yes | No |
| Kernel module | In-tree | In-tree | In-tree | DKMS (out-of-tree) |
| RHEL default | No | Yes | No | No |
| Ubuntu default (root) | Yes | No | Optional | Optional |
| Fedora default (desktop) | No | No | Yes | No |
| Production maturity | Very high | Very high | High (excl. RAID 5/6) | Very high (OpenZFS) |
| Best for | General purpose, boot, compatibility | Large FS, high-throughput, RHEL | Desktop, snapshots, COW workloads | NAS, integrity-critical, server |
Decision guide
Boot partition (/boot, /boot/efi): ext4 always. Bootloaders have limited filesystem driver support, and you do not want complexity on the partition that the system must read before the kernel is even running.
RHEL, AlmaLinux, Rocky Linux root and data volumes: XFS. It is the supported default, the one Red Hat’s support team knows deeply, and it performs excellently on the large storage workloads these platforms are often running. There is no good reason to fight the default here.
Debian/Ubuntu server, general-purpose data volumes: ext4 for simplicity and maximum tool compatibility; XFS if the volume is large and the workload is sequential-I/O-heavy; Btrfs if you specifically want checksums or subvolume/snapshot functionality without ZFS.
Desktop Linux with rollback capability: Btrfs with snapper is the most ergonomic option. Fedora and openSUSE ship this integration out of the box. The ability to take a pre-upgrade snapshot, upgrade, and roll back if something breaks is genuinely useful and Btrfs provides it at essentially no cost.
Container storage (Docker, Podman, containerd): Btrfs with the btrfs storage driver on a dedicated subvolume, or ext4/XFS with overlay2. The Btrfs reflink support makes layer sharing efficient; the subvolume model integrates well with the container image layer concept.
NAS, TrueNAS, file server where data integrity is paramount: ZFS. RAID-Z is reliable in a way Btrfs RAID 5/6 is not yet. The ARC is effective. ZFS send/receive for replication is battle-tested at scale. The DKMS module requirement is a real operational cost, but it is manageable.
HPC scratch or high-throughput sequential workloads on large volumes: XFS on LVM. Straightforward, fast, well-understood.
Any workload requiring silent corruption detection on a budget server without ECC RAM: Btrfs or ZFS. This is the most under-appreciated use case for checksummed filesystems. A server with faulty RAM or a marginal HBA can silently corrupt data that ext4 and XFS will store and serve back without complaint. Checksums detect this at the filesystem layer.
Putting it together: a practical storage stack
A typical production server might look like this:
- Two NVMe drives for OS and databases, running in LVM with ext4 on a modest LV for the root filesystem, XFS on a larger LV for database tablespaces. LVM provides the flexibility to extend these without reinstallation.
- Four HDDs for bulk storage, provisioned as an LVM VG with thin provisioning for multiple workloads sharing the same physical pool. Periodic
pvmoveto cycle aging disks out without downtime. - Btrfs for a developer-facing code repository server, taking hourly read-only snapshots with
btrfs subvolume snapshot -rand replicating incrementally to a backup server withbtrfs send | ssh backup btrfs receive. - ZFS for a separate NAS that holds long-term archives, configured with RAID-Z2 for redundancy, weekly scrubs, and ZFS send/receive replication to an off-site target.
LVM and the filesystems are not competing technologies. LVM operates below the filesystem and provides the block-level flexibility that makes everything above it more manageable. The filesystem choice above LVM (or above a ZFS pool, which has its own volume management) depends on the workload’s specific requirements: compatibility, throughput profile, need for checksums, need for snapshots, and operational context.
Understanding the full stack — from physical extent through volume group through logical volume through filesystem — is what separates storage administration from storage archaeology. With the tools and concepts in this post, you can design storage layouts that you can grow, adapt, and maintain without surprises.
Comments