Create and Mount Filesystems in Linux

Tested on RHEL 10.2 (Coughlan)
Package xfsprogs 6.16.0-1.el10
e2fsprogs 1.47.1-5.el10
dosfstools 4.2-12.el10
util-linux 2.40.2-18.el10
parted 3.6-7.el10
lvm2 2.03.36-2.el10
file 5.45-9.el10
psmisc 23.6-8.el10
Applies to RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Ubuntu, Debian
Privilege sudo or root to create and mount filesystems; listing devices works as a normal user
Scope Turning a block device into a usable filesystem: picking between ext4, XFS and VFAT, creating them with mkfs, adding labels, mounting and unmounting, reading capacity correctly, and checking or repairing each type. Does not cover partitioning depth, LVM, or persistent fstab entries.
Related guides Create a filesystem on an LVM partition
Mount by UUID and label in fstab
parted command
mke2fs command
Format an SD card
WARNING

Every mkfs command on this page destroys access to whatever is already on the device you name, and it is irreversible once it proceeds. These tools do carry safeguards, but they are uneven. All three refuse a device that is currently mounted.

An existing filesystem on an idle device is treated quite differently: mkfs.xfs and mkfs.ext4 stop and make you override them explicitly with -f and -F, while mkfs.vfat performs no such check and simply overwrites what is there. Those flag letters are not portable either, since -f and -F mean something else entirely to mkfs.vfat, so never assume a force option carries the same meaning from one mkfs tool to the next.

None of them asks you to confirm the device is the one you meant. Verify the target with lsblk, findmnt, and a signature check as shown below, and never practise on a disk that holds data you would miss.

A new disk is not usable storage. The kernel gives you a block device the moment you attach the disk, but until something writes filesystem metadata onto it there is nowhere to put a file and nothing to mount.

This guide walks the whole path on a live host: find the right device, create XFS, ext4 and VFAT on three partitions of one spare disk, mount them, read their capacity honestly, and check or repair each one. I ran every command below on a 10 GB scratch disk and kept the real output, including the errors I ran into on purpose so you can recognise them.


What Is a Linux Filesystem?

A block device is raw addressable storage. /dev/sdb is the whole disk, /dev/sdb1 is a byte range inside it, and the kernel is happy to read and write either one without knowing anything about files. What it cannot do is answer a question like "give me the third file in this directory", because on a raw device there is no such thing as a directory.

A filesystem is the set of on-disk structures that answers those questions. Creating one writes:

  • A superblock recording the type, size, and identity of the filesystem
  • Inode tables or FAT allocation tables that map names and metadata to data blocks
  • Free space maps so the kernel knows where it may write next
  • A journal, on ext4 and XFS, so an interrupted write can be replayed rather than guessed at

A mount point is the directory where you attach that tree to the running system. The filesystem exists on the device whether or not it is mounted; mounting simply makes it reachable at a path.

Two consequences matter before you type anything. First, creating a filesystem destroys access to the previous one, because the new metadata replaces the map that told the kernel where the old files were. Second, a partition can exist perfectly well with no filesystem at all, which is the state my three scratch partitions are in right now.

The -f flag of lsblk prints filesystem information rather than sizes, so it shows that gap clearly:

bash
lsblk -f /dev/sdb
output
NAME   FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sdb                                           
├─sdb1                                        
├─sdb2                                        
└─sdb3

Three partitions exist and the kernel can address all of them, but every filesystem column is empty. Nothing here can be mounted yet.

blkid reads the device directly and reports what it recognises, which separates partition identity from filesystem identity:

bash
blkid /dev/sdb1
output
/dev/sdb1: PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"

The PARTLABEL and PARTUUID come from the GPT partition table, so they describe the slot on the disk. There is no TYPE and no UUID, because those belong to a filesystem that does not exist yet. Keeping those two families of identifier apart saves a lot of confusion later, since mount LABEL= matches the filesystem label and never the partition name.


Filesystem Creation Quick Reference

The whole workflow is six steps, and the commands change very little between filesystem types:

Step Command
Confirm the device is free lsblk -f /dev/sdb, findmnt -S /dev/sdb1, then wipefs /dev/sdb1
Create the filesystem mkfs.xfs /dev/sdb1, mkfs.ext4 /dev/sdb2, or mkfs.vfat /dev/sdb3
Confirm what was written blkid /dev/sdb1
Mount it mkdir -p /mnt/data then mount /dev/sdb1 /mnt/data
Verify the mount findmnt /mnt/data and df -hT /mnt/data
Unmount when finished umount /mnt/data

Creation and labelling differ per type, and the label flags are not interchangeable:

Filesystem Create Create with a label Repair tool
XFS mkfs.xfs /dev/sdb1 mkfs.xfs -L labxfs /dev/sdb1 xfs_repair
ext4 mkfs.ext4 /dev/sdb2 mkfs.ext4 -L labext4 /dev/sdb2 e2fsck
VFAT mkfs.vfat /dev/sdb3 mkfs.vfat -n LABVFAT /dev/sdb3 fsck.vfat

Identify the Correct Block Device

This is the step that prevents a bad afternoon, and it deserves more than a glance. mkfs will format the root disk as readily as a scratch disk if you name it, so the goal is positive proof that the device you are about to write to holds nothing you need.

Start with the whole picture. Run lsblk with no arguments to see every disk, its partitions, and where each one is currently attached:

bash
lsblk
output
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda             8:0    0   30G  0 disk 
├─sda1          8:1    0    1M  0 part 
├─sda2          8:2    0    2G  0 part /boot
└─sda3          8:3    0   28G  0 part 
  ├─rhel-root 253:0    0   25G  0 lvm  /
  └─rhel-swap 253:1    0    3G  0 lvm  [SWAP]
sdb             8:16   0   10G  0 disk 
├─sdb1          8:17   0    3G  0 part 
├─sdb2          8:18   0    3G  0 part 
└─sdb3          8:19   0    2G  0 part 
sr0            11:0    1 1024M  0 rom

The MOUNTPOINTS column does most of the work. On sda there is a /boot partition and an LVM layer carrying / and swap, so that disk is the running system. Every partition on sdb has an empty mount point, which is the first sign it is idle. Reading these trees is worth practising on your own hosts, and the lsblk command guide covers the other output columns.

An empty mount point column is suggestive rather than conclusive, so ask about the specific device. findmnt -S searches the mount table by source device:

bash
findmnt -S /dev/sdb1

Nothing is printed and the exit status is 1, which is findmnt saying it found no mount using that device. Silence here is the answer you want; if the device were mounted anywhere, even somewhere you had forgotten, this command would print the row.

Absence from the mount table still does not rule out the device being claimed by a storage layer. A disk can be an LVM physical volume with no mounted filesystem of its own, and formatting it would destroy a volume group. Ask LVM directly:

bash
pvs
output
PV         VG   Fmt  Attr PSize  PFree
  /dev/sda3  rhel lvm2 a--  28.00g    0

Only /dev/sda3 is a physical volume, so sdb is not part of any volume group. If your target disk did appear in this list, stop and work through creating a filesystem on an LVM partition instead, because the device you format is the logical volume rather than the disk.

One last look at the raw bytes tells you whether anything recognisable is already there. The -s flag lets file read a block device rather than describing the device node itself:

bash
file -s /dev/sdb1
output
/dev/sdb1: data

Read that answer narrowly. Plain data means file did not recognise a known filesystem or other format, and it does not prove that every block is unused. The device could still hold application data, a database written straight to raw storage, or metadata this tool has no rule for. Had it said SGI XFS filesystem data or Linux rev 1.0 ext4 filesystem data, something was already there and I would want to know what before overwriting it.

wipefs is the better tool for the same question, because it looks specifically for the signatures that matter here. Given no erase options it only reports:

bash
wipefs /dev/sdb1

Nothing is printed, which means wipefs found no filesystem, RAID member, or partition-table signature on the device. Run the same command against the partition after it has been formatted and the difference is obvious:

output
DEVICE OFFSET TYPE UUID                                 LABEL
sdb1   0x0    xfs  a2d95b7d-0394-40d5-a667-6c69da0dc048 labxfs

