1. Introduction

In Linux system administration, optimizing our system’s performance and security often involves making strategic decisions about our filesystem layout. One such decision is moving system directories to separate partitions. By isolating critical system directories on their partitions, we can better manage disk space, contain potential filesystem corruption, and improve system performance in certain scenarios.

In this tutorial, we’ll discuss two approaches. First, we’ll explore moving key system directories like /var, /home, and /tmp to separate partitions. Then, we’ll examine using bind mounts as an alternative method for more advanced configurations.

While the first method is straightforward and suitable for most scenarios, bind mounts offer greater flexibility and can be beneficial in complex setups where maintaining the original directory structure is crucial. Let’s get started.

2. Understanding System Directories

Before we dive into the process of moving directories, let’s briefly recap the role of key system directories in a Linux environment:

  • /var – contains variable data like logs, temporary files, and caches, which tend to grow over time and can benefit from being on a separate partition
  • /home – stores user home directories, and separating it can protect user data during system upgrades or reinstalls
  • /tmp – stores temporary files, and isolating it can prevent the root filesystem from filling up due to temporary file creation
  • /usr – contains most user utilities and applications, and it’s often kept on the root partition but can be separated for certain use cases
  • /opt – used for installing optional software packages

While we can theoretically move any directory to a separate partition, the most commonly relocated ones are /var, /home, and /tmp due to their variable size and benefits gained from isolation, such as enhanced security, disk encryption, and easier backups. Therefore, we should ensure that our partition decision reflects our system’s needs and usage patterns now and later.

First, we should consider each directory’s growth rate. For example, /var might need more space on a busy server with many logs. Also, we should think about our backup strategy. Separating /home can make it easier to back up user data independently of system files. Additionally, if we’re using SSDs, we might want to keep frequently accessed directories on faster storage while moving less critical data to HDDs.

However, we should remember there’s no one-size-fits-all solution. The ideal partition decision will depend on our specific use case and system requirements.

3. Prerequisites

Moving system directories is a complex operation that, if not done correctly, can render our system unbootable or cause data loss. If possible, we should create a full system backup before proceeding with any of the steps we shall discuss. This ensures we can recover our data if anything goes wrong during the process.

Then, before we start moving directories, we need to prepare our system by identifying available partitions, current disk usage, and space, as well as making key partition decisions.

First, we can use the lsblk command to list information about all available or specified block devices:

$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0  200G  0 disk 
├─sda1   8:1    0   50G  0 part /
├─sda2   8:2    0  100G  0 part /home
└─sda3   8:3    0   50G  0 part /mnt/data

By default, it prints all our block devices (except RAM disks) in a tree-like format.

Alternatively, we can use the fdisk -l command to list all available partitions on the system along with their sizes:

$ fdisk -l
Disk /dev/sda: 200 GiB, 214748364800 bytes, 419430400 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 12345678-90AB-CDEF-1234-567890ABCDEF

Device     Start       End   Sectors  Size Type
/dev/sda1   2048 104857599 104855552   50G Linux filesystem
/dev/sda2 104857600 314572799 209715200  100G Linux filesystem
/dev/sda3 314572800 419430399 104857600   50G Linux filesystem

Next, we use the du -sh command to estimate file space usage and report the size of the intended directory:

$ du -sh /var
2.5G    /var

Afterward, we should assess our current disk usage with the df command to ensure we have enough space on the target partition(s) for the directories we plan to move:

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   30G   18G  63% /
/dev/sda2       100G   40G   57G  41% /home
/dev/sda3        50G   10G   37G  22% /mnt/data

In short, we should compare the directory size to be moved (using du -sh /directory) with the available space on the target partition (using df -h). Then, we verify that the target partition has sufficient space for the directory plus additional space for future growth.

4. Choosing Between Methods

After checking out our prerequisites, before we proceed, we need to carefully consider the method that best suits our needs.

