Mounting on linux means binding the contents of a filesystem from a disk partition or network share to a directory, making its contents available under that path prefix. What seems like a straight-forward process has a lot of edge cases and pitfalls, which we will discuss here so you can skip some debugging sessions in the future.
Block devices and filesystems
For the purpose of this article, we assume you are already familiar with the terms "block device", "drive" and "partition", as well as disk identifiers like /dev/sda and partition identifiers like /dev/sda2, as well as the NVMe counterpart like /dev/nvme0n1p3.
Note that block device does not mean "drive" by default, but rather "anything that can read/write fixed-size blocks of data at arbitrary byte offsets".
This most commonly means storage drives, but also includes CD/DVD, USB flash storage and even translation programs exposing a block device API to store blocks inside files, memory or a remote network location.
A filesystem defines how to read and write filesystem entities, specifically files, directories and attached metadata like owner, permissions and access timestamps. Some filesystems do this by defining how to interpret bytes on block storage (ext3/ext4, ntfs, fat), some instead manage multiple local block devices and offer advanced features like replication (btrfs, zfs), and some don't use local storage at all, instead converting every file operation into a network call to interact with remote systems (smb, nfs, webdav).
Therefore, filesystems are not bound to partitions or disks. A filesystem could live on a single partition, on a disk that has no partitions/partition table, across multiple storage drives, or somewhere else entirely.
Only filesystems can be mounted on linux. This may be confusing at first, as commonly mount is passed a partition identifier like /dev/sda1 (partition 1 on disk a), but this only works if the partition is already formatted as a filesystem. If it is not, mount would give you one of these errors:
mount: /mnt: unknown filesystem type '...'
mount: /mnt: wrong fs type, bad option, bad superblock on /dev/sdb1, missing codepage or helper program, or other error.Passing a partition to mount is a convenience feature, it still expects to find a filesystem in that location.
Block storage identifier stability
Before we can mount anything, we need a way to identify where the filesystem to mount actually is.
The most common usage of mount is passing local (formatted) partition identifiers like /dev/sda1 or /dev/nvme0n1p3 to the mount command to mount the filesystem previously created there.
This is generally fine for manual mounting, but keep in mind that neither of these names are stable between reboots: the disk currently available as /dev/sdc may show up as /dev/sda after the next reboot. The naming exclusively depends on the order that the kernel discovers the drives/controllers during boot. Even if your BIOS/firmware seems to expose your storage drives in the same order every time, keep in mind that if any controller fails, it will mess up the device naming order for all subsequent drives regardless of naming system.
Similarly unreliable are filesystem labels, as they are not guaranteed unique and you can give multiple filesystems the same label text.
The general advice is to not rely on device/partition names or filesystem labels when referring to a storage drive in documentation or automatic mounting scripts, and instead identify filesystems by their automatically generated unique UUID. This is generally the best approach, but be aware that it is possible to have multiple filesystems with the same UUID, for example if you clone an existing filesystem onto a different device, manually change the UUID of a filesystem to an existing one, or are unfathomably unlucky and compute the same UUID twice (the chances for the latter are virtually zero in reality, but not guaranteed impossible).
Note that this only applies to filesystems mounted from local block devices, other filesystem implementations that do not rely on local storage disks (e.g. smb/nfsnetwork shares) do not have this problem.
Identifying local drives and partitions
Since mounting most commonly means making a filesystem on a local disk partition accessible, you first need to figure out the path of a local disk, or a specific filesystem's name or UUID.
The easiest way to do that is to run
sudo blkidThis will find all local block storage devices or partitions that are formatted with a filesystem:
/dev/nvme0n1p1: UUID="A12B-34CD" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="8f3c1a20-01"
/dev/nvme0n1p2: UUID="7c9e2d41-5a63-4b87-91f2-6d8e3c0a7b25" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="8f3c1a20-02"
/dev/nvme0n1p3: UUID="c4b71e92-8d35-46fa-a219-5e7c3d8f0b64" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="8f3c1a20-03"
/dev/sda: UUID="1a6f8c24-3d91-4e57-b2a8-7c0d5f9e3146" BLOCK_SIZE="4096" TYPE="ext4"The sample above has one NVMe drive with 3 partitions and an SCSI drive that's directly formatted as ext4 (no partitions). Each entry will include the (unreliable) current device path/name, a (reliably stable) UUID for the filesystem, the detected filesystem type, the block size the device is configured to use, and optionally a partition UUID (PARTUUID) that is not relevant for mounting.
The blkid command only lists block devices/partitions that are formatted with a filesystem the linux system understands. If you have disks that are not formatted or use a filesystem that the host doesn't understand (missing driver), you can instead use
sudo lsblk -fNote the -f flag to include filesystem information. The output includes information about known block devices, their filesystems and mount points if available:
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
nvme0n1
├─nvme0n1p1 vfat FAT32 A12B-34CD 1022M 1% /boot/efi
├─nvme0n1p2 ext4 1.0 7c9e2d41-5a63-4b87-91f2-6d8e3c0a7b25 1.2G 18% /boot
└─nvme0n1p3 ext4 1.0 c4b71e92-8d35-46fa-a219-5e7c3d8f0b64 180G 12% /
sda ext4 1.0 1a6f8c24-3d91-4e57-b2a8-7c0d5f9e3146 650G 8% /dataEmpty block devices are hidden by default as they are seldom useful. Add -a to show every known block device.
Mounting drives
Once you know the source location you want to mount, you use the mount command to make its contents accessible through the local directory tree.
The basic format is:
sudo mount my_source /mount/pointWhere my_source can be a formatted disk partition like /dev/sda1, a remote NFS share like 123.456.789.123:/nfs_share or something else entirely. The target directory to mount the source into is called its "mount point" (/mount/point in this example) and must already exist on disk. If it is not empty, its real contents become inaccessible while a filesystem is mounted there, but unmounting will later reveal them again.
Mounting can alternatively identify filesystems by UUID or LABEL (if unique):
sudo mount LABEL=mydisk /mount/point
sudo mount UUID=XXXX-XXXX-XXXX /mount/pointIn this minimal form, mount is doing a lot of work behind the scenes to detect the filesystem and select the appropriate driver.
If source is a block device, mount uses the kernels builtin filesystem detection, otherwise it tries to guess the filesystem from the source identifier directly, for example sample.com:/share would be guessed as nfs, and //sample.com/share as cifs(smb).
You can pass the filesystem type explicitly during mount:
sudo mount -t ext4 /dev/sda4 /mydiskThis time, mount will attempt to mount the partition /dev/sda4 as an ext4 filesystem at /mydisk. If the format matches, you can browse the partition contents under the mount point afterwards, otherwise you will get an error like:
mount: /mydisk: wrong fs type, bad option, bad superblock on /dev/sda4,
missing codepage or helper program, or other error.
dmesg(1) may have more information after failed mount system call.For most common filesystems, it is sufficient to let mount detect the correct filesystem automatically, only rarely will you have to supply it manually.
Filesystem drivers, helper scripts and FUSE
The mount command doesn't actually mount anything itself, instead it is the user-facing frontend that figures out desired filesystem type and options, and passes those along to the VFS kernel subsystem ("virtual file system") , which executes the associated driver logic.
All drivers must register themselves with the linux kernel somehow, either as directly built into the kernel (vfat, ext4, btrfs), or by installing a kernel module to register it (openzfs).
Drivers running in userspace have to use the fuse (filesystem in userspace) API that translates kernel calls to read files or create directories into function calls from a userspace program, for example a webdav client that translates them into HTTP requests on a remote storage server.
Fuse filesystems are slightly less resource efficient because of the translation layer, but have the massive advantage of requiring no elevated privileges, so a vulnerability would not grant immediate kernel access.
When mount is passed a filesystem with the -t <fstype> argument, it first checks if a helper script for it can be found under /sbin/mount.<fstype>. For example, if you ran:
sudo mount -t myfs /some/disk /some/dirThen mount would first check if it can find the file /sbin/mount.myfs. If it exists, it is executed and mount expects it to handle the rest, otherwise mount invokes the VFS api and mount syscall to use a kernel builtin or kernel module driver directly.
This means that if the kernel provides the builtin ext4 driver and you create an executable file /sbin/mount.ext4, that kernel driver will be skipped and the script may do whatever it wants instead. If you want to force a kernel driver specifically and ignore helper script lookup, use -i:
sudo mount -i -t ext4 /some/disk /some/dirNow the kernel driver for ext4 is invoked directly, and mount does not search for a helper script first.
Note that there are no enforced rules around helper scripts in /sbin/mount.*, the mount command will happily execute any matching script name regardless of its logic, even if it does not actually mount anything.
Find and view mounts
Once filesystems are mounted, you may want to view or inspect them. We have already seen the lsblk command for block devices, but this notably excludes non-block filesystems like network shares.
To view all mounts on the system, instead use:
mount -lThe output can be fairly long and quite difficult to parse, but a single line may look like:
/dev/sda1 on /data type ext4 (rw,relatime)The output shows the source (/dev/sda1), where it is mounted (on /data), the filesystem type (type ext4) and the mount options ((rw, relatime)).
An easier view for most cases is findmnt:
findmnt -DThis produces more parseable output:
SOURCE FSTYPE SIZE USED AVAIL USE% TARGET
udev devtmpfs 15.1G 0 15.1G 0% /dev
tmpfs tmpfs 3G 2.7M 3G 0% /run
/dev/sda1 ext4 1.8T 409.7G 1.3T 23% /
tmpfs tmpfs 15.2G 8K 15.2G 0% /dev/shm
tmpfs tmpfs 5M 20K 5M 0% /run/lock
/dev/nvme0n1p2 ext4 943.2M 402.2M 476.2M 43% /boot
tmpfs tmpfs 15.2G 235.1M 15G 2% /tmp
/dev/nvme0n1p1 vfat 974.1M 9M 965M 1% /boot/efi
tmpfs tmpfs 3G 4.1M 3G 0% /run/user/1000If you omit the -D flag, you will see all mounts as well, but in organized in tree structure by mount points, which is easier to parse manually.
In practice, you will usually use findmnt -D for a quick mount state overview, findmnt to see every mount that exists, and lsblk -f to figure out which physical storage device is backing a mount points (if any).
Unmounting
Every mount eventually has to be undone at some point, but this process is not always as simple as you may expect.
In a best case scenario, you simply pass the mount point to the umount command and everything works just fine:
sudo umount /some/dirIf this produces no output, you are already done. Don't worry if the command hangs for a moment, as umount automatically flushes any buffered writes to the disk to prevent data loss before unmounting the source device.
Note that umount also invokes helper scripts by filesystem type just like mount does if a matching file in /sbin/umount.<fstype> is present.
Sometimes you may get an error like this from umount:
umount: /mount/point: target is busy.This can be caused by a lot of things, but the most common reason is a process on your machine still keeping a file or directory open inside the mount point. Using fuser is the first step to verify this:
sudo fuser -m /mount/pointMake sure to use -m to scan the entire mount recursively, skipping the flag would only check the directory /mount/point itself and not contained files like /mount/point/sample.txt.
The output will be brief:
/mount/point: 3879617c 3879833In this example, fuser found two processes, given by their IDs 3879617 and 3879833. *Note that process ids can be followed by a character to indicate the kind of file access like c (used as current working directory) or e (running as an executable). You can largely ignore these in most cases.
For a better overview of what these processes are doing with the files, lsof has more human-readable output:
sudo lsof +D /mount/pointMake sure to include the +D option to recursively walk the target directory contents and find all entries currently held open by a process. Note lsof +D can be slow and memory expensive for large mounted filesystems, prefer fuser and ps inspection for more lightweight access.
Sample output:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 3879617 root cwd REG 7,1 6 14 /mount/point
python3 3879833 root 3r REG 7,1 6 14 /mount/point/diary.txtHere we can see that the first process is running a bash shell which uses the /mount/point directory as its working directory (cwd), while a second python3 script is reading from the file /mount/point/diary.txt.
You can now either stop those processes manually with kill, further inspect them with ps, or decide you do not care about them at all and force-kill everything using the mount point instead:
sudo fuser -k /mount/pointOnly do this if you have looked at all processes and confirmed that you do not care if you lose data in their open files or potentially breaking the processes using them!
In case fuser and lsof do not find any open files, something else could still keep the mount busy or unable to unmount. Classic examples are loss of network connection for mounted network shares, or a physical block device becoming unresponsive (disconnected usb drive, malfunctioning storage controller, bad disk etc).
In such cases, you typically want to unmount lazily:
sudo umount -l /mount/pointLazy unmounting will visually remove the mount point from your local filesystem and try cleanly unmounting it in the background. New processes cannot find or access it anymore, but running ones do not lose access yet. Especially for network shares and unreliable usb drives this is often the best option as it may recover cleanly in the background.
In rare cases you may want to immediately fully force unmount a filesystem, for example when you have already pulled a usb drive out of its port and therefore know there is no path to clean unmounting anymore, so only unclean force unmounting is left:
sudo umount -f /mount/pointBehavior of force unmounting heavily depends on the filesystem and driver. Some filesystems may file to unmount even when forced, and stuck I/O operations will not magically resolve by force unmounting. Prefer lazy unmounting whenever possible.
Mount options
Mounting is highly flexible and supports a large list of options to configure the behavior of the mounted filesystem. These are split into generic options that apply to all filesystems, and extended by filesystem-specific options that only work for one or a subset of them.
The most common generic options are:
ro/rw: mount read-only/read-writesuid/nosuid: enable/disable interpreting setuid/setgid bits on executable filesexec/noexec: enable/disable running executable filesdev/nodev: enable/disable interpreting device filesstrictatime/relatime/noatime: update file access timestamps on every access / only if older than modify/change timestamps / never
Popular filesystem-specific options include:
errors=remount-ro: remount as read-only on errors for ext2, ext3 and ext4errors=continue: ignore filesystem errors on ext2/3/4compress=zstd: compress btrfs contents withzstdvers=3/vers=4: set protocol version for nfs or cifs/smb sharessize=1g: set size for in-memory tmpfs filesystemuid=12/gid=12: set owning user/group of a fat filesystem to userid12, since fat cannot store ownership informationfmask=000/dmask=000: set file/directory permission mask bits for fat filesystems,000means "full permissions" and is common since fat cannot store file/directory permissions
As you can see from the brief list above, options come either as boolean switches where the name itself turns a feature on or off, or as a key=value pair with multiple options. There are many more generic and filesystem-specific options, see the section "FILESYSTEM-INDEPENDENT MOUNT OPTIONS" in man mount for all generic ones, and your filesystem manual for its filesystem-specific option list.
Options are passed to mount using the -o flag, separating multiple options with commas:
sudo mount -o rw,relatime /some/disk /mount/pointAny option not specifically passed will use its default value. The special generic defaults option is a shorthand for rw,suid,dev,exec,auto,nouser,async and does not affect how any other options behave.
When the same option is specified multiple times, the last one overrides all previous occurrences. For example:
sudo mount -o rw,ro /some/disk /mount/point
sudo mount -o defaults,ro /some/disk /mount/pointBoth commands will default to ro (read-only) mounting, the first by overriding rw specifically, the second overriding the implicit rw contained in the defaults option set.
Altering mounts
The special mount options move and remount can be used to change an existing mount without first unmounting it.
To move an existing mount point to a different directory, you can either use the move option or the --move flag:
sudo mount -o move /old/path /new/path
sudo mount --move /old/path /new/pathNote that you only need to specify the current and new mounting point, not the device that is currently mounted.
If you instead only want to adjust the options without changing mount points, pass remount as the first option. For example, if you mount a partition read-only:
sudo mount -o ro,noatime /some/disk /mount/pointYou can later make it writable without unmounting or disrupting running processes using it:
sudo mount -o remount,rw /mount/pointNote that remounting only changes the specified mount options and does not reset all options to default values. In the previous example, the noatime option is preserved after remounting and not reset to the default atime setting.
Persistent mounts
When manually mounting filesystems with mount, the resulting mounts are ephemeral, meaning they disappear after a reboot.
Persistent mounts are handled by the "filesystem table" /etc/fstab to define filesystems with names, options and mount points, and the "mount table" /etc/mtab that keeps record of what is actually mounted where and with what options. Think of fstab as the blueprint of what you could mount, and mtab as the live record of what is currently mounted.
Modern linux systems will not use these files directly anymore, with systemd generators translating /etc/fstab to (auto)mount units and /etc/mtab often symlinked to /proc/self/mounts. However, using the files directly is still the only reliable and standardized way to manage persistent mounts across linux distributions, and newer tools are backwards-compatible with them.
Persistent mounts are specified by adding a line in /etc/fstab like:
<source> <mount point> <fs type> <options> <dump> <pass>The first arguments are very straightforward, mirroring exactly the arguments given to mount, with <source> being the filesystem source location, <mount point> the directory to mount it on, <fs type> the type of filesystem, and <options> the mount options to use. Persistent filesystems need a source identifier that is stable between reboots so do not use block device paths like /dev/sda1 or /dev/nvme0n1p3 as they may change during reboots or on hardware failure. Always prefer UUID=xxxxx where possible, fall back to LABEL=mydisk if you take care to keep filesystem labels unique, or use stable device mappers like /dev/mapper/sample-vg--data for LVM.
The <dump> field is largely obsolete on modern linux systems. It is a boolean switch for the legacy dump backup utility, accepting 0 (off) or 1 (on).
Lastly, <pass> defines the order in which fsck checks the filesystem for errors. 0 disables checking entirely, 1 checks it during the first pass (usually reserved for the root filesystem), 2 checks it during the second pass (any non-root filesystem).
Filesystems defined in /etc/fstab can be mounted by mount directly without passing their options or mount points at the command line. Assuming you have an /etc/fstab entry like:
LABEL=mydisk /data ext4 defaults 0 0You can mount persistent fstab entries by only providing their source or mount point:
sudo mount LABEL=mydisk
sudo mount /dataThe mount command fills in all unspecified arguments like options or mount points from the matches fstab entry.
Internally, mount uses the identifier to find a physically backing device for the mount point or source, then tries to find a matching entry in /etc/fstab. This means that you can even refer to fstab entries by identifiers not written in the file itself. Suppose the LABEL=mydisk refers to a filesystem with a UUID of 123-abc-def, you can even use that:
sudo mount UUID=123-abc-defAnd mount will find the filesystem, see that it's label is mydisk and match the /etc/fstab entry to it, finding the default mount point and options.
After changing the /etc/fstab file, make sure to always check if it is valid:
findmnt --verifyThe command will spot real errors, warning on potential problems and notes about side effects:
/
[W] recommended root FS passno is 1 (current is 0)
/data
[E] unreachable on boot required target: No such file or directory
/mnt
[E] unreachable on boot required source: UUID=bad
[W] your fstab has been modified, but systemd still uses the old version;
use 'systemctl daemon-reload' to reloadThe output is grouped by mount points, with the above sample pointing out several issues: a likely wrong fsck setting for the root filesystem, a missing mount point directory /data and a bad UUID= identifier for the mount on /mnt.
It ends with a general note that this is a systemd managed system and needs to run systemctl daemon-reload to update its view of the mount configuration.
Once you have verified that the /etc/fstab file is valid, you can mount all entries:
sudo mount -aThis ensures your mount configuration is genuinely valid and everything works as expected, without potentially risking a stuck boot process.
Note that -a flag does not mount all filesystems, but rather all that do not have the noauto option set. The mount -a command is effectively the same thing that runs during boot to automatically mount all necessary filesystems.
If you want to exclude a mount from automatic mounting during boot, add the noauto flag:
LABEL=mydisk /mnt ext4 defaults,noauto 0 0All disks not marked as noauto will block the boot process on error (usually dropping to an emergency shell). If you want to automatically mount a device but do not fail booting on error, add nofail:
LABEL=mydisk /mnt ext4 defaults,auto,nofail 0 0The man page says "Do not report errors for this device if it does not exist.", but nofail also includes filesystem errors.
Mounting files as block devices
Using a file as a pseudo disk is a common scenario, for example to clone a disk for diagnosis, backups or use in a virtual machine. Files containing mountable filesystems will typically have the extension .iso, .img or .raw.
Since files themselves do not expose a block device interface, a special loop device is needed to translate block operations to file operations. Such devices are called "loop devices" because they loop block i/o operations through a file on a filesystem to the real underlying storage layer.
On modern linux systems losetup handles the creation and management of loop devices automatically, and you can simply pass the loop mount option to get or create a free loop device for the mount automatically:
sudo mount -o loop mydisk.iso /mount/pointWhen unmounting, the associated loop device is either freed or destroyed, depending on system state and losetup configuration.
Modern linux systems do not have a maximum limit of loop devices anymore. In case you get an error like:
mount: could not find any free loop devicethis usually hints at losetup failing to create a new one on demand, usually because the loop kernel module is not loaded.
Some linux systems now detect if a mount source is a file and set up loop devices automatically, but this behavior is not stable across distributions and only passing -o loop explicitly is reliable.
Bind mounts
Bind mounts are a special kind of mount that allows circumventing a lot of the previously discussed mounting mechanisms. They fundamentally do not care if their target is a mountable filesystem location or a directory within one, think of it as giving any directory a second mount point somewhere else.
Let's see a quick example of this by mounting a file as a fake disk for a moment:
fallocate -l 1g disk.img
mkfs.ext4 disk.img
mkdir dir1 dir2
sudo mount -o loop,strictatime disk.img dir1
echo "test" | sudo tee dir1/file.txtThis mounts the filesystem inside disk.img at dir1 and creates a file file.txt inside it.
Using a bind mount, we can make the same mount available from dir2 as well:
sudo mount -o bind dir1 dir2You can now see and edit file.txt either in dir1 or dir2, and the change is reflected in both locations.
For a more advanced example, you can even change mount options per mount point:
sudo mount -o remount,bind,noatime dir2After this change, when accessing files through dir1 their access timestamp is always updated, but when accessing the same file through dir2 it is never updated. The same rule applies to all mount options, so you could for example take a read-only mount and bind mount it as read-write somewhere else, or vice versa.
Make sure to pass both remount and bind options, if you leave out bind then remount will affect the original mount at dir1 too!
Bind mounting has another niche use for accessing files below a mount point. Assume someone ran:
mkdir -p /mounts/disk
echo "content" | sudo tee /mounts/disk/hidden.txt
sudo mount /some/disk /mounts/diskWhen opening /mounts/disk, the file hidden.txt is not accessible anymore. It is not gone, just hidden by the mount above it. Bind mounting ignores mounts on subdirectories of its target, so we can make the file accessible again:
mkdir /real_mounts
sudo mount -o bind /mounts /real_mountsThe file hidden.txt is now accessible again under /real_mounts/disk/hidden.txt.
A typical use of bind mounts is to expose only part of a filesystem at a mount point. Assuming you have mounted an entire disk at /mydisk, you can give someone access to only the subdirectory /mydisk/users/bob using a bind mount:
sudo mount -o bind /mydisk/users/bob /home/bob/diskThis can even be done to effectively strip parent directories from view, by bind mounting a mount's own subdirectory over itself:
sudo mount /some/disk /mydisk
sudo mount -o bind /mydisk/public /mydiskAs a result, only the subdirectory /mydisk/public is available under /mydisk, even though the parent directory and its contents still exist and are also mounted there, just underneath the bind mount, so inaccessible.
The special behavior of bind mounts has made them an invaluable piece of modern container runtimes, which use them to protect pseudo root filesystems and to cross namespace isolation boundaries between container and host filesystems.