The row is a signature wipefs would erase if you asked it to with -a, and listing it changes nothing on disk. That is what makes it safe to run first.

It also catches cases file -s misses, such as a stale RAID superblock or a partition table on a disk you thought was blank, so it belongs in the same sequence as lsblk, findmnt, and pvs rather than as a replacement for any of them.

If you want the root device named explicitly rather than inferred from a tree, ask about / on its own:

bash
findmnt -n -o SOURCE /
output
/dev/mapper/rhel-root

That is the one device that must never appear in an mkfs command on a running system.

Create the partitions this walkthrough uses

My scratch disk arrived with an empty GPT and no partitions, so I carved out three before formatting anything. Partitioning is its own topic and the parted command guide goes through the options properly, but for completeness here is the label I wrote first. On a disk that already has a layout worth keeping, back up the partition table before mklabel replaces it:

bash
parted -s /dev/sdb mklabel gpt

Writing a fresh GPT prints nothing on success and discards any previous partition table, so it belongs only on a disk you have already confirmed is spare. The first partition takes 3 GB starting at the conventional 1 MiB offset, which keeps it aligned:

bash
parted -s /dev/sdb mkpart xfsdata 1MiB 3GiB

Each mkpart is silent in script mode, so there is nothing to read until the table is complete. The second partition starts where the first ended:

bash
parted -s /dev/sdb mkpart ext4data 3GiB 6GiB

The third is smaller at 2 GB, which is plenty for the VFAT filesystem and leaves the last 2 GB of the disk unallocated:

bash
parted -s /dev/sdb mkpart vfatdata 6GiB 8GiB

The names xfsdata, ext4data, and vfatdata are GPT partition names, not filesystem labels, which is exactly the PARTLABEL field you saw in the earlier blkid output. Confirm the kernel picked up all three before you format them:

bash
lsblk /dev/sdb
output
NAME   MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb      8:16   0  10G  0 disk 
├─sdb1   8:17   0   3G  0 part 
├─sdb2   8:18   0   3G  0 part 
└─sdb3   8:19   0   2G  0 part

Two 3 GB partitions and one 2 GB partition, with 2 GB of the disk left unallocated. If a partition you just created is missing here, the kernel has not re-read the table yet and udevadm settle or partprobe will usually fix it.


Choose Between ext4, XFS and VFAT

The three filesystems on this page are not competing for the same job, so the choice is usually obvious once you know what the storage is for.

Property XFS ext4 VFAT
Typical use Default on RHEL and derivatives, large files, high throughput General purpose Linux storage, wide tooling support Removable media, EFI system partitions, sharing with Windows
Journaling Yes, metadata journal Yes, metadata and optionally data No
Unix ownership and permissions Full Full None, synthesised at mount time
Readable on Windows and macOS No, without extra software No, without extra software Yes, natively
Grow Online with xfs_growfs while mounted Online with resize2fs Not practical in place
Shrink Not supported Supported offline Not practical in place
Largest single file Very large, effectively not a limit here Very large, effectively not a limit here 4 GiB
Repair tool xfs_repair e2fsck fsck.vfat
Label limit 12 characters 16 characters 11 characters, uppercase

Two rows drive most real decisions. The 4 GiB file size limit rules VFAT out for anything but interchange and boot partitions, because a single large archive or disk image will simply fail to copy. The shrink row matters when you are not sure how big a volume should be: XFS grows online but never shrinks, so if you expect to reclaim space later, ext4 leaves you an option that XFS does not.

For a data volume on an enterprise Linux host, XFS is the sensible default and is what the installer already chose for /. Pick ext4 when you want the option to shrink, or when a tool you depend on expects it. Pick VFAT only when something outside Linux has to read the media.


Create an XFS Filesystem

With the device confirmed idle, creating the filesystem is one command. mkfs.xfs prints the geometry it chose, which is worth reading rather than skipping past:

bash
mkfs.xfs /dev/sdb1
output
meta-data=/dev/sdb1              isize=512    agcount=4, agsize=196544 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=1
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=1
         =                       exchange=0   metadir=0
data     =                       bsize=4096   blocks=786176, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1, parent=0
log      =internal log           bsize=4096   blocks=16384, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
         =                       rgcount=0    rgsize=0 extents
         =                       zoned=0      start=0 reserved=0

Three numbers explain the layout. The data line shows 786176 blocks of bsize=4096, which is the 3 GB of usable space. agcount=4 means XFS split that into four allocation groups it can work on in parallel. The log line shows a 16384 block internal journal living inside the same device, and that journal is part of why the filesystem reports slightly less free space than the partition size.

Creation is silent about identity, so ask blkid what now exists on the device:

bash
blkid /dev/sdb1
output
/dev/sdb1: UUID="82c40344-5048-40d5-adc3-cef76b9f7473" BLOCK_SIZE="512" TYPE="xfs" PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"

Compare this with the same command earlier in the article. TYPE="xfs" and a UUID have appeared, generated during creation, while PARTLABEL and PARTUUID are unchanged because the partition table was never touched. There is no LABEL field, because I did not ask for one.

The signature is now visible to tools that read raw bytes too, which is the difference file -s reported as plain data before:

bash
file -s /dev/sdb1
output
/dev/sdb1: SGI XFS filesystem data (blksz 4096, inosz 512, v2 dirs)

XFS is often reported under its SGI heritage, which is the same filesystem despite the older name.

Now the safety behaviour worth knowing before you need it. Running the same mkfs.xfs command a second time does not quietly reformat:

bash
mkfs.xfs /dev/sdb1
output
mkfs.xfs: /dev/sdb1 appears to contain an existing filesystem (xfs).
mkfs.xfs: Use the -f option to force overwrite.

The tool detected the signature it wrote a moment ago and refused. That refusal is the last automatic guard between a mistyped device name and lost data, so treat it as information rather than an obstacle.

WARNING
mkfs.xfs -f skips exactly that check. Use it only when you have already confirmed which device you are naming and that its contents are expendable. On the wrong device, -f turns a harmless error message into an unrecoverable format.

Because I do want a label on this filesystem and the device is already formatted, -f is genuinely required here. The output is the same geometry report, trimmed to the first lines:

bash
mkfs.xfs -f -L labxfs /dev/sdb1
output
meta-data=/dev/sdb1              isize=512    agcount=4, agsize=196544 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=1
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=1

One detail in the result catches people out later, so check the identity again:

bash
blkid /dev/sdb1
output
/dev/sdb1: LABEL="labxfs" UUID="a2d95b7d-0394-40d5-a667-6c69da0dc048" BLOCK_SIZE="512" TYPE="xfs" PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"

The LABEL arrived as requested, but the UUID also changed from 82c40344 to a2d95b7d. Reformatting mints a brand new filesystem, so any /etc/fstab line or script referring to the old UUID now points at something that no longer exists. That is a routine cause of a host that boots into emergency mode after a "quick reformat".


Create an ext4 Filesystem

ext4 lives on the second partition. mke2fs accepts the label at creation time with -L, so there is no reason to add it afterwards:

bash
mkfs.ext4 -L labext4 /dev/sdb2
output
mke2fs 1.47.1 (20-May-2024)
Creating filesystem with 786432 4k blocks and 196608 inodes
Filesystem UUID: 830f3cbf-d586-4d43-aa50-fde3786a3569
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912

Allocating group tables:  0/24     done                            
Writing inode tables:  0/24     done                            
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information:  0/24     done

This output is more talkative than XFS and each line is a commitment made on your behalf:

  • 196608 inodes, which caps how many files this filesystem can ever hold regardless of free space
  • Superblock backups at five known block numbers, which is what lets e2fsck recover from a damaged primary superblock
  • A 16384 block journal
  • A UUID generated for you at creation time

The mke2fs command reference covers the tuning options if the defaults do not suit a particular workload.

Now a small surprise from my own run. Checking the result immediately with lsblk looked as though nothing had happened:

bash
lsblk -f /dev/sdb2
output
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sdb2

