/srv/tramontana/backups has been growing for weeks. Every night at 04:20, backup_tramontana.sh leaves a new backup there without anybody having worked out how much fits, and the 23 GB root disk is at 30%. The question is not whether it will fill up, but when — and what will happen to the application when it does, because a full / is not "it is running slowly": it is a machine that cannot even write a log or complete a transaction. In this lesson you finally go down to the hardware: you will see the full stack from the physical disk to the mount point, you will partition, format, understand /etc/fstab field by field (and why one badly written line stops the machine booting), and set up LVM, which is what a real server uses. By the end, /srv/tramontana/backups will live on its own logical volume, expandable while it is in use.

Contents

  1. The storage stack, from top to bottom
  2. Identifying the hardware: lsblk, blkid, fdisk -l
  3. Partition tables: MBR versus GPT
  4. Partitioning with fdisk and parted
  5. Filesystems compared, and inodes revisited
  6. Mounting: mount, options, bind mounts and findmnt
  7. /etc/fstab field by field and the safety net
  8. Space: df, du, and the deleted file that does not free it
  9. Swap: partition, file and how much you need
  10. LVM: PV, VG, LV, growing live and snapshots
  11. Software RAID with mdadm
  12. Disk quotas
  13. Tramontana case: a new disk for the backups

  1. The storage stack, from top to bottom

Between the platter (or the NAND cell) and the cat file.txt you type there are five or six layers. Confusing them is the cause of 90% of storage mistakes.

flowchart TD
    D["Physical disk<br/>/dev/sdb — 20 GiB"] --> T["GPT partition table"]
    T --> P1["Partition /dev/sdb1"]
    P1 --> PV["PV — pvcreate"]
    PV --> VG["VG vg-data<br/>groups PVs, 5G free"]
    VG --> LV1["LV lv-backups 15G"]
    LV1 --> FS["Filesystem<br/>mkfs.ext4 — UUID"]
    FS --> M["Mount point<br/>/srv/tramontana/backups"]

The LVM layers are optional — without them the partition is formatted directly — but they solve the worst problem of all: a partition cannot be enlarged unless the free space is right behind it; a logical volume can, even if the space is on another disk.

  1. Identifying the hardware: lsblk, blkid, fdisk -l

$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda      8:0    0   25G  0 disk
├─sda1   8:1    0    1M  0 part
├─sda2   8:2    0    2G  0 part /boot
└─sda3   8:3    0   23G  0 part /
sdb      8:16   0   20G  0 disk    # the new disk, still with no partition table
$ lsblk -f
NAME   FSTYPE LABEL  UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
sda3   ext4   root   3f0a91c4-8d2e-4b17-9c55-0a7ce31f77b2   14.8G    30% /
sdb

lsblk -f is the command you will use most often: it tells you the filesystem type, the label, the UUID and the actual usage.

$ sudo blkid /dev/sda3
/dev/sda3: LABEL="root" UUID="3f0a91c4-8d2e-..." TYPE="ext4" PARTUUID="a1b2c3d4-03"
$ sudo fdisk -l /dev/sda | sed -n '1,2p;5p'
Disk /dev/sda: 25 GiB, 26843545600 bytes, 52428800 sectors
Disklabel type: gpt
Prefix What it is Where it turns up
/dev/sda, sdb… SATA, SAS, USB and most virtual ones VirtualBox, physical machines
/dev/nvme0n1 (partitions p1) NVMe SSD Modern servers and laptops
/dev/vda Paravirtualised virtio disk KVM, cloud (AWS, OpenStack)

And the critical detail: these names can change between boots. It only takes adding a disk, changing the order of the controllers or the kernel detecting devices in a different order for today's sdb to be tomorrow's sdc. That is why you never mount by device name in fstab: you mount by UUID or by LABEL, which travel inside the filesystem itself.

  1. Partition tables: MBR versus GPT