The first method involves moving the directories to separate partitions. Its benefit is that it’s simple, straightforward, and suitable for most scenarios. However, the downside is that it requires creating and managing multiple partitions.

If we decide to follow along with this method, it involves:

Alternatively, the other method involves using bind mounts. The benefit of this method is greater flexibility, maintains the original directory structure, and is ideal for complex setups. However, the drawback is that it adds complexity to overall system management, which is better suited for advanced users.

For this second method, we should follow the last section:

Ultimately, we should ensure we choose the method that aligns with our system requirements and our comfort level with partition management.

5. Creating Separate Partitions

Now that we’ve reviewed both methods, let’s dive into the first approach: moving directories to separate partitions. We’ll start by creating new partitions for each directory we want to move.

For our commands, we’ll imagine we’re partitioning /dev/sda we identified earlier and creating new partitions /dev/sda4, /dev/sda5, and /dev/sda6 for /var, /home, and /tmp, respectively. However, aside from this tutorial example, each of these commands should be done with the drive we identify on our system.

We’ve separately discussed in detail the process of creating new partitions and resizing them, but let’s briefly recap the steps here:

  1. Use fdisk -l or lsblk to identify our disk devices
  2. Run fdisk on the preferred drive to start the partitioning process (for example, fdisk /dev/sda, if we’re using /dev/sda)
  3. Use the n command to create a new partition, following the prompts to set its size
  4. Repeat for each directory we’re moving
  5. Use the w command to write the changes and exit fdisk

The process is straightforward, but we should be careful. It requires careful execution to avoid data loss or system instability.

5.1. Formatting the Partitions

After creating the partitions (sda4, sda5 and sda6 for this example), we’ll now format them with the appropriate filesystems.

To do this, we can use the ext4 filesystem, which is a common choice for Linux partitions:

$ mkfs.ext4 /dev/sda4
$ mkfs.ext4 /dev/sda5
$ mkfs.ext4 /dev/sda6

mke2fs 1.45.5 (07-Jan-2020)
Creating filesystem with 2621440 4k blocks and 655360 inodes
..
Writing superblocks and filesystem accounting information: done

Here, if our new partition identifiers differ from sda4, sda5 and sda6, we should replace them.

ext4 is the default filesystem for many Linux distributions due to its good performance and reliability.

Alternatively, and depending on our needs, we can also use others like xfs or btrfs:

# For XFS filesystem (good for larger partitions)
$ mkfs.xfs /dev/sda4
$ mkfs.xfs /dev/sda5
$ mkfs.xfs /dev/sda6

Notably, xfs is more suitable for larger partitions and large files, while btrfs provides advanced filesystem features like snapshots, built-in RAID, and self-healing.

5.2. Verifying the Filesystem

Lastly, we can verify the filesystem creation with blkid:

$ blkid /dev/sda4
$ blkid /dev/sda5
$ blkid /dev/sda6

/dev/sda4: UUID="d1c93d5b-8e8b-4c9d-9871-9d41a52f2d0a" TYPE="ext4" PARTUUID="12345678-04"
/dev/sda5: ...
/dev/sda6: ...

With the filesystems created, we can now proceed to move the directories to these new partitions.

6. Moving Directories to New Partitions

We can now move preferred directories such as /var, /home, and /tmp to their respective new partitions. This process involves copying the data to the new partitions and updating the system’s mount points.

Let’s see an example process for moving /var, which applies to any other directory as well.

First, we mount the new partition temporarily:

$ mkdir /mnt/var
$ mount /dev/sda4 /mnt/var

Then, we use rsync to copy the data from the current /var to the new partition (preserves permissions and attributes):

$ rsync -avx /var/ /mnt/var/
sending incremental file list
./
binlog.index
cache/
...
sent 150,264,847 bytes received 82,334 bytes 12,022,462.48 bytes/sec
total size is 149,235,200 speedup is 0.99

Notably, -a for archive mode preserves permissions and timestamps, -v for verbose output, and -x to stay on one filesystem.