Empty columns, right after a successful format. The filesystem was fine; lsblk reports properties that udev caches, and udev had not yet re-read the device. blkid goes to the disk itself and told the truth straight away:

bash
blkid /dev/sdb2
output
/dev/sdb2: LABEL="labext4" UUID="830f3cbf-d586-4d43-aa50-fde3786a3569" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="ext4data" PARTUUID="d8dc1e1e-0e48-455d-9432-935cc3e10270"

Label, UUID, type, and a 4096 byte block size, all present. A second later the cache caught up on its own and lsblk -f showed the same values. When the two disagree, believe blkid and give udev a moment, or prompt it with udevadm settle.


Create a VFAT Filesystem

VFAT needs the dosfstools package, which is not always installed on a minimal server. Confirm it before reaching for the command:

bash
rpm -q dosfstools
output
dosfstools-4.2-12.el10.x86_64

The package is present, so mkfs.vfat is available. On Debian and Ubuntu the package carries the same name and installs with apt install dosfstools.

VFAT labels use -n rather than -L, and they are conventionally uppercase:

bash
mkfs.vfat -n LABVFAT /dev/sdb3
output
mkfs.fat 4.2 (2021-01-31)

One version line and nothing else. FAT has far less metadata to build than ext4 or XFS, so there is no geometry report to print and no journal to create. Silence here means success.

The identity of a FAT filesystem looks noticeably different from the other two:

bash
blkid /dev/sdb3
output
/dev/sdb3: LABEL_FATBOOT="LABVFAT" LABEL="LABVFAT" UUID="7A93-48B7" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="vfatdata" PARTUUID="c154f0aa-70bc-446b-8a5d-98343bbbd428"

The UUID is 7A93-48B7, which is eight hexadecimal digits rather than the long random identifier ext4 and XFS use. FAT has no field for a real UUID, so this is a volume serial number that tools present in the UUID position. It is unique enough for /etc/fstab in practice, but it carries far less entropy, and reformatting will produce a different one.

The LABEL_FATBOOT entry is the copy of the label stored in the boot sector alongside the one in the root directory.

Reading the raw bytes shows how much of FAT's design is inherited from DOS:

bash
file -s /dev/sdb3
output
/dev/sdb3: DOS/MBR boot sector, code offset 0x58+2, OEM-ID "mkfs.fat", sectors/cluster 8, Media descriptor 0xf8, sectors/track 63, heads 255, hidden sectors 12582912, sectors 4194288 (volumes > 32 MB), FAT (32 bit), sectors/FAT 4088, serial number 0x7a9348b7, label: "LABVFAT    "

Two details are worth pulling out. FAT (32 bit) confirms mkfs.vfat chose FAT32 for a 2 GB device, and the label is stored padded to eleven characters as "LABVFAT ", which is the fixed-width field FAT allows. The sectors per track and heads values are geometry from an era of physical cylinders and are ignored on modern hardware.

What this filesystem cannot do is store Unix ownership, which changes how files appear once it is mounted. That behaviour has its own section below, because it surprises people who reach for chmod and find it silently ineffective. For the removable media case specifically, formatting an SD card walks through the same tools on a device you can carry.


Assign Filesystem Labels During Creation

A label is a human readable name stored inside the filesystem. It matters because mounting by label survives device renaming: today's /dev/sdb1 can become /dev/sdc1 after you add a disk, but LABEL=labxfs still finds the right filesystem.

Each tool spells the option differently and enforces different limits:

Filesystem Flag at creation Maximum length Case handling
XFS -L labxfs 12 characters Preserved
ext4 -L labext4 16 characters Preserved
VFAT -n LABVFAT 11 characters Uppercase expected

Those limits are enforced, not advisory, and each tool reacts in its own way. I tried all three against a 400 MB scratch image file rather than a real partition, so nothing important was at risk. XFS rejects an over-long label outright and refuses to run:

bash
mkfs.xfs -f -L thirteenchars /root/label-test.img
output
Invalid value thirteenchars for -L option
Usage: mkfs.xfs

Thirteen characters is one too many and the command stops without creating anything. You get an error you cannot miss.

mke2fs is more forgiving and that forgiveness is the trap:

bash
mkfs.ext4 -q -L abcdefghijklmnopqrst /root/label-test.img
output
Warning: label too long; will be truncated to 'abcdefghijklmnop'

It created the filesystem successfully with a label you did not ask for. If a script later mounts LABEL=abcdefghijklmnopqrst, it will fail while blkid shows the truncated sixteen character version, and the warning that explained it scrolled past during the install.

VFAT enforces its own shorter limit and refuses:

bash
mkfs.vfat -n TWELVECHARSX /root/label-test.img
output
mkfs.vfat: Label can be no longer than 11 characters
mkfs.fat 4.2 (2021-01-31)

Eleven characters is the hard ceiling. Lowercase is accepted but warned about, because other operating systems reading the media may not handle it consistently:

bash
mkfs.vfat -n lowercase /root/label-test.img
output
mkfs.fat: Warning: lowercase labels might not work properly on some systems
mkfs.fat 4.2 (2021-01-31)

The filesystem is created either way, so this one is a judgement call. For media that will only ever be read on Linux the case does not matter; for a USB stick heading to a Windows machine, use uppercase and avoid the question.

Labels are only half of the persistence story. Making a mount survive a reboot means writing an entry that refers to the label or UUID, and mounting by UUID and label in fstab covers the file format and the failure modes that come with it.


Mount a Filesystem

A filesystem you cannot reach is not much use. Mounting attaches it to a directory, and that directory needs to exist first:

bash
mkdir -p /mnt/xfsdata

mkdir prints nothing when it succeeds, and -p means it will not complain if the directory is already there. Any empty directory works as a mount point; /mnt is the conventional place for storage you attach by hand.

With somewhere to put it, attach the XFS filesystem:

bash
mount /dev/sdb1 /mnt/xfsdata

Success is silent, which is why the next command matters. findmnt queries the kernel's mount table and shows what is actually attached rather than what you asked for:

bash
findmnt /mnt/xfsdata
output
TARGET       SOURCE    FSTYPE OPTIONS
/mnt/xfsdata /dev/sdb1 xfs    rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota

Four things are confirmed at once: the target path, the device behind it, the type the kernel detected without being told, and the options in force. rw means writable, and the rest are XFS defaults the kernel filled in. Notice I never passed -t xfs; mount reads the superblock and works out the type itself.

A mount that reports correctly can still be read-only or otherwise unusable, so write something before you trust it:

bash
echo "created on xfs" > /mnt/xfsdata/notes.txt

Redirection into a new file prints nothing, so confirm the file landed with the size and ownership you expect:

bash
ls -l /mnt/xfsdata/notes.txt
output
-rw-r--r--. 1 root root 15 Aug  8 16:11 /mnt/xfsdata/notes.txt

Fifteen bytes owned by root with mode 644. XFS stored the ownership and permissions exactly as the process created them, which is the behaviour you should keep in mind when the VFAT filesystem later refuses to do the same.

Finally, ask how much space the mounted filesystem actually offers. The -T flag adds the type column, which is useful when several filesystems are mounted:

bash
df -hT /mnt/xfsdata
output
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb1      xfs   3.0G   90M  2.9G   3% /mnt/xfsdata

The partition is 3 GB and df agrees on the size, but 90 MB is already used on a filesystem holding a single fifteen byte file. That is the XFS metadata and journal from the creation output, not your data, and the next section on capacity comes back to it.

Mount by label or UUID instead of device name

Device names are assigned in the order the kernel finds disks, so they can change when hardware does. Mounting by label removes that dependency. Unmount first, since the device is currently attached:

bash
umount /mnt/xfsdata

With the mount point free, ask for the filesystem by the label set at creation:

bash
mount LABEL=labxfs /mnt/xfsdata

Silent again, so confirm which device the label resolved to:

bash
findmnt -n -o TARGET,SOURCE,FSTYPE /mnt/xfsdata
output
/mnt/xfsdata /dev/sdb1 xfs