Aspect MBR (msdos) GPT
Age 1983 2000, part of UEFI
Maximum disk size 2 TiB 8 ZiB (no practical limit)
Number of partitions 4 primary (or 3 + extended) 128 by default
Redundancy and integrity None: one damaged sector and goodbye A copy at the end of the disk and CRC32
Booting Legacy BIOS UEFI (and BIOS with a bios_grub partition)

For any new disk today: GPT, with no exceptions worth discussing. The 1 MiB partition you see as sda1 is precisely the bios_grub one GRUB needs on a GPT disk booted by legacy BIOS; its role is explained in 07-01.

  1. Partitioning with fdisk and parted

fdisk is interactive and convenient; parted accepts one-line commands, which makes it suitable for scripts. We prepare /dev/sdb with a single partition taking up the whole disk:

$ sudo parted -s /dev/sdb mklabel gpt
$ sudo parted -s /dev/sdb mkpart data 1MiB 100%
$ sudo parted -s /dev/sdb set 1 lvm on
$ sudo parted -s /dev/sdb print | tail -3
Partition Table: gpt
Number  Start   End     Size    Name  Flags
 1      1049kB  21.5GB  21.5GB  data  lvm

The 1MiB start is not a whim: it aligns the partition with the physical blocks and avoids a noticeable performance penalty on SSDs and on storage arrays.

In fdisk the equivalent session would be g (create a GPT), n (new partition), Enter three times, t and 31 (type Linux LVM) and w to write. Nothing is written until the w: q gets you out without touching anything, and that is fdisk's safety net.

If the kernel does not notice the new table — typical when the disk has a partition mounted — sudo partprobe /dev/sdb re-reads it, and lsblk /dev/sdb confirms that sdb1 has appeared.

  1. Filesystems compared, and inodes revisited

FS Maturity Grow / Shrink Snapshots When to choose it
ext4 The highest; the default on Ubuntu Yes / yes, unmounted No (LVM provides them) The safe option for almost everything
XFS Very high; the default on RHEL While mounted / no No Large files, parallel writes
Btrfs Stable for common uses Yes / yes Yes, native Frequent snapshots, checksums
ZFS Very high, integrated into Ubuntu Yes / depends on the design Yes, plus send/receive RAID + FS unified; demands RAM

