XFS: Explanation & Insights

The filesystem built for big disks and many writers at once — it grows beautifully, but it never shrinks.

What It Is

XFS is a journaling filesystem — the on-disk data structure that turns a raw block device into something with files, directories, owners, and timestamps. It sits in exactly the same slot as ext4: you put it on a partition or an LVM volume, you mount it onto the directory tree, and from then on every ls, cat, and cp is a conversation with it. Where XFS differs is its temperament. It was engineered, from its very first line, for scale — enormous files, enormous volumes, and many CPUs hammering it at once without tripping over each other.

If you've never chosen a filesystem before, here's the orientation. Linux can run a dozen of them, but on a server you mostly meet two: ext4, the unflashy default that just works, and XFS, the one you reach for when the workload gets large — big media files, backup targets, log aggregation, database volumes measured in terabytes. XFS isn't exotic, either. If you've ever run RHEL, CentOS, Rocky, or AlmaLinux, you've already been using it: it has been the default root filesystem on RHEL since version 7 (2014). So roughly half the Linux servers on Earth boot onto XFS without their owners ever having typed mkfs.xfs.

This page goes the whole distance. We'll explain where XFS came from, how its allocation-group design buys it that parallelism, why it can grow but can never shrink (the one fact that bites people, and the one we'll nail until it's unforgettable), how to create it, inspect it, and repair it — including a xfs_repair gotcha that has cost more than one admin an evening — and exactly when to pick it over ext4 and when not to. By the end you'll know XFS well enough to format a fresh data volume with confidence and to recognise the one mistake everyone makes the first time.

Why It Matters

The filesystem is the most consequential decision you make at format time, because it's the one you can't easily undo. Changing it later means backing up everything, reformatting, and restoring — in practice, rebuilding the volume. So the choice between ext4 and XFS isn't a detail; it's a commitment you live with for the life of the server.

XFS matters because it answers a specific question better than anything else in the mainline kernel: what filesystem do I put under a large, write-heavy, many-threaded workload? A backup server streaming hundreds of gigabytes a night. A video pipeline writing multi-gigabyte files. A database with a dozen connections all flushing to disk at once. A log lake on a 40 TB volume where ext4 starts to run out of address space. In every one of those, XFS pulls ahead — not by a hair, but by a margin you can feel.

It also matters because if you run a Red Hat-family distribution, the choice has already been made for you, and you should understand the thing your root volume is sitting on. Knowing how XFS behaves under a crash, how to inspect it, and — above all — that you cannot shrink it later is the difference between a calm afternoon and a confused one when you discover you over-sized a volume by 200 GB.

History and Philosophy

XFS was born in 1993 at Silicon Graphics (SGI), the company that built the high-end graphics workstations of the era, for their Unix variant IRIX. SGI's customers were doing things almost nobody else was doing in the early '90s: editing uncompressed video, running scientific visualisation, manipulating single files larger than the entire disks most people owned. The filesystems of the day — built when a "large" disk was measured in tens of megabytes — simply fell over at that scale. So SGI threw out the assumptions and designed for the future: 64-bit from the start, extent-based, allocation groups for parallelism, B+ trees everywhere instead of linear scans. The IRIX boxes that rendered the dinosaurs in Jurassic Park were writing those frames onto XFS.

Backstory

When SGI open-sourced XFS in 2000 and the port to Linux began, the codebase arrived already battle-hardened by nearly a decade of production use on the world's most demanding media and scientific workloads. It's rare for a filesystem to enter Linux fully grown up — most are raised from infancy in the open. XFS showed up as an adult with a résumé.

It merged into the Linux kernel in 2001 and spent a quiet decade as the specialist's choice — the thing you reached for when ext3 wasn't enough. Then Red Hat, looking for a default that would carry their enterprise customers into the era of multi-terabyte volumes, chose it for RHEL 7 in 2014. That single decision turned XFS from a niche tool into one of the most-deployed filesystems in the server world overnight, and it remains the RHEL default today.

The philosophy underneath all this is worth stating plainly, because it explains every design trade-off on this page: XFS optimises for the large and the parallel, and is willing to give up flexibility to get there. It is not trying to be everything to everyone. It is trying to move enormous amounts of data, from many writers, without flinching — and the design choices that make that possible are exactly the ones that mean it can never give space back. More on that shortly.

How XFS Works

To understand why XFS is fast at scale — and why it can't shrink — you have to understand four ideas. None is hard; together they are XFS.

Allocation Groups — The Source of the Parallelism

This is the central idea, the one that makes XFS XFS. Instead of treating the volume as one big pool of free space with one set of bookkeeping structures, XFS carves the block device into several roughly-equal independent regions called allocation groups (AGs). A typical volume has anywhere from a handful to dozens of them. Each AG is, in effect, a near-complete little filesystem of its own: it has its own free-space indexes, its own inode allocation structures, its own B+ trees.