Next, we switch to single-user mode to minimize system activity:

# For systemd-based systems
$ systemctl isolate rescue.target

# For older SysVinit systems
$ init 1

Notably, rescue.target (systemd) and run level 1 (SysVinit) are similar but not identical. The rescue.target provides a basic system with most services stopped but with some key services still running. This can be more convenient for system maintenance.

We can now rename the old /var directory and create a new, empty /var directory:

$ mv /var /var.old
$ mkdir /var

Afterward, we now unmount the temporary mount and mount the new partition to /var:

$ umount /mnt/var
$ mount /dev/sda4 /var

umount: /mnt/var: unmounted.
mount: /var: mounted /dev/sda4.

Lastly, we update the /etc/fstab file to ensure the new partition is mounted at boot:

$ vi /etc/fstab

# Editing the /etc/fstab file
...
/dev/sda4 /var ext4 defaults 0 0

Notably, each line in the /etc/fstab file represents a filesystem that should be mounted automatically. The format is .

Finally, we should reboot the system to apply the changes:

$ reboot

Similarly, we follow the same steps for other directories we want to move, like /home and /tmp, adjusting the partition identifiers and mounting points accordingly.

7. Configuring Mount Points

After moving the directories, we need to configure the mount points properly to ensure the system uses the new partitions.

But before we proceed, we should switch to single-user mode to minimize system activity:

# For systemd-based systems
$ systemctl isolate rescue.target

# For older SysVinit systems
$ init 1

This provides a safer environment for making system-level changes we’re about to make.

Now, we need to ensure that the mount points (directories) exist. For example, if we’re moving /var, /home, and /tmp:

$ mkdir -p /mnt/var /mnt/home /mnt/tmp

Here, we use the -p flag with mkdir to ensure all parent directories are created as needed.

Then, we temporarily mount the new partitions:

$ mount /dev/sda4 /mnt/var
$ mount /dev/sda5 /mnt/home
$ mount /dev/sda6 /mnt/tmp

Afterward, we can now check if the partitions are mounted correctly by checking for disk space usage:

$ df -h | grep '/mnt'
Filesystem Size Used Avail Use% Mounted on
/dev/sda4 20G 2G 18G 10% /mnt/var
/dev/sda5 20G 5G 15G 25% /mnt/home
/dev/sda6 10G 1G 9G 10% /mnt/tmp

Our output now shows the mounted partitions and their usage.

Finally, to make these mounts permanent, we need to update the /etc/fstab file:

$ vi /etc/fstab

# Editing the /etc/fstab file
/dev/sda4       /var     ext4    defaults    0 0
/dev/sda5       /home    ext4    defaults    0 0
/dev/sda6       /tmp     ext4    defaults    0 0

This ensures that the partitions are mounted automatically at boot.

With these steps, we ensure that the moved directories are properly mounted on their new partitions and will be automatically mounted at system boot.

8. Ensuring System Stability

Maintaining system stability after the move is critical. Let’s look at ways to ensure our system remains stable after the process.

8.1. Verifying Data Integrity

First, after moving the directories, we should verify that all data has been copied correctly.

To do this, we can use diff or rsync to verify the integrity of the copied data:

Let’s see an example with diff:

$ diff -r /var.old /mnt/var

The diff command can recursively compare directories to ensure that files have been copied correctly. If diff produces no output similar to what we have here, the directories are identical. If otherwise, any differences will be listed, indicating discrepancies that need attention.

Alternatively, we can use the rsync command to verify data integrity by performing a dry run:

$ rsync -avxn /var.old/ /var/

Here, the -n flag performs a dry run, -a is for archive mode, -v is for verbose, and -x is to stay on one filesystem. Similarly, no output indicates no differences.

8.2. Restoring Security Contexts with SELinux

If our system uses SELinux, we need to restore the correct security contexts for each directory with restorecon to ensure system security:

