1. Introduction

Similar to /etc/mtab, the /etc/fstab (FileSystem TABle) file is a way to define filesystem mount points and options. In addition, it’s usually employed during boot to mount most entries automatically. Due to the fundamental role of the filesystem in computing, knowing how to mount according to the use case and conditions can be paramount.

In this tutorial, we explore the /etc/fstab file and the options it provides for mounting. First, we briefly refresh our knowledge about /etc/fstab. After that, we turn to the filesystem and device specification field details. Finally, we explore the options that most entries in /etc/fstab support.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. Unless otherwise specified, it should work in most POSIX-compliant environments.

2. /etc/fstab (FileSystem TABle)

Essentially, /etc/fstab is a text file with one entry per line that employs the usual octothorp for comments:

$ cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# systemd generates mount units based on this file, see systemd.mount(5).
# Please run 'systemctl daemon-reload' after making changes here.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda1 during installation
UUID=0384f97c-c436-4f80-914e-bd26674d32fc /               ext4    errors=remount-ro 0       1
# /tmp was on /dev/sda7 during installation
UUID=349107bc-693f-4e74-6660-2986bedcd49c /tmp            ext4    defaults        0       2
# /var was on /dev/sda5 during installation
UUID=bc7e309f-ead1-4301-0f1b-dcc795bd877f /var            ext4    defaults        0       2
# swap was on /dev/sda6 during installation
UUID=8acf3237-7b40-45b8-b666-cf534dfb8181 none            swap    sw              0       0
/dev/sr0        /media/cdrom0   udf,iso9660 user,noauto     0       0

