/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
- The storage stack, from top to bottom
- Identifying the hardware:
lsblk,blkid,fdisk -l - Partition tables: MBR versus GPT
- Partitioning with
fdiskandparted - Filesystems compared, and inodes revisited
- Mounting:
mount, options, bind mounts andfindmnt /etc/fstabfield by field and the safety net- Space:
df,du, and the deleted file that does not free it - Swap: partition, file and how much you need
- LVM: PV, VG, LV, growing live and snapshots
- Software RAID with
mdadm - Disk quotas
- Tramontana case: a new disk for the backups
- 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.
- Identifying the hardware:
lsblk, blkid, fdisk -l
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% /
sdblsblk -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.
- 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.
- Partitioning with
fdisk and parted
fdisk and partedfdisk 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 lvmThe 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.
- 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: 983040tune2fs 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/backups43% 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).
- Mounting:
mount, options, bind mounts and findmnt
mount, options, bind mounts and findmntsudo 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 releaseIf umount answers "target is busy", pick up lsof and fuser from 03-06:
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,nosuidfindmnt 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.
/etc/fstab field by field and the safety net
/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 NOWmount -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.
- Space:
df, du, and the deleted file that does not free it
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/tramontanancdu 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
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.
- 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 -2With 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.
- 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.00gNotice 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-deployTwo 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.
- Software RAID with
mdadm
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.
- 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.
- 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/newStep 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,noatimenosuid 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/backupsOnly 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
ENDCommon Mistakes and Tips
- Mounting by
/dev/sdb1infstab. 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. Andnofailon everything that is not/. lvextendwithout-r. The volume grows,dfdoes 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.
rmon a log that is in use. The space is not freed until the process closes the descriptor:lsof +L1gives it away. Logs are rotated (05-06).- Copying data with
cp -rinstead ofrsync -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,lvsand a copy of thefstabalongside theHISTORY. Reconstructing the disk layout from memory, with the server down, is not a plan.
Exercises
- Diagnosing a full disk.
df -h /shows 100%, butdu -sh /adds up to considerably less. List the three possible causes in order of likelihood and the command that confirms each one. - 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 todfand explain what would have happened without-r. - A safe
fstabentry. Write thefstabline 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 mountThe 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/backupsWithout -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.
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 optionsNote 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
- What Is Linux?
- History of Linux
- Linux Distributions
- Installing Linux
- First Contact with the System
- The Linux File System Structure
Module 2: Basic Linux Commands
- Introduction to the Command Line
- Getting Help and System Documentation
- Navigating the File System
- File and Directory Operations
- Viewing and Editing Files
- Hard and Symbolic Links
- File Permissions and Ownership
Module 3: Advanced Command-Line Skills
- The Shell Environment: Variables, Aliases and History
- Using Wildcards and Regular Expressions
- Searching Files and Content: find, locate and grep
- Pipes and Redirection
- Text Processing: cut, sort, uniq, sed and awk
- Process Management
- Scheduling Tasks with Cron
- Networking Commands
Module 4: Shell Scripting
- Introduction to Shell Scripting
- Variables and Data Types
- Script Input, Output and Arguments
- Control Structures
- Functions and Libraries
- Debugging and Error Handling
- Production Scripts: Best Practices
Module 5: System Administration
- User and Group Management
- sudo and Special Permissions
- Package Management
- Disk Management
- systemd and Service Management
- System Logs: journald and syslog
- System Monitoring and Performance Tuning
- Backup and Restore
Module 6: Networking and Security
- Network Configuration
- SSH and Remote Access
- Firewalls and Perimeter Security
- Intrusion Detection Systems
- Secrets Management and TLS Certificates
- Securing Linux Systems
Module 7: Advanced Topics
- The Boot Process and System Recovery
- Advanced Diagnostics: strace, perf and eBPF
- Linux Kernel Tuning
- Virtualization with Linux
- Linux Containers and Docker
- Automation with Ansible
- High Availability and Load Balancing