The label matched /dev/sdb1 without me naming it. Behind the scenes udev maintains symlinks under /dev/disk/by-label/ and mount follows them, which is why a label you have just changed may need udevadm settle before it can be used this way.

UUIDs work identically and are stronger, because a UUID is generated per filesystem while two disks can easily carry the same label:

bash
umount /mnt/xfsdata

The unmount is silent, so go straight to mounting by the UUID that blkid reported earlier:

bash
mount UUID=a2d95b7d-0394-40d5-a667-6c69da0dc048 /mnt/xfsdata

Confirm it resolved to the same device as the label did:

bash
findmnt -n -o TARGET,SOURCE,FSTYPE /mnt/xfsdata
output
/mnt/xfsdata /dev/sdb1 xfs

Same device, reached three different ways. For anything permanent, UUID is the safest of the three, and it is what an installer writes into /etc/fstab for exactly that reason.

Mount the ext4 filesystem too

The ext4 filesystem created earlier is still sitting on /dev/sdb2 with nothing attached to it, and the capacity and repair sections further down both expect it to be mounted. The order is the same for every filesystem type: make the mount point, mount the device, verify. Start with the directory:

bash
mkdir -p /mnt/ext4data

Then attach the device, again without naming a type:

bash
mount /dev/sdb2 /mnt/ext4data

Silence, so confirm it rather than assuming:

bash
findmnt /mnt/ext4data
output
TARGET        SOURCE    FSTYPE OPTIONS
/mnt/ext4data /dev/sdb2 ext4   rw,relatime,seclabel

The kernel detected ext4 from the superblock and mounted it read-write. Compare that OPTIONS column with the XFS one above: ext4 needed three options where XFS listed nine, because most XFS behaviour is recorded in the geometry at creation time while ext4 keeps its defaults in the superblock. Both filesystems stay mounted from here through the capacity section, so leave them attached.


Fix VFAT Ownership and Permissions with Mount Options

This section exists because the behaviour below looks like a bug the first time you meet it. The third filesystem needs the same treatment as the other two, so give it a mount point first:

bash
mkdir -p /mnt/vfatdata

Then mount the VFAT filesystem the same way you mounted XFS and ext4:

bash
mount /dev/sdb3 /mnt/vfatdata

Verify it before drawing any conclusions from what follows, because the options are the whole story in this section:

bash
findmnt /mnt/vfatdata
output
TARGET        SOURCE    FSTYPE OPTIONS
/mnt/vfatdata /dev/sdb3 vfat   rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro

Nothing was passed on the command line, yet the kernel recorded fmask=0022, dmask=0022, and a character set. Those defaults are where the surprising behaviour below comes from. Now create a file and look at what the kernel reports for it:

bash
echo "created on vfat" > /mnt/vfatdata/notes.txt

The write succeeds silently. The interesting part is how that file is described:

bash
ls -l /mnt/vfatdata/notes.txt
output
-rwxr-xr-x. 1 root root 16 Aug  8 16:11 /mnt/vfatdata/notes.txt

Mode 755 on a text file nobody asked to be executable. On the ext4 filesystem the identical operation produced 644. Nothing went wrong: FAT has no field on disk for a Unix mode, so the kernel synthesises one for every file from the fmask mount option, and the default produces 755.

Because the mode is invented rather than stored, changing it does not work in the way the exit status suggests:

bash
chmod 600 /mnt/vfatdata/notes.txt

chmod exits 0 with no error, which normally means it did the job. Check whether anything actually changed:

bash
ls -l /mnt/vfatdata/notes.txt
output
-rwxr-xr-x. 1 root root 16 Aug  8 16:11 /mnt/vfatdata/notes.txt

Still 755. The call was accepted and discarded, because there is nowhere to record mode 600. This silent no-op is worth remembering when a script that hardens permissions appears to run cleanly on removable media and changes nothing.

Ownership behaves the same way, except it does report an error:

bash
chown student /mnt/vfatdata/notes.txt
output
chown: changing ownership of '/mnt/vfatdata/notes.txt': Operation not permitted

Running as root and still not permitted, because the filesystem cannot represent per-file ownership at all. The practical consequence is that an ordinary user cannot write here either:

bash
runuser -u student -- touch /mnt/vfatdata/fromstudent.txt
output
touch: cannot touch '/mnt/vfatdata/fromstudent.txt': Permission denied

The whole filesystem is presented as owned by root, so student has read and execute access and nothing more. Since the ownership is a property of the mount rather than the files, the fix is to mount it differently. Unmount first:

bash
umount /mnt/vfatdata

Then mount again, naming who the files should appear to belong to:

bash
mount -o uid=student,gid=student /dev/sdb3 /mnt/vfatdata

The mount is silent, so read back the options the kernel recorded:

bash
findmnt /mnt/vfatdata
output
TARGET        SOURCE    FSTYPE OPTIONS
/mnt/vfatdata /dev/sdb3 vfat   rw,relatime,uid=1001,gid=1001,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro

The names resolved to uid=1001,gid=1001. Now look at the file that already existed before this mount:

bash
ls -l /mnt/vfatdata
output
total 4
-rwxr-xr-x. 1 student student 16 Aug  8 16:11 notes.txt

The same notes.txt from earlier now belongs to student, without anything being rewritten on disk. That is the clearest possible demonstration that ownership here is a display decision made at mount time. Confirm the practical effect:

bash
runuser -u student -- touch /mnt/vfatdata/fromstudent.txt

The command produces no output, which means it worked this time. Use fmask and dmask alongside uid and gid when you need the synthesised modes to be narrower than the default 755.


Understand Filesystem Capacity

Three different numbers get called "size" and mixing them up leads to bad decisions about disks. This section works on the ext4 filesystem mounted earlier, so confirm it is still attached before writing to it:

bash
findmnt /mnt/ext4data
output
TARGET        SOURCE    FSTYPE OPTIONS
/mnt/ext4data /dev/sdb2 ext4   rw,relatime,seclabel

If that prints nothing, the filesystem is not mounted and mount /dev/sdb2 /mnt/ext4data will put it back. With a confirmed mount, write a file large enough to be visible before comparing the numbers:

bash
dd if=/dev/zero of=/mnt/ext4data/big.bin bs=1M count=200
output
200+0 records in
200+0 records out
209715200 bytes (210 MB, 200 MiB) copied, 0.111265 s, 1.9 GB/s

Exactly 200 MiB of zeroes now occupy real blocks on the ext4 filesystem. Ask df what that did to the filesystem:

bash
df -hT /mnt/ext4data
output
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb2      ext4  2.9G  201M  2.6G   8% /mnt/ext4data

Size is 2.9G even though the partition is 3G, and Used is 201M rather than the 200M I wrote.

Both differences are metadata: the filesystem spent part of the device on inode tables and a journal when it was created, and the directory entry plus indirect blocks for a 200 MiB file cost slightly more than the file contents.

du measures something different, and the distinction is the useful one. It walks the directory tree and adds up what the files occupy:

bash
du -sh /mnt/ext4data
output
201M	/mnt/ext4data

Here du and the Used column agree, because I put one file on an otherwise empty filesystem.

They diverge in real life: du cannot see space consumed by filesystem metadata, nor space held by a deleted file that a running process still has open, which is the classic reason a disk reports itself full while du insists there is nothing there. The df and du command guide goes deeper into that gap.

The third number is the device itself, and lsblk can show both at once:

bash
lsblk -o NAME,SIZE,FSTYPE,FSSIZE,FSAVAIL /dev/sdb2
output
NAME SIZE FSTYPE FSSIZE FSAVAIL
sdb2   3G ext4     2.9G    2.5G

SIZE is the partition at 3G and FSSIZE is the filesystem at 2.9G, side by side in one row. Roughly 100 MB of the partition is filesystem overhead you will never store files in.

There is a further deduction that df hides from ordinary users. ext4 keeps a percentage of blocks for root so that a full filesystem does not immediately break privileged processes:

bash
tune2fs -l /dev/sdb2 | grep -E "Block count|Reserved block count"
output
Block count:              786432
Reserved block count:     39321

