Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: August 8, 2024
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.
Before we dive into the process of moving directories, let’s briefly recap the role of key system directories in a Linux environment:
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.
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.
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.
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:
The process is straightforward, but we should be careful. It requires careful execution to avoid data loss or system instability.
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.
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.
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 <filesystem> <mount point> <type> <options> <dump> <pass>.
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.
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.
Maintaining system stability after the move is critical. Let’s look at ways to ensure our system remains stable after the process.
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.
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.
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.
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.
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:
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.
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.
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.