Why does that matter? Because the bottleneck in a busy filesystem is usually contention on the bookkeeping. When ten threads all want to create a file at the same instant, they all need to grab free space and allocate an inode — and if there's one shared free-space structure, they queue up behind a lock. With allocation groups, those ten threads can be steered into ten different AGs and allocate simultaneously, each touching its own independent indexes, none waiting on the others. That's the parallelism. It's why XFS scales so well across many CPUs and many concurrent writers — the design hands each writer its own sandbox.

The catch is hiding in plain sight here, and we'll come back to it: the AGs are laid out across the volume in a fixed arrangement decided at format time, with the last AG sitting at the far end of the disk. Hold that thought.

Extent-Based Allocation

Like ext4 and btrfs, XFS tracks file data in extents rather than block by block. An extent is a single record that says "this file occupies blocks 50000 through 51999" — one entry for two thousand blocks, instead of two thousand pointers. For a large, contiguous file this is enormously more efficient than the old block-list approach: less metadata, fewer lookups, and the allocator naturally tries to keep each file in long unbroken runs. Large files are XFS's home turf, and extents are why it handles them so gracefully.

Delayed Allocation

XFS doesn't decide where on disk a write will land at the moment your program calls write(). Instead it holds the data in the page cache and defers the on-disk allocation until the data is actually flushed — often a second or two later. This is delayed allocation (sometimes "allocate-on-flush").

Why wait? Because by the time the data is flushed, XFS often knows much more — how big the file ended up, whether more writes are coming — and can place the whole thing in one contiguous extent rather than scattering it. The result is dramatically less fragmentation and better throughput, especially for the streaming, large-file writes XFS was built for. The trade-off is the usual one for write-back caching: data that's been write()-en but not yet flushed lives only in RAM, so a hard power loss can lose the last couple of seconds of writes. The filesystem structure stays consistent (that's the journal's job, below) — you just lose the unflushed tail. Every modern filesystem makes a version of this bargain; XFS leans into it harder because its workloads benefit most.

Journaling — Crash Insurance Without the Hour-Long Wait

XFS is a journaling filesystem, and it journals metadata. Before it changes the real structures (the inodes, the directory entries, the free-space trees), it first writes a compact description of the intended change to a dedicated area called the log. If the machine loses power mid-update, the recovery code replays the log on the next mount — finishing the transactions that completed, discarding the ones that didn't — and the filesystem is consistent again in seconds, no matter how big the volume.

This is the feature that made the old fsck nightmare go away. On a pre-journaling filesystem like ext2, recovering after a crash meant scanning every inode and block on the disk for contradictions — hours of your server sitting there not booting. XFS just replays a small log. We'll see in a moment that this changes how you repair XFS too, in a way that surprises people coming from the ext world.

When To Reach For XFS (and When Not To)

Here's the opinionated part, because a reference page that won't tell you what to actually do isn't worth much.

Reach for XFS when:

  • The files are big. Media, backups, disk images, scientific datasets, anything where individual files run to gigabytes. Extents plus delayed allocation make XFS shine here.
  • There are many parallel writers. Lots of threads or processes writing to different files at once — busy databases, build farms, multi-tenant storage. The allocation-group design is purpose-built for exactly this.
  • The volume is large. XFS supports files and filesystems up to 8 exabytes; once you climb past 16 TB, ext4 starts running short on address space and XFS is the natural home.
  • You're on RHEL / CentOS / Rocky / AlmaLinux. It's the native default. Going with the grain of your distribution means the best-tested path, the documentation that matches, and one less surprise. Don't fight the platform without a reason.

Stay with ext4 when:

  • You might ever need to shrink the volume. This is the big one (next section). If you're not certain of your sizing, ext4 can shrink offline and XFS cannot shrink at all. That alone is reason enough on a volume you might over-provision.
  • You want the most-tested, most-boring path. ext4 has thirty years of every-conceivable-workload battle-testing and the most mature fsck tooling in existence. For a small general-purpose server with no large-file story, it's the safer default — predictable beats clever.
  • The workload is small-file, random I/O on a modest box. XFS historically lagged ext4 slightly on tiny-file workloads (modern versions have largely closed the gap, but the rule of thumb persists).

The short version: XFS for big, parallel, and large-volume; ext4 when you might need to shrink or just want the path with the fewest surprises. And if you're on Red Hat and the workload is large, the answer is simply "use the default" — they chose XFS for good reasons.

The One Thing Everyone Gets Wrong: XFS Grows But Never Shrinks

This is the fact to tattoo on the inside of your eyelids, because it's the one that turns a routine resize into a weekend.