As we can already notice, each line has several fields:

  1. file system device: how we indicate the filesystem device can vary, but a Universal Unique IDentifier (UUID) is usually the best choice
  2. mount point path: where the kernel should place the new mount
  3. type of filesystem: usually one of /proc/filesystems, but it can be a comma-separated list
  4. options for mount: comma-separated list of modifiers
  5. dump filesystem: whether to dump (back up) the filesystem (1) or not (0)
  6. pass order: priority for filesystem integrity checks (#) or 0 to skip

Further, the comments hint that systemd employs /etc/fstab for its mount units. Hence, performing a daemon-reload after changes to /etc/fstab is a good idea.

While there are alternatives to this mounting method, they either rely on fstab similar to systemd.automount or are older like /etc/autofs/.

Since the type, dump, and pass fields are fairly basic to understand and use, let’s check the last two, and more complex, fields.

3. /etc/fstab Filesystem Device Specification

Linux refers to filesystem devices in four main ways:

  • custom device label like Debian, Personal, and others
  • system device path like /dev/sdx, /dev/hdx, and similar
  • system hardware block device ID like scsi-360022480809666fb097b81a7a090e1d7, scsi-360022480809666fb097b81a7a090e1d7-part2, and similar
  • generated filesystem UUID like 349107bc-693f-4e74-6660-2986bedcd49c

Notably, we can specify both a LABEL and PARTLABEL similar to UUID and PARTUUID. The PART* identifiers usually mean we work with a GUID Partition Table (GPT).

In any case, a UUID usually preserves better stability over time.

3.1. Device Paths

If we migrate from an HDD to an SSD, the device path may change. Even if we switch around cables to different storage media within a system, we might still end up with an incorrect path like /dev/sda1 instead of /dev/sdb1.

3.2. Labels

When dealing with labels, ambiguity is often the biggest problem. Another is support since not all filesystems understand labeling. This can also lead to a mix between labels and paths in /etc/fstab, which can be confusing.

3.3. UUID

On the other hand, a UUID is a generated string that a system uses to refer to a partition. This is based on the partition instead of the underlying device and doesn’t change as long as the partition metadata remains the same. In fact, we can usually preserve a UUID even when modifying the definition of a given partition, but this isn’t often recommended.

3.4. Hardware Block Device ID

Lastly, a hardware block device ID is often based on the actual hardware ID, so it can also serve as a good and stable way to reference a device or partition. Still, unlike UUID, it’s linked to the physical medium.

3.5. Verifying /etc/fstab Entries

Different scenarios may require verifying the entries in fstab:

  • migrating between storage media
  • moving storage medium to another machine
  • permanently encrypting partitions
  • permanently decrypting partitions
  • permanently changing the encryption settings for a partition
  • component changes
  • rewiring

Failing to register a bad change or lack thereof may result in a critical filesystem like / root not mounting. This may leave the system in recovery mode at best or require special tools to restore at worst.

4. /etc/fstab Mount Options

Perhaps the central and more complex part of the /etc/fstab file is the options column that defines specific modifications to the behavior of a given mount. These are supplied to the standard mount command.

Let’s explore them.

Notably, we only look at options that apply to any filesystem, regardless of whether all filesystems recognize them. There are many other specific options that depend on the filesystem type.

In most cases, the last specified option overwrites its counterparts earlier in the list, even if they are implied.

4.1. defaults Basic Options

Often, we encounter only defaults in the options column. This isn’t a specific setting but a set of common options:

  • rw: allow reading and writing
  • suid: enable special security bits
  • dev: enable block-special devices
  • exec: permit binaries
  • auto: automatic mount
  • nouser: only root can mount (implicit default)
  • async: asynchronous IO

Let’s delve into those and other options.

4.2. auto or noauto Mount on Boot

The auto option can be used with most entries:

$ cat /etc/fstab
[...]
UUID=633ebbcc-4ae0-48ab-a4e2-c08666547564 /initx            ext4    auto        0       0

When an entry includes auto, the filesystem should be mounted during boot.

Further, if we issue mount with the –all (-a) flag, the system attempts to mount all auto entries:

$ mount --all

This way, we don’t need to specify each mount separately.

Since defaults includes auto, we can deduce that this is the more common option.

However, there are cases when we might want to use noauto and explicitly prevent automatic mounts.

Let’s see an example:

$ cat /etc/fstab
[...]
UUID=e46741f7-70c0-4eca-b797-f3c4d3df6a87 /var/kubernetes            ext4    noauto        0       0

In this case, we have a mount that contains Kubernetes data. Since Kubernetes might not start for a while or at all after the system boots, it’s usually a better idea to perform this mount manually from the script that initializes Kubernetes itself.

4.3. exec or noexec Binaries

By default, *the mount command enables the [exec]ution of files on new mounts*. This is usually preferred since storage is often universal and aims to accommodate multiple scenarios.

Depending on the contents of a filesystem, we may want to prevent binary file execution with noexec:

  • backups
  • databases
  • data-only media
  • known infected storage

In these instances, we can still set a file as executable, but we wouldn’t be able to actually run it.

$ mount
[...]
/dev/sdx8 on /backup type ext4 (rw,noexec,relatime)
$ cd /backup
$ chmod +x script.sh
$ ls -lh
total 13K
-rwxr-xr-x 1 root root  50 Mar 22 06:56 script.sh
$ ./script.sh
-bash: ./script.sh: Permission denied

Although the executable bit is set on script.sh, we get a Permission denied error during execution.

Still, *we can use an executable from an exec filesystem to run the noexec-filesystem script*:

$ bash script.sh
Success.

In this case, we employ the bash interpreter from /bin/ on the root filesystem.

4.4. sync (dirsync) or async Input and Output

The async option is the default as it optimizes performance and extends the lifespan of storage media. Filesystem drivers achieve this mainly through multiple threads and buffering.

Depending on the way storage is used, *we can prefer to set its input and output as [sync]hronized*, which implies several conditions:

  • single-threaded processing, uses only one CPU core
  • no buffering, usually leading to more wear and higher overall usage
  • predictable behavior
  • less prone to data loss

In general, sync prioritizes safety over performance. The sync option only affects ext*, *fat, ufs and xfs filesystems.

Notably, dirsync is similar, but only affects directory updates for some system calls: creat(), link(), unlink(), symlink(), mkdir(), rmdir(), mknod(), and rename().

4.5. dev or nodev

Character and block devices are files that represent access points to devices. Of course, to have such a file, a device needs proper drivers or recognition by the Linux kernel. The main distinction between the two is the way they handle data: by character or by block.

For example, /dev/sda is a way to refer to, read from, write to, and generally manipulate the first storage drive. The same goes for devices like /dev/ttyS*, but also /dev/char/10:1 and similar.

Whether we want a filesystem to recognize character and block device files as such (dev) or not (nodev) depends on the way we intend to use that filesystem. In general, if we don’t expect such files, but enable their recognition, it can become a vulnerability.

4.6. Security Options

Several options relate to security and permissions.

The main way to prevent any writes to a filesystem is to use the ro option, which performs a read-only mount. On the other hand, the default rw enables both reading and writing.

Further, the suid option allows the use and interpretation of suid and sgid bits for running executables with elevated privileges. Since this mechanism can provide a loophole around the regular permissions, we might want to use nosuid and disable it.

Similar to the configuration for other special files, the nosymfollow option disables following symbolic links during path resolution but doesn’t prevent their creation.

When it comes to users that can perform a particular mount there are several ways to manage them:

  • user: any user is allowed to mount the filesystem
  • group: any user from the device owner group is allowed to mount the filesystem
  • users: like user but disregards who performed the initial mount
  • owner: any user is allowed to mount the filesystem as long as they are the owner of the device
  • nouser: only root is allowed to mount the filesystem (implicit default)

Notably, the user, group, owner, and users options also imply several others:

  • noexec
  • nosuid
  • nodev

Lastly, the context, fscontext, defcontext, and rootcontext options enable the specification of extended attributes in some filesystems that don’t natively support them.

4.7. Performance Options

In terms of performance, multiple options can disable certain filesystem features to decrease wear and increase speed and throughput. Often, such settings come down to metadata updates.

For example, lazytime forces the filesystem to only work on access, modify, and change times in-memory. With this buffering, storage writes are significantly reduced at the cost of potential data loss, since timestamps are only written to storage in one of four cases:

  • inode updates not related to timestamps
  • fsync(), syncfs(), or sync() calls
  • undeleted inode removed from memory
  • every 24 hours

As expected, nolazytime disables lazytime.

Specifically for access times, noatime (implies nodiratime) prevents inode updates for access times on any filesystem object. This is the default option. On the other hand, we can add atime to force these updates at the cost of performance. The *dir* relatives of atime and noatime function the same way, but only affect directories. Unlike other options, noatime disables diratime, which is normally the default.

If we do need to update access times, but only if they are currently before the modify or change times, we can use relatime.

Another performance setting is iversion (with its counterpart noiversion) that keeps track of inode versions in the i_version field on each update.

4.8. Extract X-* and x-* Options

The X-* and x-* custom options are both ways to pass comments or userspace options to applications. The main difference between them is that X-* options are stored in userspace. Usually, the suggested format for either is similar to [X|x]-appname.option

For instance, the X-mount.mkdir option makes mount create a directory for the mount point if it doesn’t exist. In addition, we can provide the optional octal mode (default is 0755) of the directory. Another example is the X-mount.subdir option, which points to a subdirectory instead of the root of a filesystem to mount.

5. Summary

In this article, we explored the /etc/fstab file and most filesystem-agnostic options it supports.

In conclusion, automated filesystem mounts are essential to any system, so knowing how to manage their options can be critical.