A practical rule for a conventional Ubuntu server: ext4 on LVM. You get resizing, snapshots (LVM's) and the most battle-tested filesystem there is.

$ sudo mkfs.ext4 -L backups /dev/vg-data/lv-backups
Creating filesystem with 3932160 4k blocks and 983040 inodes
Filesystem UUID: 9d4f2b70-6c1a-4e8b-b3f7-52a0c9e14d68
$ sudo tune2fs -l /dev/vg-data/lv-backups | grep -E 'Volume name|Inode count'
Volume name:              backups
Inode count:              983040

tune2fs lets you change the label (-L), the check interval (-i) and, very useful on a data volume, the 5% reserved for root: sudo tune2fs -m 1 /dev/vg-data/lv-backups recovers about 600 MiB out of 15 GiB. That 5% makes sense on / — it stops a full disk preventing root from fixing the situation — but on a volume dedicated to backups it is wasted space.

Inodes: the other limit

In 02-06 you saw that the inode holds the metadata and that the name is just a directory entry. Here comes the operational consequence: the number of inodes is fixed when you format and it does not grow. A directory with millions of tiny files can exhaust them with the disk half empty, and the error is baffling:

$ df -h /srv/tramontana/backups | tail -1
/dev/mapper/vg--data-lv--backups  15G   6.1G  8.2G  43% /srv/tramontana/backups
$ df -i /srv/tramontana/backups | tail -1
/dev/mapper/vg--data-lv--backups  983040  983021      19  100% /srv/tramontana/backups

43% of free space and No space left on device when creating a file. Faced with that error, df -i is the mandatory second check. The solution means deleting small files or reformatting with more inodes (mkfs.ext4 -i 8192).

  1. Mounting: mount, options, bind mounts and findmnt

sudo mkdir -p /mnt/test && sudo mount /dev/vg-data/lv-backups /mnt/test
sudo mount -o remount,ro /mnt/test   # change options without unmounting; umount to release

If umount answers "target is busy", pick up lsof and fuser from 03-06:

$ sudo fuser -vm /mnt/test
                     USER        PID ACCESS COMMAND
/mnt/test:           operator   2841 ..c..  bash

Somebody — you, probably — has their working directory there. umount -l (lazy) unmounts when it is released, but it is a patch.

Option What it does When to use it
defaults rw,suid,dev,exec,auto,nouser,async The starting point
noatime Does not update the last-access timestamp Always on servers: it saves writes
nodev / nosuid Ignores device files / SUID bits Any data volume: it closes the route from 05-02
noexec / ro Forbids running binaries / read-only Backups, /tmp, uploads; forensics
nofail Booting continues if the device is missing Every disk that is not /
$ findmnt -no SOURCE,FSTYPE,OPTIONS /srv/tramontana/backups
/dev/mapper/vg--data-lv--backups ext4 rw,noatime,nodev,nosuid

findmnt is infinitely more readable than mount with no arguments, and findmnt --verify validates the fstab without mounting anything. A bind mount (sudo mount --bind /srv/tramontana/backups/outgoing /opt/tramontana/output) makes an existing directory appear at another point in the tree without copying anything: it is the clean way of exposing a folder to a confined service, and in 05-05 you will see that systemd does the same thing with BindPaths.

  1. /etc/fstab field by field and the safety net

# /etc/fstab
# <device>                                   <mount point>            <type> <options>                   <dump> <fsck>
UUID=3f0a91c4-8d2e-4b17-9c55-0a7ce31f77b2    /                        ext4   defaults,noatime             0      1
UUID=b7e12a55-0c4d-4e39-8a61-3f9d2b70c1a8    /boot                    ext4   defaults,noatime             0      2
/dev/mapper/vg--data-lv--backups             /srv/tramontana/backups  ext4   defaults,noatime,nodev,nosuid,nofail  0  2
Field Content Notes
1 Device UUID= or LABEL=, never /dev/sdb1
2 and 3 Mount point and type The directory must exist; ext4, xfs, swap, auto
4 Options Comma-separated, no spaces
5 and 6 dump and fsck The 5th is a relic (always 0); the 6th is 1 for /, 2 for the rest and 0 for no check

Why a badly written fstab stops the machine booting

At boot, systemd turns each line of fstab into a .mount unit and makes them a dependency of local-fs.target. If a device does not turn up, the boot waits 90 seconds and then drops into emergency mode asking for the root password — which on Ubuntu is locked. A remote server in that state is a lost server until somebody opens the console.

That is why there are two non-negotiable rules:

sudo cp -a /etc/fstab /etc/fstab.bak-$(date +%F)   # prior backup, as always
sudo vim /etc/fstab && sudo diff -u /etc/fstab.bak-$(date +%F) /etc/fstab
sudo findmnt --verify --verbose                    # syntax validation
sudo mount -a                                      # mount EVERYTHING in the fstab NOW

mount -a before rebooting is not optional. If it fails, you fix it with the server on its feet; if you do not run it, you will find out with the server down. And add nofail to everything that is not /: it turns a boot failure into a warning in the log.

  1. Space: df, du, and the deleted file that does not free it

$ df -h --total | tail -1
total             25G   6.9G   17G  30%
$ sudo du -sh --max-depth=1 /srv/tramontana 2>/dev/null | sort -h
4.8G	/srv/tramontana/backups
6.1G	/srv/tramontana

ncdu does the same thing interactively and is what you will use when you are hunting the culprit in a hurry. Remember from 02-03 that du measures space occupied in blocks and ls -l the logical size: they do not have to match.

The classic: df says full, du says empty

$ df -h / | tail -1
/dev/sda3         23G    23G     0  100% /
$ sudo du -sh /var
2.1G	/var

Gigabytes are missing that nobody can see. The explanation connects directly with the inodes from 02-06: when you delete a file that a process has open, the name disappears from the directory, but the inode and its blocks stay alive until the last descriptor is closed. Somebody ran rm on a huge log and the process is still writing into a file with no name.

$ sudo lsof +L1
COMMAND    PID           USER  FD TYPE  SIZE/OFF NLINK   NODE NAME
tramonta  1284 svc-tramontana  3w  REG 16106127360   0 262147 /var/log/tramontana/access.log (deleted)

NLINK 0 and (deleted) are the signature of the problem. The two ways out: restart the process (systemctl restart, which we will see in 05-05) or, if you cannot, truncate the file through its descriptor with sudo truncate -s 0 /proc/1284/fd/3, which frees the space without killing it.

And the underlying lesson: a log is not deleted, it is rotated — which is exactly what we will set up in 05-06.

  1. Swap: partition, file and how much you need

Swap is disk space the kernel uses to offload rarely used memory pages. Nowadays a swap file is preferred to a partition: it is resized without touching the partition table.

$ sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile
$ sudo mkswap /swapfile && sudo swapon /swapfile && swapon --show
NAME      TYPE SIZE USED PRIO
/swapfile file   2G   0B   -2

With its line in fstab (/swapfile none swap sw 0 0). The chmod 600 is mandatory: the contents of swap include chunks of processes' memory, and that includes secrets.

vm.swappiness (0–200, 60 by default) decides how eager the kernel is to use it; lowering it to 10 is the usual choice on an application server. Fine-tuning this and other kernel parameters is the subject of 07-03. How much swap? With 3.8 GB of RAM, between 2 and 4 GB is reasonable: not as a substitute for memory — if the server swaps constantly it is going to be very slow anyway — but as a cushion that stops the OOM killer from killing the application during an occasional peak.

  1. LVM: PV, VG, LV, growing live and snapshots

LVM introduces three levels: the PVs (partitions or whole disks handed over to LVM), the VG (a pool of space made up of one or more PVs) and the LVs (the "virtual disks" that get formatted and mounted). The VG is divided into 4 MiB extents, and an LV is simply a set of extents, which do not have to be contiguous or on the same disk. That is where all of LVM's power comes from.

$ sudo pvcreate /dev/sdb1 && sudo vgcreate vg-data /dev/sdb1
  Physical volume "/dev/sdb1" successfully created.
  Volume group "vg-data" successfully created
$ sudo lvcreate -L 15G -n lv-backups vg-data
  Logical volume "lv-backups" created.
$ sudo vgs && sudo lvs
  VG      #PV #LV #SN Attr   VSize   VFree
  vg-data   1   1   0 wz--n- <20.00g <5.00g
  LV         VG      Attr       LSize
  lv-backups vg-data -wi-a----- 15.00g

Notice that we have deliberately left 5 GiB free in the VG: it is the margin for growing the volume and, above all, for being able to create a snapshot. A VG at 100% is a VG with no room to manoeuvre.

Growing it live, with the volume mounted and in use:

$ sudo lvextend -L +3G -r /dev/vg-data/lv-backups
  Size of logical volume vg-data/lv-backups changed from 15.00 GiB to 18.00 GiB.
  The filesystem is now 4718592 (4k) blocks long.

The -r option (--resizefs) is the key one: it grows the LV and the filesystem on top of it in a single step. Without it you would have a bigger volume and an identical df, which is the most frequent mistake of anybody starting out with LVM. lvextend -l +100%FREE -r eats all the VG's free space.

Shrinking is another story. You have to do it the other way round (the filesystem first, then the LV), with the volume unmounted, and XFS simply cannot be shrunk. Getting the order wrong here destroys data. The rule: create LVs small and grow them when you need to; never the other way round.

LVM snapshots

A snapshot is an LV that stores the differences from the original since the moment it was created. It is created in seconds and lets you copy a volume in a frozen, consistent state while the application keeps writing:

$ sudo lvcreate -L 2G -s -n snap-pre-deploy /dev/vg-data/lv-backups
  Logical volume "snap-pre-deploy" created.
$ sudo mount -o ro /dev/vg-data/snap-pre-deploy /mnt/snapshot   # copy from here
$ sudo umount /mnt/snapshot && sudo lvremove -y /dev/vg-data/snap-pre-deploy

Two indispensable warnings: the snapshot fills up if the original changes more than its size allows, and when it fills up it is invalidated and lost. And it is not a backup: it lives in the same VG, on the same disk. It is a consistency tool, and that is how we will use it in 05-08.

  1. Software RAID with mdadm

Level Min. disks Usable capacity Tolerates Writing
0 (striping) 2 100% Nothing: one disk dies and everything is lost Very fast
1 (mirror) 2 50% 1 disk Normal
5 / 6 3 / 4 n−1 / n−2 1 / 2 disks Penalised by the parity
10 (1+0) 4 50% 1 per mirror Fast; the choice for databases
$ sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdc1 /dev/sdd1
mdadm: array /dev/md0 started.
$ cat /proc/mdstat | tail -1
      20955136 blocks super 1.2 [2/2] [UU]
$ sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf && sudo update-initramfs -u

[UU] means both disks are healthy; a [U_] is a failed disk and you have to act today, not next week. An LVM PV is then put on top of /dev/md0, and that is how redundancy and flexibility are combined.

RAID is not a backup. RAID protects you against a disk breaking. It does not protect you against an rm -rf (which is replicated to the mirror instantly), nor against ransomware encryption, nor against a controller fault that corrupts both disks, nor against a fire in the room. The backup is 05-08, and they are different, complementary things.

  1. Disk quotas

When several users share a volume, quotas limit how much each of them takes up. They are enabled with usrquota,grpquota in fstab, initialised with quotacheck -cug and set with edquota -u user or non-interactively: sudo setquota -u luis 5G 6G 0 0 /srv/tramontana/backups (soft 5G, hard 6G), and sudo repquota -s /srv/tramontana/backups gives the usage report. The soft limit can be exceeded during a grace period; the hard one is never exceeded. On an application server with a single service user they are of little use; on a shared one they are the difference between an incident and a phone call at three in the morning.

  1. Tramontana case: a new disk for the backups

Marta approves adding a 20 GiB disk to the VM. The goal: to stop /srv/tramontana/backups competing for /'s space, without losing a single byte of the existing backups and without backup_tramontana.sh noticing.

Step 1 — Add the disk. With the VM powered off, VirtualBox → Storage → a 20 GiB disk. On booting, lsblk should show sdb with no partitions.

Step 2 — Partition, PV, VG and LV: the commands from sections 4 and 10, already run. The result: a 20 GiB vg-data with a 15 GiB lv-backups and 5 GiB of margin.

Step 3 — Format and mount at a temporary point.

sudo mkfs.ext4 -L backups /dev/vg-data/lv-backups && sudo tune2fs -m 1 /dev/vg-data/lv-backups
sudo mkdir -p /mnt/new && sudo mount /dev/vg-data/lv-backups /mnt/new

Step 4 — Copy preserving everything. rsync -aHAX is not a luxury: -a preserves permissions and owners (the operator:tramontana from 05-01), -H the hard links, -A the ACLs from 05-02 and -X the extended attributes.

$ sudo rsync -aHAX --info=progress2 /srv/tramontana/backups/ /mnt/new/
$ sudo diff -qr /srv/tramontana/backups /mnt/new && echo "Contents identical"
Contents identical
$ sudo du -sh /srv/tramontana/backups /mnt/new
4.8G	/srv/tramontana/backups
4.8G	/mnt/new

(Before copying, comment out the backup's cron line so that one does not start halfway through the job.)

Step 5 — Swap over, with a safety net. We do not delete the original: we rename it, so the rollback is instantaneous. sudo umount /mnt/new && sudo mv /srv/tramontana/backups /srv/tramontana/backups.old && sudo mkdir /srv/tramontana/backups.

Step 6 — fstab, with the three protections.

$ sudo cp -a /etc/fstab /etc/fstab.bak-$(date +%F)
$ echo "UUID=$(sudo blkid -s UUID -o value /dev/vg-data/lv-backups) /srv/tramontana/backups ext4 defaults,noatime,nodev,nosuid,nofail 0 2" | sudo tee -a /etc/fstab
$ sudo diff -u /etc/fstab.bak-$(date +%F) /etc/fstab | tail -1
+UUID=9d4f2b70-... /srv/tramontana/backups ext4 defaults,noatime,nodev,nosuid,nofail 0 2
$ sudo mount -a && findmnt -no SOURCE,OPTIONS /srv/tramontana/backups
/dev/mapper/vg--data-lv--backups rw,nosuid,nodev,noatime

nosuid and nodev because on a backup volume there must never be a SUID binary or a device file; nofail because a fault on this disk cannot be allowed to stop the server booting.

Step 7 — Restore ownership, verify and clean up.

$ sudo chown -R operator:tramontana /srv/tramontana/backups
$ sudo chmod 2770 /srv/tramontana/backups          # the SGID from 05-02: inherited group
$ sudo -u operator /home/operator/scripts/backup_tramontana.sh --dry-run
[2026-08-18T11:04:12+02:00] dry-run: destination /srv/tramontana/backups (9.4G free)
$ df -h /srv/tramontana/backups | tail -1
/dev/mapper/vg--data-lv--backups   15G   4.9G  9.4G  35% /srv/tramontana/backups

Only when a real backup has worked and its sha256sum has been verified do you delete /srv/tramontana/backups.old, and you write everything down:

sudo tee -a /opt/tramontana/HISTORY >/dev/null <<'END'
2026-08-18  Backup disk (operator)
  - sdb 20G -> GPT + PV + vg-data (5G free for snapshots) + lv-backups 15G ext4
  - rsync -aHAX, fstab by UUID with nofail/nodev/nosuid, mount -a OK
  - backups.old kept until the first verified backup
END

Common Mistakes and Tips

  • Mounting by /dev/sdb1 in fstab. The day the detection order changes, the wrong disk will boot, or none will. UUID or LABEL, always.
  • Rebooting without mount -a. It is the number one cause of servers that do not come back. And nofail on everything that is not /.
  • lvextend without -r. The volume grows, df does not change and you lose half an hour. With -r, the filesystem grows with it. And do not fill the VG to 100%: with no free space there is no snapshot and no margin to grow.
  • Trusting RAID as a backup. It is not one, and finding out on the day of the accidental deletion is expensive.
  • rm on a log that is in use. The space is not freed until the process closes the descriptor: lsof +L1 gives it away. Logs are rotated (05-06).
  • Copying data with cp -r instead of rsync -aHAX: you lose owners, hard links, ACLs and attributes, and then the permissions from 05-01 do not add up.
  • Tip: keep lsblk -f, vgs, lvs and a copy of the fstab alongside the HISTORY. Reconstructing the disk layout from memory, with the server down, is not a plan.

Exercises

  1. Diagnosing a full disk. df -h / shows 100%, but du -sh / adds up to considerably less. List the three possible causes in order of likelihood and the command that confirms each one.
  2. Growing it live. lv-backups (15 GiB) has run short and the VG has 5 GiB free. Grow it by 3 GiB without unmounting, verify that the space is visible to df and explain what would have happened without -r.
  3. A safe fstab entry. Write the fstab line for a user-uploads volume mounted at /opt/tramontana/shared/uploads, with the options a directory written to by third parties must have, and describe how you would validate it before rebooting.

Solutions

1. In order of likelihood:

sudo lsof +L1     # (a) a deleted file with a process keeping it open
df -i /           # (b) inodes exhausted: there is space but nothing can be created
sudo mount --bind / /mnt/root && sudo du -sh /mnt/root/*   # (c) data underneath a mount

The third is the most treacherous: if somebody wrote into /srv/tramontana/backups before the volume was mounted on top, those files still take up space on / but are invisible because the mount covers them. Bind-mounting / at another point brings them into view. It is the risk in step 5 of the Tramontana case, and that is why we created an empty directory instead of reusing the one that had data.

2.

$ df -h /srv/tramontana/backups | tail -1
/dev/mapper/vg--data-lv--backups   15G   4.9G  9.4G  35% /srv/tramontana/backups
$ sudo lvextend -L +3G -r /dev/vg-data/lv-backups
  Size of logical volume vg-data/lv-backups changed from 15.00 GiB to 18.00 GiB.
$ df -h /srv/tramontana/backups | tail -1
/dev/mapper/vg--data-lv--backups   18G   4.9G   12G  29% /srv/tramontana/backups

Without -r, lvs would show 18 GiB and df would go on saying 15 GiB: the ext4 filesystem does not know that the device underneath has grown until it is told with resize2fs. The operation is safe while mounted because ext4 can grow mounted; shrinking would require unmounting and doing it in the reverse order.

3.

LABEL=uploads  /opt/tramontana/shared/uploads  ext4  defaults,noatime,nodev,nosuid,noexec,nofail  0  2

noexec is the key addition compared with the backup volume: in a directory written to by third parties, preventing binaries from running stops a malicious upload turning into running code dead in its tracks. nodev and nosuid close the other two routes, and nofail stops a problem with this volume preventing the boot. Validation before rebooting:

sudo cp -a /etc/fstab /etc/fstab.bak-$(date +%F)   # prior backup
sudo findmnt --verify --verbose                    # syntax and coherence
sudo mount -a && findmnt /opt/tramontana/shared/uploads   # mount and see the effective options

Note that findmnt at the end shows the effective options: it is the only proof that what you wrote is what the kernel applied.

Conclusion

There is no longer an opaque layer between your files and the hardware. You walk the whole stack — disk, partition table, partition, PV, VG, LV, filesystem, mount point — and you know what problem each rung solves; you identify the hardware with lsblk -f, blkid and fdisk -l, and you know why the /dev/sdX names are not trustworthy and the UUID is; you choose GPT without hesitating, you partition with parted in one line and with fdisk knowing that nothing is written until the w; you compare ext4, XFS, Btrfs and ZFS with judgement, you tune with tune2fs and you recognise the bewilderment of a df -h with room and a df -i at 100%.

You mount with the options a server needs — noatime, nodev, nosuid, noexec, nofail —, you read /etc/fstab field by field and you know that mount -a before rebooting is the difference between a tweak and a lost night. You diagnose a full disk in its three variants, including the deleted file that lsof +L1 gives away. And you have mastered LVM: you create PVs, VGs and LVs, you grow them live with lvextend -r, you know why shrinking is dangerous and you use snapshots to freeze a consistent state. You know mdadm and its levels, and you have it engraved that RAID is not a backup.

Above all, /srv/tramontana/backups now lives on its own 15 GiB logical volume, expandable while in use, mounted by UUID with nofail, with the backups migrated byte by byte with rsync -aHAX and verified, and with 5 GiB of margin in the VG reserved for snapshots.

And now we reach the heart of the module. You have identities, permissions, packages and disk, but the Tramontana application is still started by hand: if the server reboots, it does not come back; if the process dies, nobody brings it up; deploy.sh changes the symbolic link and still restarts nothing; and the 04:20 backup depends on a cron line with no real overlap control and no decent logging. In systemd and Service Management all of that ends: you will write tramontana.service from scratch and hardened, you will understand the real difference between ordering and dependency, you will turn backup_tramontana.sh into a .service with its .timer and Persistent=true, and deploy.sh will finally do the systemctl restart it was missing.

Linux Course: From Beginner to System Administrator

Module 1: Introduction to Linux

Module 2: Basic Linux Commands

Module 3: Advanced Command-Line Skills

Module 4: Shell Scripting

Module 5: System Administration

Module 6: Networking and Security

Module 7: Advanced Topics

Module 8: Practical Projects

© Copyright 2026. All rights reserved