$ restorecon -R /var
$ restorecon -R /home
$ restorecon -R /tmp

Here, restorecon restores the default SELinux security contexts. The -R flag applies the restoration recursively to all files and directories.

8.3. Monitoring System Logs

After completing the moves and verifying data integrity, we can also keep an eye on system logs for any unusual activity or errors:

$ tail -f /var/log/messages
$ tail -f /var/log/syslog
$ tail -f /var/log/dmesg

Jul 16 12:34:56 hostname systemd[1]: Started Session 1 of user root.
Jul 16 12:34:56 hostname systemd[1]: Starting Daily apt download activities...
...

Notably, tail -f follows the log files in real time. We should look for error messages or unusual entries that could indicate issues with the new configuration.

9. Using Bind Mounts as an Alternative Approach

Aside from the earlier method (sections 5 – 8) we have discussed, bind mounts can also come to our rescue as an alternative approach that provides additional flexibility in how directories are organized and accessed.

Specifically, bind mounts allow us to make a directory or file available at multiple locations in the filesystem hierarchy. They can be particularly useful in scenarios where we want to maintain the original directory structure while storing data on just a separate partition.  That’s where the difference lies.

9.1. When to Choose Bind Mounts

While the primary method of moving directories to separate partitions is suitable for most scenarios, there are specific situations where we’re better off with bind mounts:

  • Complex Directory Structures: If we have a complex directory structure that we want to preserve while still benefiting from separate partitions, bind mounts allow us to maintain the original structure while storing data on different partitions.
  • Resource Isolation: Bind mounts can be very useful when we need to provide isolated views of the filesystem to different applications or users without physically separating the data.
  • Data Sharing: Bind mounts offer an efficient solution for sharing specific directories across multiple locations in our filesystem without duplicating data.
  • Flexible Partition Management: Bind mounts provide the necessary flexibility when we want to reorganize our filesystem layout without changing the directory structure that applications expect.
  • Testing and Development: Bind mounts can be beneficial in testing and development environments where we need to simulate different filesystem configurations without altering the underlying structure.

However, we should keep in mind that bind mounts add a layer of complexity to our system. For straightforward scenarios where we simply need to move directories to separate partitions, the primary method we discussed earlier is often simpler and easier to manage.

On the other hand, managing bind mounts in their entirety may require a deeper understanding of Linux filesystem management.

Let’s now see how we can use bind mounts to organize system directories on a separate partition.

9.2. Implementing Bind Mounts for System Directories

First, we create a directory for the primary mount point:

$ mkdir /mnt/data

Then, we mount the partition to the primary mount point:

$ mount /dev/sda4 /mnt/data

For this example, we’re using sda4 as our partition. We can replace sda4 with the appropriate partition we prefer.

Afterward, we move the directories to the primary mount point:

$ mv /var /mnt/data/
$ mv /home /mnt/data/
$ mv /tmp /mnt/data/

This physically moves the directories to the new partition.

Next, we create empty directories for the original mount points:

$ mkdir /var /home /tmp

Finally, we edit /etc/fstab and add entries for bind mounts:

$ vi /etc/fstab

# Editing the /etc/fstab file

...
/dev/sda4       /mnt/data ext4 defaults 0 0
/mnt/data/var   /var    none    bind    0 0
/mnt/data/home  /home   none    bind    0 0
/mnt/data/tmp   /tmp    none    bind    0 0

Here, the first line mounts the primary partition. Then, the subsequent lines create bind mounts for /var, /home, and /tmp.

Lastly, we can now mount bind directories and apply the changes by mounting all filesystems:

$ mount -a
mount: /var: mounted.
mount: /home: mounted.
mount: /tmp: mounted.

As we can see, mount -a mounts all filesystems we have mentioned in /etc/fstab.

10. Conclusion

Moving system directories to separate partitions is a valuable practice for optimizing Linux systems. In this article, we have covered two best approaches: directly moving directories to separate partitions and using bind mounts as an alternative method.