XFS can grow online, while mounted and serving traffic, with a single command. It can never shrink — there is no operation, online or offline, mounted or unmounted, that makes an XFS filesystem smaller. The tool to do it does not exist, because the capability does not exist. Not "it's risky" or "you need a special flag" — it is simply absent.

Warning

If you put XFS on an LVM volume and over-provision it — say you give /data 2 TB "to be safe" and later realise you needed that 800 GB somewhere else — you are stuck. You cannot reclaim the space by shrinking the filesystem. Your only path is: back up the data, destroy the volume, recreate it at the right size with mkfs.xfs, and restore. On a live server with terabytes of data, that's hours of downtime over a sizing mistake you can't take back. Size XFS volumes for what you need, not for "just in case."

Why It Can't Shrink

The reason traces straight back to the allocation groups. Remember that XFS lays out its AGs across the volume in a fixed arrangement at format time, with the final allocation group anchored at the far end of the disk. To shrink the filesystem, you'd have to lop off that tail — but the last AG is a real, structural part of the filesystem, holding live data, inodes, and free-space trees. You can't just cut it off without relocating everything in it and rewriting the AG layout, the superblock geometry, and every internal reference that assumes the old number of groups. That's not a tweak; it's a from-scratch reorganisation of the entire on-disk structure — which is exactly the work that "back up, reformat, restore" does anyway. The XFS developers looked at the rare benefit of shrinking versus the enormous complexity and risk of doing it safely, and decided the cost wasn't worth it. The same allocation-group design that gives XFS its parallelism is the thing that nails its size to the floor.

ext4, by contrast, can shrink (offline, with resize2fs), because its layout is more flexible about giving back the tail. So the difference isn't an oversight in XFS — it's the price of the architecture, paid knowingly. Performance at scale in, flexibility out.

Growing, the Half That Does Work

The happy half is genuinely easy. When you've extended the underlying LVM volume or partition, you grow the XFS on top of it with xfs_growfs — and note the quirk that catches first-timers: it takes the mount point, not the device:

xfs_growfs /data        # correct — point at where it's mounted
xfs_growfs /dev/sdb1    # wrong — XFS growfs wants the mountpoint

This grows the filesystem to fill the newly-available space, live, with the volume still mounted and serving. New allocation groups are added at the end, the geometry is updated, and you're done. Growing forward is trivial; it's only going backward that's impossible.

How I Inspect and Manage It

These are the commands you'll actually use, roughly in the order you meet them on a real box.

mkfs.xfs — Creating It

mkfs.xfs /dev/sdb1

That's the whole thing for the common case — XFS picks sensible defaults (number of allocation groups, log size, block size) based on the volume. On a block device sitting on top of RAID, XFS will even try to read the array's stripe geometry and align its AGs to it, so writes land cleanly across the disks. If you're reformatting a device that already has a filesystem, mkfs.xfs refuses unless you add -f (force) — a small seatbelt against formatting the wrong disk. See mkfs for the family of format tools.

xfs_info — Reading the Geometry

xfs_info /data
meta-data=/dev/sdb1   isize=512    agcount=4, agsize=119206144 blks
         =            sectsz=512   attr=2, projid32bit=1
         =            crc=1        finobt=1, sparse=1
data     =            bsize=4096   blocks=476824576, imaxpct=25
naming   =version 2   bsize=4096   ascii-ci=0, ftype=1
log      =internal log bsize=4096  blocks=232832, version=2
realtime =none        extsz=4096   blocks=0, rtextents=0

The line that tells the story is agcount=4 — this volume has four allocation groups, each agsize blocks. That's the parallelism made visible: four independent regions four writers can hit at once. crc=1 means metadata checksumming is on (standard since the v5 on-disk format), so XFS can detect corruption in its own structures. bsize=4096 is the 4 KB block size matching the kernel's page size. log=internal means the journal lives inside the data volume (the usual setup). This is the XFS equivalent of tune2fs -l on ext4 — the superblock laid bare.

df and lsblk — Where It Sits

The everyday view comes from the same tools as any other filesystem. df -hT shows usage and confirms the type:

df -hT /data
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb1      xfs   1.8T  1.2T  617G  67% /data

And lsblk -f maps the whole stack from disk up through partition and LVM to the XFS sitting on top. One thing worth knowing: XFS allocates inodes dynamically, growing the inode pool as needed rather than fixing the count at format time the way ext4 does. That means the classic "disk full with gigabytes free because you ran out of inodes" surprise is far rarer on XFS — there's no fixed inode table to exhaust while blocks remain. (df -i will still report a number; XFS just isn't boxed in by it the way ext4 is.)

xfs_repair — Fixing It (and the Gotcha That Bites)

Here's where XFS departs from everything you know about ext4, so read carefully.

