Skip to content
Menu

Linux Storage13 min read

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)

Bootstrap code 446 bytes — first-stage bootloader
Part 1 16 B
Part 2 16 B
Part 3 16 B
Part 4 16 B
55AA 2 B — boot signature

Each 16-Byte Partition Entry

Boot flag 1 B — 0x80 = active
CHS first 3 B — legacy Cylinder/Head/Sector
Type 1 B — 0x83 Linux, 0x05/0x0F ext, 0xEE GPT
CHS last 3 B — legacy end
Start LBA 4 B (32-bit)
Num sectors 4 B (32-bit)

MBR Constraints

LimitValueWhy
Max primary partitions4Only 4 slots in the 64-byte table
Max disk size~2 TiBStart LBA + count fields are 32-bit; 232 × 512 B = 2 TiB
Max partition size~2 TiBSame 32-bit sector count
Integrity checkNoneNo CRC, no backup — a single stray write at LBA 0 wipes the table
Partition labelsNoneJust 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).

  1. Primary 1

    sda1

  2. Primary 2

    sda2

  3. Extended

    sda4 — container

  4. EBR → Logical

    sda5

  5. 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

LBA 0 Protective MBR 512 B One entry type 0xEE spanning the whole disk — hides GPT from legacy tools
LBA 1 Primary GPT Header 512 B Disk GUID, pointer to entries, CRC32 of self + entries
LBA 2–33 Primary Partition Entries 16 KiB 128 entries × 128 B each (default). Type GUID, unique GUID, name
LBA 34+ Usable Area Actual partition data lives here — first partition typically starts at LBA 2048 (1 MiB)
N-33…N-1 Backup Partition Entries 16 KiB Exact copy of primary entries
LBA N Backup GPT Header 512 B Backup of primary; pointers are reversed. Used for recovery

GPT Header Fields (highlights)

OffsetFieldPurpose
0Signature "EFI PART"Magic bytes identifying a GPT header
8RevisionUsually 00 00 01 00 (1.0)
16Header CRC32Validates the 92-byte header
24Current LBA / Backup LBASelf-location and pointer to the other copy
40First/Last usable LBARange available for partition data
56Disk GUIDUnique identifier for the entire disk
72Partition entries LBAWhere the entry array lives
80Number of entries / entry sizeDefault 128 × 128 B
88Partition array CRC32Validates 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

TypeGUIDNotes
Linux filesystem0FC63DAF-8483-4772-8E79-3D69D8477DE4Generic Linux data. ext4, xfs, btrfs all use this
Linux swap0657FD6D-A4AB-43C4-84E5-0933C84B4F4F
Linux LUKSCA7D7CCB-63ED-4C53-861C-1742536059CCEncrypted container
Linux LVME6D6D379-F507-44C2-A23C-238F2A3DF928Physical volume
Linux RAIDA19D880F-05FC-4D3B-A006-743F0F84911Emdraid member
EFI System PartitionC12A7328-F81F-11D2-BA4B-00A0C93EC93BMandatory on UEFI boot disks
BIOS boot partition21686148-6449-6E6F-744E-656564454649Tiny (~1 MiB) slot for GRUB on BIOS+GPT
Microsoft reserved (MSR)E3C9E316-0B5C-4DB8-817D-F92DF00215AEWindows metadata
Microsoft basic dataEBD0A0A2-B9E5-4433-87C0-68B6B72699C7NTFS, FAT, exFAT
Linux root (x86-64)4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709Discoverable Partitions Spec — systemd auto-mounts based on type
Linux /home933AC7E1-2EB4-4F13-B844-0E14E2AEF915Same 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)

Partition type GUID 16 B
Unique partition GUID 16 B
First LBA 8 B (64-bit)
Last LBA 8 B (64-bit)
Attributes 8 B — bit 0 required, bit 2 legacy BIOS bootable, bit 60 read-only…
Name (UTF-16LE) 72 B — 36 characters, human-readable label

MBR vs GPT Side by Side

MBR
  • 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
GPT
  • 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

ToolModeScopeBest for
fdiskInteractive (util-linux)MBR + GPT (since 2.26)Quick edits on a single disk; familiar for MBR users
partedInteractive + scriptableMBR + GPT, many othersScripts, resize operations, consistent CLI across label types
sgdiskNon-interactive CLIGPT onlyAutomation, cloud-init, Ansible. Every op in one command.
gdiskInteractiveGPT onlyGPT-focused interactive editing; fdisk-style menu
cfdiskncurses TUIMBR + GPTVisual editing on a serial console / rescue shell
blkdiscardCLIDevice-wideNot partitioning — erases all data; use before repartitioning SSDs
wipefsCLISignature levelRemoves filesystem / partition table signatures without writing a new one

fdisk — Quick Inspection

console
console

    # 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

console
console

    # 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

console
console

    # 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.

PropertyValue
FilesystemFAT32 (vfat)
Type GUIDC12A7328-F81F-11D2-BA4B-00A0C93EC93B
Typical size256 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 entriesEFI/BOOT/BOOTX64.EFI (removable media fallback) or vendor dirs like EFI/ubuntu/, EFI/fedora/, EFI/systemd/

Typical ESP Contents

text
bash

/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.

console
console

    # 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:

CommandWhat it does
partprobe /dev/sdXAsk the kernel to re-read the partition table. From parted. Simple and common.
partx -u /dev/sdXUpdate (add/remove) partitions from an existing table without a full reload. Safer on mounted disks.
partx -a /dev/sdXAdd newly-defined partitions.
partx -d /dev/sdXDelete partition device nodes.
blockdev --rereadpt /dev/sdXForce BLKRRPART ioctl. Fails if any partition on the device is mounted.
kpartx -av image.imgCreate /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.

console
console

    # 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

  1. 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
  2. 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
  3. Ask the kernel to see the new partitions.
    console
    # partprobe /dev/sdb
  4. 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
  5. Mount and populate, then install a bootloader (GRUB or systemd-boot) to /boot/efi and register it with efibootmgr. 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.

Solidnines — solidnines.com