39321 of 786432 blocks is 5 percent, which at 4 KB per block is about 161 MB set aside. On a root filesystem that reserve is a safety net worth having.

On a large data volume it is space you paid for and cannot use, which is why mkfs.ext4 -m 1 or a later tune2fs -m 1 is common on dedicated data disks. XFS has no equivalent reserve, which is part of why its Avail figure looked closer to its Size earlier.


Unmount a Filesystem

Unmounting flushes pending writes and detaches the filesystem. You can name either end of the relationship, and the mount point is the usual choice:

bash
umount /mnt/xfsdata

When it works there is no output at all. Run it a second time and you get the one error that is not a problem:

output
umount: /mnt/xfsdata: not mounted.

Exit status 32 with nothing detached, because there was nothing there to detach. That message means you have already reached the state you wanted.

The failure worth understanding is the other one, and it is almost always something using the filesystem. Reproducing it needs the filesystem back:

bash
mount /dev/sdb1 /mnt/xfsdata

With it mounted again, standing inside it is enough to block the unmount:

bash
cd /mnt/xfsdata

Changing directory is silent, and now my shell's working directory is inside the filesystem I am about to detach:

bash
umount /mnt/xfsdata
output
umount: /mnt/xfsdata: target is busy.

The exit status is 32 and nothing was unmounted. A working directory counts as usage just as much as an open file does, which is why this happens so often to the person doing the maintenance rather than to a service.

Rather than guessing, ask which processes are involved. fuser -vm lists everything using the filesystem containing the given path:

bash
fuser -vm /mnt/xfsdata
output
USER        PID ACCESS COMMAND
/mnt/xfsdata:        root     kernel mount /mnt/xfsdata
                     root      22595 ..c.. bash

The ACCESS column is the answer. The c flag means the process is using the directory as its current working directory, and the culprit is my own bash. The kernel row is the mount itself rather than a process, so it is never something you act on. If you saw f instead of c, a process has a file open on the filesystem, and you would want to know which service that is before killing anything.

The fix here needs no force and no -l lazy unmount, just moving out of the way:

bash
cd /root

With nothing holding the filesystem, the same command that failed now succeeds:

bash
umount /mnt/xfsdata

Silence, which means the filesystem is detached. Confirm rather than assume:

bash
findmnt /mnt/xfsdata

No output and exit status 1, which is findmnt reporting that nothing is mounted there. The directory /mnt/xfsdata still exists and is empty again, because the files live on the device rather than in the mount point.

You can also unmount by device, which is handy when you cannot remember where something was attached:

bash
umount /dev/sdb3

That detaches the VFAT filesystem from whatever directory it was on, with the same silence on success. Naming the device and naming the mount point are equivalent as long as the device is mounted in exactly one place.


Check and Repair ext4 Filesystems

Two different operations get called the same thing here, and keeping them apart is the whole skill. Checking reads metadata and reports what it finds. Repairing rewrites that metadata to make it consistent again, which means throwing away whatever cannot be reconciled.

Both belong on an unmounted filesystem, and the reason is the same in each case: a live filesystem changes underneath the tool, so what it reads may never have existed as a single consistent state.

The ext4 filesystem is still mounted from the capacity section, so start by doing the tempting thing and checking it in place. The -n flag answers no to every question e2fsck would ask, which is what makes it safe to try:

bash
e2fsck -n /dev/sdb2
output
e2fsck 1.47.1 (20-May-2024)
Warning!  /dev/sdb2 is mounted.
Warning: skipping journal recovery because doing a read-only filesystem check.
labext4: clean, 14/196608 files, 82238/786432 blocks

Read the two warnings rather than the verdict. e2fsck documents that the results it reports are not valid when the filesystem is mounted, so clean on that last line is not a finding you can act on.

The second warning shows one reason why: journal recovery was skipped, so a filesystem with an unreplayed journal can be reported as damaged purely because of what was not applied. Treat this output as a message telling you to unmount, not as a diagnosis.

The documented order on RHEL is to mount the filesystem and then unmount it, which lets the kernel replay the journal, and only then check the unmounted device. The filesystem has been mounted throughout this walkthrough, so the unmount is the step that matters:

bash
umount /mnt/ext4data

A clean unmount records that fact in the superblock, and tune2fs -l reads it back without checking anything:

bash
tune2fs -l /dev/sdb2 | grep -E "Filesystem state|Last checked"
output
Filesystem state:         clean
Last checked:             Sat Aug  8 16:50:22 2026

clean means the journal has no outstanding work, so a check now sees the same filesystem the kernel last agreed on. A value of not clean means the journal still holds updates, which the kernel replays automatically at the next mount, and that is exactly why the mount-then-unmount step comes first.

Now run the same -n check that was unreliable a moment ago:

bash
e2fsck -n /dev/sdb2
output
e2fsck 1.47.1 (20-May-2024)
labext4: clean, 14/196608 files, 82238/786432 blocks

Same verdict, but this time with no warnings above it, which is the difference between a result and a guess. Fourteen inodes and 82238 blocks in use, exit status 0.

-f forces a full check even when the superblock says clean, which is what you want when you are actually investigating rather than confirming:

bash
e2fsck -f /dev/sdb2
output
e2fsck 1.47.1 (20-May-2024)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
labext4: 14/196608 files (0.0% non-contiguous), 82238/786432 blocks

All five passes completed with no questions asked, and the exit status was 0.

This is where checking turns into repair on a filesystem that is not healthy: each pass reports the problems it finds and asks whether to correct them, so the same command that only reported here would start rewriting metadata as soon as you answered yes. That is why -y, which answers yes to everything, deserves real thought before it goes into a script, and why -n is the right first command on a filesystem you do not yet understand.

When the filesystem you need to check is the root filesystem, you cannot unmount it from a running system, and repairing ext4 from rescue mode covers that path.

One side effect is worth knowing about. Read the superblock again:

bash
tune2fs -l /dev/sdb2 | grep -E "Filesystem state|Last checked"
output
Filesystem state:         clean
Last checked:             Sat Aug  8 16:52:01 2026

e2fsck -f wrote the new Last checked time even though it changed nothing else, so a full check is never quite read-only in the way -n is. That distinction matters when you are gathering evidence from a filesystem you may need to hand to someone else.

Put the filesystem back where the rest of the walkthrough expects it:

bash
mount /dev/sdb2 /mnt/ext4data

The mount is silent, and the filesystem is available again with the 200 MiB file intact.


Check and Repair XFS Filesystems

XFS is the one type on this page where the obvious command is the wrong one. fsck.xfs exists, so fsck /dev/sdb1 and any generic tooling that calls the per-type helper will find it, but it does not check anything:

bash
fsck.xfs /dev/sdb1
output
If you wish to check the consistency of an XFS filesystem or
repair a damaged filesystem, see xfs_repair(8).

It printed advice and exited 0, which is a success status on a filesystem it never read. The file is not even a binary:

bash
file -L "$(command -v fsck.xfs)"
output
/usr/sbin/fsck.xfs: a /usr/bin/sh -f script, ASCII text executable

A shell script shipped in xfsprogs purely so the fsck framework has something to call at boot, where XFS needs no check because it replays its log at mount. Treat a clean exit from fsck.xfs as no information at all. xfs_repair is the real tool, and like e2fsck it wants the device unmounted and the log already replayed by a clean unmount. The -n flag means no modify, so it reports what it would do without touching anything:

bash
xfs_repair -n /dev/sdb1
output
Phase 1 - find and verify superblock...
Phase 2 - using internal log
        - zero log...
        - scan filesystem freespace and inode maps...
        - found root inode chunk