On ext4, the boot process runs fsck automatically and it both replays the journal and checks for damage. XFS does not work that way. There is no mount-time fsck-style check. Instead:

  1. Log replay happens at mount. When you simply mount a crashed XFS, the kernel replays the log to bring the structure back to consistency. This is the normal recovery, and it's automatic — usually you mount after a crash and it just works.
  2. xfs_repair is only for real damage — corruption the log can't fix (a bad disk, a software bug, a filesystem that's genuinely inconsistent). You run it on an unmounted filesystem, deliberately, when something is actually wrong:
umount /data
xfs_repair /dev/sdb1

Now the gotcha. xfs_repair refuses to run on a filesystem with a dirty log. If the filesystem crashed and the log still holds unreplayed transactions, xfs_repair will stop and tell you it cannot proceed — because repairing without first replaying the log would mean working on a known-inconsistent picture and could destroy data. The correct fix is almost always to mount the filesystem normally first (letting the kernel replay the log cleanly), then unmount, then repair if you still need to.

Danger

When xfs_repair refuses because of a dirty log, it offers an escape hatch: the -L flag, which zeroes the log — throwing away every transaction sitting in it unreplayed. This is not a "make the error go away" button. It discards in-flight metadata changes and can leave you with lost or corrupted files. Use -L only as a last resort, when the log genuinely cannot be replayed (e.g. the underlying disk is failing and a normal mount won't complete), and only with a backup in hand. The right first move, every time, is to mount the filesystem so the log replays itself — reach for -L only when that's impossible.

Cheat Sheet

# --- Create ---
mkfs.xfs /dev/sdb1                  # format a device as XFS (sensible defaults)
mkfs.xfs -f /dev/sdb1               # force-format a device that already has a filesystem
mkfs.xfs -L data /dev/sdb1          # set a filesystem label at creation

# --- Mount ---
mount /dev/sdb1 /data                                      # mount now
echo 'UUID=<uuid> /data xfs defaults,noatime 0 0' >> /etc/fstab   # persist (note: pass 0 — XFS has no boot fsck)

# --- Inspect ---
xfs_info /data                      # geometry: allocation groups, block size, log, CRC
df -hT /data                        # usage + confirm it's XFS
lsblk -f                            # the full block-device → filesystem map
blkid /dev/sdb1                     # type + UUID

# --- Grow (online, mounted) — the half that works ---
xfs_growfs /data                    # grow to fill the volume — takes the MOUNTPOINT, not the device

# --- Shrink ---
# There is no command. XFS cannot shrink. Back up, recreate smaller, restore.

# --- Repair (unmounted, only on real damage) ---
umount /data
xfs_repair /dev/sdb1                # replays nothing the kernel hasn't; fixes structural damage
xfs_repair -n /dev/sdb1             # DRY RUN — report problems, change nothing
xfs_repair -L /dev/sdb1             # DANGER: zero a dirty log (data loss) — last resort only

# --- Defragment (online) ---
xfs_fsr /data                       # reorganise fragmented extents while mounted

Pro Tip

Mount server data volumes with noatime. By default a filesystem updates each file's access timestamp every time the file is read, which on a busy XFS volume turns a flood of harmless reads into a flood of tiny metadata writes. noatime switches that off and is safe for the overwhelming majority of workloads — a small free performance win on exactly the large, busy volumes XFS is built for.

See Also

  • filesystem — the big-picture comparison of every Linux filesystem and how to choose
  • ext4 — the safe default; the one to pick when you might need to shrink
  • btrfs — copy-on-write with snapshots and checksums, a different set of trade-offs
  • journaling — the log that makes crash recovery take seconds instead of hours
  • inode — the metadata record XFS allocates dynamically instead of fixing at format time
  • LVM — the volume manager under XFS; where the "grow but never shrink" lesson bites hardest
  • partition — the slice of disk an XFS filesystem usually lives on
  • block device — the raw storage a filesystem is built on
  • page cache — where delayed-allocation writes wait before hitting disk
  • mkfs.xfs — the tool that creates an XFS filesystem
  • mkfs — the family of filesystem-creation commands
  • xfs_repair — repair structural damage on an unmounted XFS
  • xfs_growfs — grow an XFS online to fill its volume
  • df — space usage on every mounted filesystem
  • lsblk — map block devices to filesystems and mountpoints
  • mount — attach a filesystem to the directory tree
  • disk full — what happens when a volume runs out of space

Sized that XFS volume a little too generously and now want the space back?

CleverUptime watches every mounted filesystem on your server and tells you in plain language when one is trending toward full — so you catch a tight volume early, while you still have easy options, instead of the night it fills up.

Want to see your own server's health right now? One command, no signup, no install.

Check your server →