Partitions (MBR & GPT)
MBR, GPT, and how disk space gets divided
What a Partition Actually Is
A block device is a flat array of LBAs — 0, 1, 2, …, N-1. A partition is nothing more than a named range of those LBAs: [start_lba, end_lba] plus a type code and optional label. The range is described by an entry in a partition table — a small metadata structure at a fixed, well-known location on the disk so that firmware, bootloaders, and the OS can find it without prior knowledge of what's on the drive.
Why Partition at All?
- Multiple filesystems on one device (e.g. ESP + root + swap)
- Multi-boot with different OSes
- Isolation — quota at the FS level, or separate encryption domains
- Firmware contract — UEFI looks for an ESP with a specific type GUID; without it the system won't boot
- Alignment — partitioning is where you pay (or forget to pay) the alignment cost
Note
Partitions are a pre-filesystem concept. LVM and ZFS pools provide a more flexible alternative (growable, spanning multiple devices), but nearly every system still starts life with a partition table because firmware requires it.
MBR — Master Boot Record
Introduced with the IBM PC in 1983. It is a 512-byte sector at LBA 0 that contains bootstrap code plus a tiny partition table.
MBR Layout (LBA 0)
Each 16-Byte Partition Entry
MBR Constraints
| Limit | Value | Why |
|---|---|---|
| Max primary partitions | 4 | Only 4 slots in the 64-byte table |
| Max disk size | ~2 TiB | Start LBA + count fields are 32-bit; 232 × 512 B = 2 TiB |
| Max partition size | ~2 TiB | Same 32-bit sector count |
| Integrity check | None | No CRC, no backup — a single stray write at LBA 0 wipes the table |
| Partition labels | None | Just a 1-byte type code |
The Extended/Logical Workaround
To break past 4 partitions, one of the primary slots is marked as an extended partition (type 0x05 or 0x0F). Inside it lives a chain of Extended Boot Records (EBRs), each describing one logical partition and pointing to the next EBR. Linux numbers these sda5, sda6, … (primaries stay 1–4, logicals start at 5).
Primary 1
sda1
Primary 2
sda2
Extended
sda4 — container
EBR → Logical
sda5
EBR → Logical
sda6 …
Warning
Don't use MBR on anything new. Every constraint above is solved by GPT, and every modern partitioning tool defaults to GPT. MBR knowledge is needed only for legacy systems, embedded devices, and reading old disks.
GPT — GUID Partition Table
Defined in the UEFI specification. Fixes every MBR limitation: 64-bit LBAs, up to 128 partitions by default (up to ~218 in theory), CRC-checked, with a full backup at the end of the disk.
GPT Disk Layout
GPT Header Fields (highlights)
| Offset | Field | Purpose |
|---|---|---|
| 0 | Signature "EFI PART" | Magic bytes identifying a GPT header |
| 8 | Revision | Usually 00 00 01 00 (1.0) |
| 16 | Header CRC32 | Validates the 92-byte header |
| 24 | Current LBA / Backup LBA | Self-location and pointer to the other copy |
| 40 | First/Last usable LBA | Range available for partition data |
| 56 | Disk GUID | Unique identifier for the entire disk |
| 72 | Partition entries LBA | Where the entry array lives |
| 80 | Number of entries / entry size | Default 128 × 128 B |
| 88 | Partition array CRC32 | Validates the entry table |
Tip
Why a protective MBR? Legacy tools that don't understand GPT see a fully-allocated MBR covering the whole disk with an unknown type (0xEE). They refuse to touch it rather than helpfully "fixing" it by deleting what they can't parse.
Common Partition Type GUIDs
| Type | GUID | Notes |
|---|---|---|
| Linux filesystem | 0FC63DAF-8483-4772-8E79-3D69D8477DE4 | Generic Linux data. ext4, xfs, btrfs all use this |
| Linux swap | 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F | |
| Linux LUKS | CA7D7CCB-63ED-4C53-861C-1742536059CC | Encrypted container |
| Linux LVM | E6D6D379-F507-44C2-A23C-238F2A3DF928 | Physical volume |
| Linux RAID | A19D880F-05FC-4D3B-A006-743F0F84911E | mdraid member |
| EFI System Partition | C12A7328-F81F-11D2-BA4B-00A0C93EC93B | Mandatory on UEFI boot disks |
| BIOS boot partition | 21686148-6449-6E6F-744E-656564454649 | Tiny (~1 MiB) slot for GRUB on BIOS+GPT |
| Microsoft reserved (MSR) | E3C9E316-0B5C-4DB8-817D-F92DF00215AE | Windows metadata |
| Microsoft basic data | EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 | NTFS, FAT, exFAT |
| Linux root (x86-64) | 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709 | Discoverable Partitions Spec — systemd auto-mounts based on type |
| Linux /home | 933AC7E1-2EB4-4F13-B844-0E14E2AEF915 | Same DPS |
Note
The Discoverable Partitions Specification (used by systemd) lets you boot without writing a /etc/fstab: if your root partition has the right type GUID for your architecture, systemd-gpt-auto-generator mounts it automatically.
Each Partition Entry (128 B)
MBR vs GPT Side by Side
- 1983, BIOS era
- Single 512 B sector at LBA 0
- 4 primary + extended/logical chain
- 32-bit LBAs → 2 TiB disk max
- No integrity check
- No backup
- 1-byte type code
- No partition names
- Works with legacy BIOS bootloaders
- 2000, UEFI standard
- Header + 128-entry table, with backup at disk end
- 128 partitions by default (expandable)
- 64-bit LBAs → 8 ZiB disk max
- CRC32 on header and entries
- Primary + backup at disk end
- 16-byte type GUID
- 36-char UTF-16 names
- Required by UEFI; works with BIOS via BIOS boot partition
1 MiB Alignment
Modern tools default the first partition to LBA 2048 — exactly 1 MiB into the disk (2048 × 512 B). Each subsequent partition boundary also lands on a 1 MiB multiple. This is not decoration; it is a correctness property.
What 1 MiB Aligns To
Physical sector
4 KiB drives: 1 MiB = 256 physical sectors. No RMW penalty on the first partition.
NAND page / erase block
NAND pages are 4–16 KiB; erase blocks up to 1 MiB. A partition that begins on a 1 MiB boundary cannot cause partial-erase-block writes at the device level.
RAID stripe
Typical stripe widths (64, 128, 256, 512 KiB, 1 MiB) all divide 1 MiB. A 1 MiB-aligned partition won't straddle stripe boundaries for small I/O.
Warning
Starting partitions below 1 MiB (e.g. LBA 34 — the first legal GPT LBA) works and wastes no space, but misaligns the partition against every modern NAND geometry and every RAID stripe you'll ever put underneath. Don't do it.
Partitioning Tools
| Tool | Mode | Scope | Best for |
|---|---|---|---|
fdisk | Interactive (util-linux) | MBR + GPT (since 2.26) | Quick edits on a single disk; familiar for MBR users |
parted | Interactive + scriptable | MBR + GPT, many others | Scripts, resize operations, consistent CLI across label types |
sgdisk | Non-interactive CLI | GPT only | Automation, cloud-init, Ansible. Every op in one command. |
gdisk | Interactive | GPT only | GPT-focused interactive editing; fdisk-style menu |
cfdisk | ncurses TUI | MBR + GPT | Visual editing on a serial console / rescue shell |
blkdiscard | CLI | Device-wide | Not partitioning — erases all data; use before repartitioning SSDs |
wipefs | CLI | Signature level | Removes filesystem / partition table signatures without writing a new one |
fdisk — Quick Inspection
# fdisk -l /dev/nvme0n1
Disk /dev/nvme0n1: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
Disk model: Samsung SSD 980 PRO 2TB
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: 1D3F2A01-BB55-4E2C-8F1E-9D27AEF5B3E1
Device Start End Sectors Size Type
/dev/nvme0n1p1 2048 1050623 1048576 512M EFI System
/dev/nvme0n1p2 1050624 3147775 2097152 1G Linux filesystem
/dev/nvme0n1p3 3147776 3907028991 3903881216 1.8T Linux LUKS
parted — Scriptable and Resize-Capable
# parted /dev/nvme0n1 print
Model: Samsung SSD 980 PRO 2TB (nvme)
Disk /dev/nvme0n1: 2000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Number Start End Size File system Name Flags
1 1049kB 538MB 537MB fat32 EFI boot, esp
2 538MB 1612MB 1074MB ext4 boot
3 1612MB 2000GB 1999GB
# parted -s /dev/sdb mklabel gpt \
mkpart ESP fat32 1MiB 513MiB set 1 esp on \
mkpart root ext4 513MiB 100%
sgdisk — Automation Friend
# sgdisk -p /dev/nvme0n1
Disk /dev/nvme0n1: 3907029168 sectors, 1.8 TiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 1D3F2A01-BB55-4E2C-8F1E-9D27AEF5B3E1
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 3907029134
Partitions will be aligned on 2048-sector boundaries
Total free space is 0 sectors (0 bytes)
Number Start (sector) End (sector) Size Code Name
1 2048 1050623 512.0 MiB EF00 EFI System
2 1050624 3147775 1024.0 MiB 8300 boot
3 3147776 3907028991 1.8 TiB 8309 crypt
# One-liner to build a fresh layout:
# sgdisk --zap-all /dev/sdb
# sgdisk -n 1:0:+512M -t 1:ef00 -c 1:"EFI" \
-n 2:0:+1G -t 2:8300 -c 2:"boot" \
-n 3:0:0 -t 3:8309 -c 3:"crypt" /dev/sdb
Tip
sgdisk code shortcuts: ef00 = ESP, 8300 = Linux FS, 8309 = LUKS, 8e00 = LVM, fd00 = RAID, 8200 = swap. These are sgdisk's 4-hex-digit aliases for the full type GUIDs.
The EFI System Partition (ESP)
Why It Exists
UEFI firmware does not know how to read ext4, xfs, btrfs, or any other "real" filesystem. It speaks only FAT32 (and sometimes FAT12/FAT16). The ESP is a dedicated FAT32 partition on which the firmware can find and execute UEFI applications — bootloaders, firmware updaters, memory testers.
| Property | Value |
|---|---|
| Filesystem | FAT32 (vfat) |
| Type GUID | C12A7328-F81F-11D2-BA4B-00A0C93EC93B |
| Typical size | 256 MiB – 1 GiB (512 MiB is a safe default) |
| Typical mountpoint | /boot/efi (Debian/Ubuntu), /efi (Fedora with systemd-boot), or /boot (unified kernel image setups) |
| Required entries | EFI/BOOT/BOOTX64.EFI (removable media fallback) or vendor dirs like EFI/ubuntu/, EFI/fedora/, EFI/systemd/ |
Typical ESP Contents
/boot/efi/
└── EFI/
├── BOOT/
│ └── BOOTX64.EFI # fallback path, tried by firmware if NVRAM is empty
├── ubuntu/
│ ├── shimx64.efi # signed shim — speaks to Secure Boot
│ ├── grubx64.efi # GRUB EFI binary
│ ├── mmx64.efi # MokManager (signing key mgmt)
│ └── grub.cfg
└── systemd/
└── systemd-bootx64.efi # alternative: systemd-boot, simpler than GRUB
Warning
On UEFI systems, forgetting the ESP means the system will not boot — not at install time, not after a wipe, not after a GPT restore. If you restore a GPT from backup onto a fresh disk with no data, you also need to repopulate the ESP and the firmware's NVRAM boot entries (efibootmgr --create …).
Loop Devices — Files as Block Devices
A loop device binds a regular file to a /dev/loopN node so the kernel treats it as a block device. This is how disk images (.img, .iso, qcow2 when combined with qemu-nbd) are mounted, and how utilities like losetup, snap, or cloud-init handle image-based storage.
# dd if=/dev/zero of=/tmp/disk.img bs=1M count=1024
# losetup -f --show /tmp/disk.img
/dev/loop0
# sgdisk -n 1:0:+256M -t 1:ef00 -n 2:0:0 -t 2:8300 /dev/loop0
# partprobe /dev/loop0 # or: losetup -P (partition scanning)
# ls /dev/loop0*
/dev/loop0 /dev/loop0p1 /dev/loop0p2
# mkfs.ext4 /dev/loop0p2
# mount /dev/loop0p2 /mnt
# ... use it ...
# umount /mnt && losetup -d /dev/loop0
Tip
losetup -P tells the kernel to scan the loop device for a partition table and expose each partition as /dev/loopNpM. Without -P (or without a subsequent partprobe), only the whole-disk node exists and the partitions are invisible to userspace.
Telling the Kernel to Re-read the Table
When you change the partition table on a disk that is in use, the kernel keeps the old in-memory view because rewriting it would invalidate mounts. You need to explicitly ask it to re-scan:
| Command | What it does |
|---|---|
partprobe /dev/sdX | Ask the kernel to re-read the partition table. From parted. Simple and common. |
partx -u /dev/sdX | Update (add/remove) partitions from an existing table without a full reload. Safer on mounted disks. |
partx -a /dev/sdX | Add newly-defined partitions. |
partx -d /dev/sdX | Delete partition device nodes. |
blockdev --rereadpt /dev/sdX | Force BLKRRPART ioctl. Fails if any partition on the device is mounted. |
kpartx -av image.img | Create /dev/mapper/loopNpM for a loop-backed image (uses device-mapper instead of loop partitions). |
Warning
Resize with mounted root: when you grow the root partition on a running system, partprobe often fails with EBUSY. Use partx -u /dev/sdX (updates entries in-place) or reboot. Afterwards, resize2fs /dev/sdXN or xfs_growfs / grows the live filesystem.
GPT Backup and Restore
GPT has a native backup (the table at the end of disk), but if the whole disk is dying or you want off-device safety, sgdisk can export the entire table to a file.
# Back up the table
# sgdisk --backup=/root/nvme0n1.gpt.bak /dev/nvme0n1
# Verify
# sgdisk --verify /dev/nvme0n1
# Restore (wipes existing table — both primary and backup!)
# sgdisk --load-backup=/root/nvme0n1.gpt.bak /dev/nvme0n1
# If only the primary header is corrupt, regenerate from backup:
# sgdisk --move-second-header /dev/nvme0n1
# gdisk /dev/nvme0n1 # interactive 'r' recovery menu
Note
What a backup file contains: disk GUID, all 128 partition entries (type GUID, unique GUID, first/last LBA, attributes, name), and header checksums. It does not contain partition contents — if your data is gone, so is your data. Backups are for re-deriving a valid partition layout after accidental deletion.
Example: Create a UEFI-Boot Linux Layout
- Wipe any existing signatures so the kernel doesn't confuse leftover filesystems with the new layout.
console # wipefs -a /dev/sdb # sgdisk --zap-all /dev/sdb - Create ESP + boot + root, all 1 MiB-aligned.
console # sgdisk \ -n 1:0:+512M -t 1:ef00 -c 1:"EFI" \ -n 2:0:+1G -t 2:8300 -c 2:"boot" \ -n 3:0:0 -t 3:8309 -c 3:"crypt" \ /dev/sdb - Ask the kernel to see the new partitions.
console # partprobe /dev/sdb - Make filesystems / crypto layers.
console # mkfs.vfat -F32 -n EFI /dev/sdb1 # mkfs.ext4 -L boot /dev/sdb2 # cryptsetup luksFormat /dev/sdb3 # cryptsetup open /dev/sdb3 cryptroot # mkfs.ext4 -L root /dev/mapper/cryptroot - Mount and populate, then install a bootloader (GRUB or systemd-boot) to
/boot/efiand register it withefibootmgr. This is OS installer territory.
Common Pitfalls
Warning
Mixing MBR and GPT tools. Old fdisk versions (< 2.26) cannot read GPT and will offer to write an MBR over the top, destroying the GPT header. If the backup GPT header at disk end is intact you may recover with gdisk, but it's avoidable — use modern tools.
Warning
Starting partitions at LBA 34. This is the first legal GPT sector, but not the first sensible one. Start at 2048 (1 MiB) to align with NAND and RAID geometries.
Warning
Forgetting the ESP on UEFI. Without a FAT32 partition flagged esp/boot, UEFI firmware cannot find a bootloader and the system drops to the firmware shell.
Warning
Forgetting the BIOS boot partition on BIOS+GPT. If you use GPT on a BIOS-booted machine, GRUB needs a small (~1 MiB) unformatted partition with type GUID 2168…EFI to embed its core image. Without it, grub-install fails.
Warning
Resizing a partition that's actively mounted read-write. Growing works on recent kernels (with partx -u), but shrinking requires the filesystem to be unmounted first (or at least frozen). Always back up before resize operations.
Warning
Cloning a disk with dd and not regenerating GUIDs. Two disks with the same partition GUIDs confuse udev's /dev/disk/by-partuuid/ and can cause the wrong disk to be mounted. Use sgdisk -G /dev/sdb to randomize GUIDs after a clone.
Warning
Writing LUKS or LVM directly on /dev/sdb instead of on a partition is valid, and avoids alignment concerns, but leaves no room for an ESP or swap partition on that device. On a boot disk this means you can't boot from it; fine for a dedicated data disk.