Phase 3 - for each AG...
        - scan (but don't clear) agi unlinked lists...
        - process known inodes and perform inode discovery...
        - agno = 0
        - agno = 1
        - agno = 2
        - agno = 3
        - process newly discovered inodes...
Phase 4 - check for duplicate blocks...
        - setting up duplicate extent list...
        - check for inodes claiming duplicate blocks...
        - agno = 0
        - agno = 1
        - agno = 2
        - agno = 3
No modify flag set, skipping phase 5
Phase 6 - check inode connectivity...
        - traversing filesystem ...
        - traversal finished ...
        - moving disconnected inodes to lost+found ...
Phase 7 - verify link counts...
No modify flag set, skipping filesystem flush and exiting.

The phases walk the four allocation groups that mkfs.xfs reported when it created the filesystem. The two No modify flag set lines are -n doing its job: phase 5 rebuilds metadata and is skipped entirely, and nothing is flushed at the end.

Phase 6 mentions moving disconnected inodes to lost+found, but with -n in effect that is a description of what a real repair would do rather than something that happened. No problems were reported, so this filesystem is healthy.

Unlike e2fsck, xfs_repair will not even start on a mounted filesystem. Mount it and try:

bash
mount /dev/sdb1 /mnt/xfsdata

The mount succeeds silently, and now the repair tool has an opinion about it:

bash
xfs_repair -n /dev/sdb1
output
xfs_repair: /dev/sdb1 contains a mounted and writable filesystem

fatal error -- couldn't initialize XFS library

It refuses before reading anything. This is deliberate and more helpful than a partial answer, because a repair against a live filesystem can destroy it. If you need to check the root XFS filesystem, boot from rescue media so it is not mounted.

WARNING
xfs_repair -L zeroes the log rather than replaying it, and it discards whatever metadata updates the log was holding. It is a last resort for a filesystem that cannot mount at all, not a way to get past the mounted-device error. Reach for it only after an unmounted xfs_repair has failed, and expect data loss.

For the routine question of how a mounted XFS filesystem is laid out, xfs_info reads geometry from the live mount and needs no unmount:

bash
xfs_info /mnt/xfsdata
output
meta-data=/dev/sdb1              isize=512    agcount=4, agsize=196544 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=1, sparse=1, rmapbt=1
         =                       reflink=1    bigtime=1 inobtcount=1 nrext64=1
         =                       exchange=0   metadir=0
data     =                       bsize=4096   blocks=786176, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0, ftype=1, parent=0
log      =internal log           bsize=4096   blocks=16384, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
         =                       rgcount=0    rgsize=0 extents
         =                       zoned=0      start=0 reserved=0

This is the same report mkfs.xfs printed at creation, which makes it a useful way to confirm the geometry of a filesystem somebody else built. The blocks=786176 figure is also what you would compare against after growing the device with xfs_growfs.


Check VFAT Filesystems

FAT has no journal, so a check is the only mechanism for finding inconsistencies after an unclean removal. That is exactly the situation removable media ends up in, and it is why this check matters more on a USB stick than the equivalent does on a server disk.

This filesystem was already detached by the umount /dev/sdb3 above, so there is nothing to unmount here. Confirm that rather than assuming it, because a check against a mounted FAT filesystem has the same validity problem as it does on ext4:

bash
findmnt /dev/sdb3

No output and exit status 1, so the device is free. If it did print a row, unmount it with umount /mnt/vfatdata before going any further. With the device free, start with -n, which makes no changes whatever it finds:

bash
fsck.vfat -n /dev/sdb3
output
fsck.fat 4.2 (2021-01-31)
/dev/sdb3: 3 files, 2/523260 clusters

That is the summary and nothing else, because there was nothing to report. The reason to reach for -n first on FAT is that the bare command is interactive: given a problem it stops and asks whether to fix it, so -n is how you find out what is wrong before deciding anything. The three-file count includes the volume label, which occupies a directory entry in the root directory like a file does.

-v gives the same verdict with a verbose report of the structures it walked, which is the version to read when you want to know how the filesystem is laid out:

bash
fsck.vfat -v /dev/sdb3
output
fsck.fat 4.2 (2021-01-31)
Checking we can access the last sector of the filesystem
Boot sector contents:
System ID "mkfs.fat"
Media byte 0xf8 (hard disk)
       512 bytes per logical sector
      4096 bytes per cluster
        32 reserved sectors
First FAT starts at byte 16384 (sector 32)
         2 FATs, 32 bit entries
   2093056 bytes per FAT (= 4088 sectors)
Root directory start at cluster 2 (arbitrary size)
Data area starts at byte 4202496 (sector 8208)
    523260 data clusters (2143272960 bytes)
63 sectors/track, 255 heads
  12582912 hidden sectors
   4194288 sectors total
Checking for unused clusters.
Checking free cluster summary.
/dev/sdb3: 3 files, 2/523260 clusters

The last line is the verdict: three files using two of 523260 clusters, and no problems reported.

The line above it matters more than it looks. FAT keeps a summary of free clusters that can drift out of step with the actual allocation tables after an unclean unmount, and that mismatch is the single most common thing this tool fixes.

I did manage to create a real inconsistency during this walkthrough by relabelling the filesystem while it was mounted, which the tool reported as a difference between the boot sector and its backup copy. The -a flag repairs what it finds without asking:

bash
fsck.vfat -a /dev/sdb3
output
fsck.fat 4.2 (2021-01-31)
/dev/sdb3: 3 files, 2/523260 clusters

The warning is gone and the summary is clean. fsck.vfat -a repairs automatically without prompting, and where more than one repair is possible it chooses the least destructive approach. Run the read-only -n pass first when you want to inspect the problems before allowing any changes.

Automatic repair is not unique to FAT either: ext4 has e2fsck -p, which fixes what can be fixed safely without human intervention and exits with a distinct status when a problem needs an administrator to decide.


Change Filesystem Labels After Creation

Labels are not fixed at creation. Each filesystem has its own tool, and their rules about mounted devices are the part worth getting right, because two of the three want the filesystem detached first.

XFS uses xfs_admin -L, and xfs_admin(8) is explicit that "devices that are mounted cannot be modified", so the supported order is to unmount, relabel, then mount again. Red Hat documents the same prerequisite. Start by detaching it:

bash
umount /mnt/xfsdata

With the device free, set the new label:

bash
xfs_admin -L xfsvol /dev/sdb1
output
writing all SBs
new label = "xfsvol"

writing all SBs is the useful half of that output. XFS keeps a superblock copy in every allocation group, and xfs_admin updates all of them directly on the device, which is precisely why it wants the kernel out of the way. Next, give udev a chance to notice:

bash
udevadm settle

That returns silently once the event queue is empty. Skipping it is what leaves /dev/disk/by-label/ pointing at the old name, and mounting by label then fails for a filesystem that is perfectly healthy. Now mount it back:

bash
mount /dev/sdb1 /mnt/xfsdata

The mount is silent, so confirm the change reached the disk rather than just the tool's output:

bash
blkid /dev/sdb1
output
/dev/sdb1: LABEL="xfsvol" UUID="a2d95b7d-0394-40d5-a667-6c69da0dc048" BLOCK_SIZE="512" TYPE="xfs" PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"

LABEL is now xfsvol while the UUID is unchanged, which is the difference between relabelling and reformatting. The PARTLABEL is also untouched, since that lives in the partition table rather than in the filesystem.

ext4 is the exception in this group. tune2fs -L changes the label online, and /dev/sdb2 is mounted at /mnt/ext4data as this runs:

bash
tune2fs -L ext4vol /dev/sdb2
output
tune2fs 1.47.1 (20-May-2024)

Only the version banner, which is tune2fs succeeding quietly. Verify the same way:

bash
blkid /dev/sdb2
output
/dev/sdb2: LABEL="ext4vol" UUID="830f3cbf-d586-4d43-aa50-fde3786a3569" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="ext4data" PARTUUID="d8dc1e1e-0e48-455d-9432-935cc3e10270"

New label, same UUID. Note that tune2fs enforces the sixteen character ceiling here just as mkfs.ext4 did.

FAT uses fatlabel, and this is the one to be careful with. The VFAT filesystem is still detached from the check above, which is the state it should be in for this. Called with no label, fatlabel reads the current one:

bash
fatlabel /dev/sdb3
output
LABVFAT

That is the label set at creation. Writing a new one is equally terse:

bash
fatlabel /dev/sdb3 USBDATA

No output, and the change is immediate:

bash
blkid /dev/sdb3
output
/dev/sdb3: LABEL_FATBOOT="USBDATA" LABEL="USBDATA" UUID="7A93-48B7" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="vfatdata" PARTUUID="c154f0aa-70bc-446b-8a5d-98343bbbd428"

Both label fields updated together. The catch is that fatlabel does not refuse to run on a mounted filesystem: it writes to the block device directly, behind the kernel's cached copy of the boot sector. That is how I produced the boot sector mismatch that fsck.vfat -a cleaned up in the previous section. Unmount FAT media before relabelling it.

Filesystem Change label with Mounted?
XFS xfs_admin -L xfsvol /dev/sdb1 Unmount first
ext4 tune2fs -L ext4vol /dev/sdb2 Can be changed while mounted
VFAT fatlabel /dev/sdb3 USBDATA Unmount first

Renaming a label breaks anything that mounts by the old name, so check /etc/fstab and any scripts before you change a label on a system you did not build yourself.


Compare the Three Filesystems Side by Side

This comparison needs all three filesystems attached at once, and VFAT has been unmounted since the check and relabel above, so put it back:

bash
mount /dev/sdb3 /mnt/vfatdata

Confirm all three are attached before comparing anything, because an unmounted filesystem silently drops out of the output of every command below rather than reporting an error:

bash
lsblk -o NAME,FSTYPE,LABEL,MOUNTPOINTS /dev/sdb
output
NAME   FSTYPE LABEL   MOUNTPOINTS
sdb                   
├─sdb1 xfs    xfsvol  /mnt/xfsdata
├─sdb2 ext4   ext4vol /mnt/ext4data
└─sdb3 vfat   USBDATA /mnt/vfatdata

Three partitions, three types, three mount points, and the labels are the ones set in the previous section rather than the originals. Now one command shows how differently the three behave with identical input. I wrote the same twenty two byte string to each:

bash
for m in xfsdata ext4data vfatdata; do printf 'filesystem comparison\n' > /mnt/$m/compare.txt; done

The loop prints nothing, so compare how the three filesystems describe files that are byte for byte identical:

bash
ls -l /mnt/xfsdata/compare.txt /mnt/ext4data/compare.txt /mnt/vfatdata/compare.txt
output
-rw-r--r--. 1 root root 22 Aug  8 16:15 /mnt/ext4data/compare.txt
-rwxr-xr-x. 1 root root 22 Aug  8 16:15 /mnt/vfatdata/compare.txt
-rw-r--r--. 1 root root 22 Aug  8 16:15 /mnt/xfsdata/compare.txt

Same size, same owner, same content, and the VFAT copy is mode 755 while the other two are 644. Nothing about the file caused that; the mode came from the mount options because FAT had nowhere to store the real one.

Capacity tells a similar story about metadata cost:

bash
df -hT /mnt/xfsdata /mnt/ext4data /mnt/vfatdata
output
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb1      xfs   3.0G   90M  2.9G   3% /mnt/xfsdata
/dev/sdb2      ext4  2.9G  201M  2.6G   8% /mnt/ext4data
/dev/sdb3      vfat  2.0G   12K  2.0G   1% /mnt/vfatdata

Three rows worth reading against each other:

  • XFS reports the full 3.0G but has 90M in use for a single small file, all of it metadata
  • ext4 reports 2.9G rather than 3.0G because its metadata is deducted from the size instead, and its 201M is mostly the test file from earlier
  • VFAT has almost no fixed overhead at all, showing 12K used, because it lacks the journal and inode tables the other two spend space on

The consolidated view ties device, filesystem, label, and mount point together:

bash
lsblk -f /dev/sdb
output
NAME   FSTYPE FSVER LABEL   UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
sdb                                                                             
├─sdb1 xfs          xfsvol  a2d95b7d-0394-40d5-a667-6c69da0dc048    2.8G     3% /mnt/xfsdata
├─sdb2 ext4   1.0   ext4vol 830f3cbf-d586-4d43-aa50-fde3786a3569    2.5G     7% /mnt/ext4data
└─sdb3 vfat   FAT32 USBDATA 7A93-48B7                                 2G     0% /mnt/vfatdata

One disk, three filesystems, three labels, three mount points. The FSVER column shows FAT32 for the VFAT partition, which is the variant mkfs.vfat selected for a 2 GB device, and the short 7A93-48B7 identifier stands out against the two full UUIDs.

findmnt gives the same information from the mount table's perspective, which is the view that matters when you are working out what is attached where:

bash
findmnt -t xfs,ext4,vfat -o TARGET,SOURCE,FSTYPE
output
TARGET          SOURCE                FSTYPE
/               /dev/mapper/rhel-root xfs
├─/mnt/xfsdata  /dev/sdb1             xfs
├─/boot         /dev/sda2             xfs
├─/mnt/ext4data /dev/sdb2             ext4
└─/mnt/vfatdata /dev/sdb3             vfat

Filtering by type puts the three new filesystems in the same list as the root and boot filesystems, which is a good final sanity check that you built what you intended and nothing landed on the wrong device.


Troubleshoot Filesystem Creation and Mounting

Symptom Likely cause Fix
appears to contain an existing filesystem A filesystem is already there and mkfs is protecting it Confirm the device is the right one, then add -f for XFS or -F for ext4
is mounted; will not make a filesystem here! The target device is currently mounted Unmount it first; the force flags do not override this particular check
target is busy on umount A process has an open file or its working directory inside the mount Run fuser -vm /mnt/data, move out with cd, or stop the service holding it
mount point does not exist The directory was never created mkdir -p /mnt/data then mount again
wrong fs type, bad option, bad superblock Wrong -t value, or no filesystem on the device at all Drop -t and let mount autodetect; check blkid output and dmesg for the driver's complaint
Can't open blockdev on mount The device is already mounted, so the new mount cannot open it exclusively Check findmnt -S /dev/sdb3 and unmount it first; this is a busy device, not a damaged one
Filesystem must be larger than 300MB The device is below the XFS minimum size Use a larger device or choose ext4, which works on much smaller volumes
Read-only file system when writing Mounted read-only, or remounted that way after an error Check findmnt options; remount with mount -o remount,rw /mnt/data and investigate dmesg if the kernel forced it
Operation not permitted from chown on VFAT FAT cannot store per-file ownership Remount with uid=, gid=, fmask= and dmask= instead
chmod succeeds but nothing changes Same FAT limitation, applied to mode bits Set the permissions you want through mount options
lsblk -f shows no filesystem after mkfs udev has not re-read the device yet Trust blkid, then run udevadm settle or wait a moment
contains a mounted and writable filesystem xfs_repair was pointed at a live filesystem Unmount it first, or boot rescue media when it is the root filesystem
Mounting by label fails after renaming The /dev/disk/by-label/ symlink is stale Run udevadm settle, or mount by UUID instead

Two of these deserve a closer look because their error text is misleading. The wrong fs type message appears both when you name the wrong type and when there is no filesystem at all, so it never tells you which. To see it, the device has to be free, because a mounted device produces a different error entirely:

bash
umount /mnt/vfatdata

With /dev/sdb3 detached, naming ext4 on the VFAT partition produced this:

bash
mount -t ext4 /dev/sdb3 /mnt/vfatdata
output
mount: /mnt/vfatdata: wrong fs type, bad option, bad superblock on /dev/sdb3, missing codepage or helper program, or other error.
       dmesg(1) may have more information after failed mount system call.

Four possible causes in one sentence, which is not much help on its own. The kernel is more specific, and the last line of the message is telling you where to look:

bash
dmesg | tail -3
output
[ 1707.026357] XFS (sdb1): Mounting V5 Filesystem a2d95b7d-0394-40d5-a667-6c69da0dc048
[ 1707.048865] XFS (sdb1): Ending clean mount
[ 1823.759539] EXT4-fs (sdb3): VFS: Can't find ext4 filesystem

Can't find ext4 filesystem on sdb3 is unambiguous: the ext4 driver was asked to mount something that is not ext4. Omitting -t entirely avoids the whole class of problem, since mount reads the superblock and picks the right driver.

That is also why the unmount above mattered. Put the filesystem back:

bash
mount /dev/sdb3 /mnt/vfatdata

Then run the identical wrong-type command against the device now that it is busy:

bash
mount -t ext4 /dev/sdb3 /mnt/vfatdata
output
mount: /mnt/vfatdata: fsconfig system call failed: /dev/sdb3: Can't open blockdev.
       dmesg(1) may have more information after failed mount system call.

Nothing here mentions the filesystem type, because the ext4 driver never got far enough to look: the device was already open exclusively by the VFAT mount. Can't open blockdev means busy, not wrong type, and confusing the two sends you hunting for corruption that is not there.

The XFS size floor is the other one that reads oddly, because the number is not documented anywhere near the command you are running. Pointing mkfs.xfs at a 100 MB device gives:

bash
mkfs.xfs /root/small.img
output
Filesystem must be larger than 300MB.

Below roughly 300 MB, XFS declines to create a filesystem at all, because its allocation groups and log need more room than that leaves. ext4 has no comparable floor, so small volumes are one of the few places where ext4 is the only reasonable choice of the two.

There is one refusal you should be glad to see, and it is the reason a mistyped device name is usually survivable. Pointing mkfs.ext4 at a device that is currently mounted gets you nowhere:

bash
mkfs.ext4 /dev/sdb2
output
mke2fs 1.47.1 (20-May-2024)
/dev/sdb2 is mounted; will not make a filesystem here!

The command stops before writing anything. What makes this guard more valuable than the signature check is that the force flag does not switch it off:

bash
mkfs.ext4 -F -n /dev/sdb2
output
mke2fs 1.47.1 (20-May-2024)
/dev/sdb2 is mounted; will not make a filesystem here!

Same refusal with -F present, and -n here means dry run so nothing was at risk either way. -F overrides the existing-filesystem warning but not the mounted-device check, which means an active production filesystem is protected even from a forced command.

An idle device gets far less protection. On XFS and ext4 only the signature check stands between it and -f or -F, and on VFAT not even that, because mkfs.vfat does not look for an existing filesystem before overwriting it.

Those two letters are also specific to these tools rather than a shared convention. On mkfs.vfat, -f sets the number of file allocation tables and -F selects FAT12, FAT16 or FAT32, and its own safety-check override is -I.


References


Summary

Creating a filesystem is a short command surrounded by decisions that matter more than the command does. The path through this guide was always the same six steps: confirm the block device is genuinely free, run the right mkfs tool, check the result with blkid, mount it somewhere, verify with findmnt and df, and unmount cleanly when you are finished.

On one spare 10 GB disk I built XFS, ext4 and VFAT alongside each other, and the differences showed up immediately in how much space each one spent on metadata and in how each one treated an identical file.

The step worth slowing down on is identification. mkfs has two safety nets and they are not equally strong. The refusal to touch a mounted device is the reliable one and holds against every flag I tried.

Protection for an idle device is much weaker and varies by tool: mkfs.xfs and mkfs.ext4 refuse an existing signature until you override them with -f or -F, while mkfs.vfat never checks and overwrites silently. Those option letters are filesystem specific rather than a general force convention, so read the right manual page instead of carrying a flag across tools.

The practical upshot is that a running production filesystem is well protected and a decommissioned one is barely protected at all. Everything else that protects you comes from lsblk, findmnt, pvs and wipefs before you press Enter, and it is worth being precise about what those tools prove: file -s reporting data only means it recognised no format, not that the device is empty.

Two habits that saved me time here are worth keeping:

  • Believe blkid over lsblk when they disagree, because lsblk reports a udev cache that lags a fresh format
  • Remember that reformatting mints a new UUID even when the label is unchanged, which is what silently breaks an /etc/fstab entry after a supposedly harmless redo

Checking and repairing follow one rule that covers all three types: unmount first. Mounting and then unmounting is what lets the kernel replay the journal, so the check that follows sees a state the filesystem actually agreed on.

e2fsck -n will read a mounted ext4 filesystem, but it warns that the results are not valid and it skips journal recovery. xfs_repair refuses a live device outright rather than giving you half an answer.

Start with the read-only form in every case: xfs_repair -n, e2fsck -n, or fsck.vfat -n. On XFS, ignore fsck.xfs altogether, since it is a shell script that prints advice and exits successfully without reading the filesystem.

The other lasting lesson is that VFAT is a different kind of filesystem rather than a weaker version of the other two. Its files looked executable, chmod accepted a change and discarded it, and chown failed outright as root, all because FAT has no field on disk for Unix ownership and the kernel invents one from uid, gid, fmask and dmask at mount time.

That makes it the right answer for a USB stick a Windows machine has to read and the wrong answer for anything relying on permissions. It is also why its 4 GiB per-file ceiling tends to be discovered at the worst moment.

From here the natural next step is making these mounts permanent, which is a separate file and a separate set of failure modes rather than another mount command. Write the entry by UUID or label rather than by device name, and test it with mount -a before you trust a reboot to it.


Frequently Asked Questions

1. Do I have to partition a disk before I can create a filesystem on it?

No. mkfs writes to any block device you point it at, so a whole disk such as /dev/sdb works just as well as a partition such as /dev/sdb1. Partitioning is only a way to divide one disk into several independent devices, which is what you want when a single disk needs more than one filesystem or when you plan to boot from it. If the disk will hold exactly one filesystem and nothing boots from it, formatting the whole device is valid and skips a layer.

2. Does mkfs erase the whole disk or only the partition I name?

It only writes to the device you name. Running mkfs.ext4 on /dev/sdb2 leaves /dev/sdb1 and /dev/sdb3 untouched, because each partition is a separate block device with its own byte range. That is also why naming the wrong device is so damaging: mkfs does not ask which files matter, it writes fresh metadata over whatever was there, and the previous files become unreachable even though most of their data blocks are still physically present.

3. Why does df report less space than the partition size?

Two separate deductions are at work. First, filesystem metadata such as inode tables, allocation groups, and the journal is written when you create the filesystem, so some of the device is spent before you store a single file. Second, ext4 reserves a percentage of blocks for root by default, which keeps a full filesystem usable for privileged processes but does not count as available space for ordinary users. That is why a three gigabyte partition can report roughly 2.9 gigabytes of capacity and slightly less than that as available.

4. Can I create a filesystem on a device while it is mounted?

No, the tools refuse, and that refusal is protecting you. mkfs.xfs, mkfs.ext4, and mkfs.vfat each check whether the device is mounted and stop rather than writing new metadata underneath a filesystem the kernel currently has cached, which would corrupt both the cache and the on-disk state. This is the one mkfs safeguard a force flag does not override: mkfs.xfs -f still refuses a mounted device, unlike the merely advisory warning it gives about an existing filesystem on an idle one. Unmount the filesystem first, confirm with findmnt that nothing is attached, then format. Repair tools follow the same rule, which is why xfs_repair refuses a mounted device outright and e2fsck warns that its results are not valid on one.

5. Why do files on a VFAT filesystem show up as executable?

FAT has nowhere on disk to store Unix mode bits, owners, or groups, so the kernel invents them at mount time from the fmask, dmask, uid, and gid mount options. The default masks produce mode 755 on files, which is why everything looks executable in ls output. Changing that with chmod appears to succeed but has no effect, because there is no field to write the new mode into. To change what ownership and permissions look like, remount with different uid, gid, and mask options.

6. Do I have to unmount a filesystem before checking it?

Yes, if you want an answer you can rely on. On ext4, e2fsck -n can technically open a mounted filesystem read-only, but its own documentation warns that the results it reports are not valid while the filesystem is mounted, and it skips journal recovery as well. The documented procedure on RHEL is to mount and then unmount the filesystem so the kernel replays the journal, and then run the check against the unmounted device. XFS checks with xfs_repair -n should follow the same clean unmount, and xfs_repair refuses outright on a mounted device rather than giving you a partial answer. Repair is stricter still: letting a repair tool rewrite metadata under a live mount is one of the reliable ways to lose a filesystem.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)