GPT vs MBR partition tables on a server
Why servers use GPT, what the protective MBR in sector zero is for, how many partitions you get, and how to fix the backup header after a volume resize.
GPT vs MBR partition tables: the short answer
On a server the GPT vs MBR partition table question has one answer: use GPT. An MBR table cannot describe anything past 2 TiB on a disk with 512-byte sectors, so a 4 TB volume carrying an MBR label loses more than half of itself before you write a single file. GPT stores 64-bit sector addresses. It also protects its table with two checksums and keeps a full copy of that table at the far end of the device.
MBR only earns a place when something outside the disk demands it, such as firmware that refuses to boot from GPT. A data volume attached to a running VPS makes no such demand, so the decision costs one command. What follows is what each layout actually writes to the disk, because that is what makes the warnings readable, in particular the one that appears after a provider grows a volume.
What a partition table actually is
A partition table is a short list of records stored in the first sectors of a block device. Each record says where a range of the device starts, how long it is, and what kind of content it holds. When the device appears, the kernel reads that list and creates one child device per record, so /dev/sdb gains /dev/sdb1. The table describes ranges and nothing else. It holds none of your data, and writing a new one does not move a byte inside any partition.
That distinction matters because rented storage stacks four sizes on top of each other: the block device the provider exposes, the partition table written on it, the partition described by that table, and the filesystem inside the partition. df reports only the last one. A volume that grew at the provider keeps showing its old size in df until every layer above the device has been told, which is the resize case further down this page. If you are still choosing a number, work out how much storage the workload really needs before partitioning, because growing a volume is routine and shrinking one is not.
The sizes stop matching as soon as you read them, too. A 4 TB volume appears as roughly 3.6 TiB in lsblk, for the same reason a 500 GB disk shows as 465 GiB: the vendor counts in powers of ten and the kernel prints powers of two. Nothing is lost there, and no partition table can get it back.
MBR: four slots and a 2 TiB ceiling
MBR stands for master boot record. It lives in sector 0, the first 512 bytes of the device. The first 446 bytes hold boot code. The next 64 bytes hold four partition records of 16 bytes each. The final two bytes are a fixed signature that marks the sector as a boot record.
Each 16-byte record stores the starting sector of a partition and its length as 32-bit numbers, and that single fact is the ceiling. 2^32 sectors of 512 bytes is 2 TiB, so an MBR table cannot describe a partition that starts or ends beyond that point. A disk with 4096-byte sectors reaches 16 TiB using the same fields, but rented volumes normally present 512-byte logical sectors, so 2 TiB is the number you will meet.
The four slots are the second limit, and the old workaround is the extended partition. One of the four records points at a chain of further records stored inside the disk itself, and each link in that chain describes one logical partition. Linux still reads it correctly. It does not raise the 2 TiB ceiling, and a linked chain carries no checksum anywhere in it.
GPT: a header, an entry array, and a copy at the far end
GPT stands for GUID partition table, and GUID means globally unique identifier: the 128-bit values it uses in place of the single type byte MBR has. On a device with 512-byte sectors the layout is fixed, and it is worth knowing, because most confusing partition warnings name one of these locations. LBA below means logical block address, the sector number counted from zero.
- LBA 0 holds the protective MBR, which the next section covers.
- LBA 1 holds the primary header: a signature, its own address, the address of the backup header, the first and last usable LBA, the disk GUID, the number of partition entries, the size of one entry, and two CRC32 checksums.
- LBA 2 through 33 hold the partition entry array, 128 entries of 128 bytes each.
- LBA 34 is the first sector a partition is allowed to use.
- The last LBA of the device holds the backup header, and the 32 sectors before it hold the second copy of the entry array.
Each entry carries a type GUID, a GUID unique to that one partition, the first and last LBA of the partition, attribute flags, and a name of up to 36 characters. The addresses are 64-bit, which puts the ceiling of the format at 8 ZiB with 512-byte sectors. No storage you can rent comes close to it.
The two checksums are why the copy at the end exists. One covers the header, the other covers the entry array. A tool reading a header whose checksum does not match knows the table is damaged instead of guessing, so it can read the copy at the other end of the device instead. That redundancy only works while both copies sit where the header says they sit, and a volume resize is exactly what breaks that.
Why sector zero still holds a protective MBR
A GPT disk carries a valid MBR in sector 0 holding exactly one partition record. That record has type 0xEE, starts at sector 1, and covers the rest of the device. On a device larger than 2 TiB the length field is written as the largest value it can hold, because it cannot express the real size. This is the protective MBR. It boots nothing and it points at no filesystem.
Its only job is to make the device look occupied to software that understands MBR and nothing else. Such a tool sees one partition of a type it cannot name, filling the whole device, so it treats the disk as in use. Without that record the same tool would see an unpartitioned disk, offer to initialise it, and write a fresh MBR over the GPT header and entry array at the start of the device.
This is why an old utility describes a multi-terabyte volume as a single unrecognised partition. fdisk builds from before it learned GPT do this, and so do some storage appliance and firmware menus. The disk is correct and the tool is old. Never accept an offer to repair or initialise a device from a tool in that state. Read it with something that understands GPT instead, starting with lsblk -o NAME,SIZE,TYPE,PTTYPE on the server itself.
How many partitions can a GPT disk hold?
The data behind this chart
[
{
"label": "MBR records in sector 0",
"entries": 4,
"table_bytes": "64"
},
{
"label": "GPT default array",
"entries": 128,
"table_bytes": "16,384"
},
{
"label": "GPT array resized to 256",
"entries": 256,
"table_bytes": "32,768"
}
]The MBR number is a law of the layout. Sector 0 has room for 4 records in 64 bytes, and nothing can add a fifth.
The GPT number is not a law. The header records how many entries the array holds and how large each entry is, and tools read those two fields rather than assuming them. Every common tool creates the array with 128 entries of 128 bytes, which is 16,384 bytes, or exactly 32 sectors, and that is where the familiar 128 comes from. It is a table size chosen when the table is created. The expert menu in gdisk can write a larger array, and a header declaring 256 entries is read correctly by the kernel and by current tools.
Leave it at the default anyway. A larger array pushes the first usable sector further into the disk and takes you outside what firmware and older tools expect. The practical answer for a rented data volume is far smaller than 128: one partition, or none at all.
Write a GPT table on a new volume
Confirm the device before you touch it. Names are handed out in the order the kernel finds disks, so today's /dev/sdb can be /dev/sdc after a reboot or after you attach a second volume. Match the size you rented, and check that the PTTYPE column is empty.
lsblk -o NAME,SIZE,TYPE,PTTYPE,MOUNTPOINTS
sudo apt update && sudo apt install -y gdisk partedThe package names are the same on Fedora and Enterprise Linux, though the installer is not, so reach for the dnf equivalent of these apt commands there. If you also want to know what kind of storage sits behind the device node, check whether the disk is really NVMe before you trust any speed claim made about it.
The next three commands destroy whatever the device already holds. Run them only against a volume you have just attached and confirmed empty.
sudo parted -s /dev/sdb mklabel gpt
sudo parted -s -a optimal /dev/sdb mkpart data 0% 100%
sudo mkfs.ext4 -L data /dev/sdb1mklabel gpt writes the protective MBR, the primary header, the entry array, and the backup structures at the end, in one step. Writing 0% and 100% instead of sector numbers lets parted align the partition for you, which puts the start at 1 MiB on a normal volume. On GPT the first argument to mkpart is the partition name, not the word primary, because GPT has no primary or extended distinction to make.
Check the result before you mount anything:
lsblk -o NAME,SIZE,TYPE,PTTYPE
sudo sgdisk -v /dev/sdblsblk should show gpt in the PTTYPE column, and one partition whose size is within a couple of MiB of the whole device. sgdisk -v reads both copies of the table, recomputes the checksums, and says plainly when it finds nothing wrong. Then take the UUID from sudo blkid /dev/sdb1 and mount by UUID in /etc/fstab, so a device letter that moves at boot does not leave the mount missing. The full attach and mount procedure for a block storage volume covers the fstab entry and the mount options.
You can also skip the partition table on a pure data volume and run sudo mkfs.ext4 -L data /dev/sdb against the whole device. That is legal, it is common for volumes that hold exactly one thing, and it makes a later resize one step shorter because there is no partition to grow. The cost is that the device then has no partition table at all, so a tool that only reads partition tables calls it unpartitioned, and only tools that look for filesystem signatures, such as lsblk -f and blkid, show what is really on it. Pick one convention for your fleet and write it down.
The resize case: after your provider grows the volume
This is the failure that only happens on rented storage. You raise the size in the provider's panel, the block device becomes larger, and nothing above the device changes on its own.
Two of the addresses in the primary header are measured from the end of the device: the location of the backup header, and the last usable LBA. Both were written when the disk was smaller. After the volume grows, the backup header is no longer in the last sector, it is stranded somewhere in the middle, and the primary header still claims the device ends where it used to. Tools that read the table report this: parted says the backup table is not at the end of the device and offers to move it, and sgdisk -v counts the same mismatch as a problem. Nothing is corrupt. The table is describing a device that has since changed size, and until it is fixed the new space lies past the last usable sector, so no partition can be extended into it.
The repair is a fixed order, bottom layer upwards. Skip a step and df keeps showing the old number.
lsblk -o NAME,SIZE,TYPE,PTTYPE
sudo sgdisk -e /dev/sdb
sudo growpart /dev/sdb 1
sudo partx -u /dev/sdb
sudo resize2fs /dev/sdb1lsblk comes first because nothing else works until the kernel itself sees the new size on the disk line. If it still shows the old size, echo 1 | sudo tee /sys/class/block/sdb/device/rescan asks a SCSI-attached device to look again. Virtio and NVMe devices normally pick the change up on their own, and a reboot always works.
sgdisk -e is the command the warning is asking for. It moves the backup header and the backup copy of the entry array to the true end of the device, and it rewrites the primary header so the last usable LBA matches the real size. sudo sfdisk --relocate gpt-bak-std /dev/sdb does the same job from util-linux rather than gdisk, on version 2.37 and later, which covers Ubuntu 24.04 and Debian 12. Answering yes to the offer parted makes when it prints the table performs the same relocation. Current growpart handles a stranded backup header on its own, but running the relocate first is safe and it separates the two changes, so you can watch each one land.
growpart /dev/sdb 1 rewrites the partition entry so partition 1 ends at the new last usable sector. The device and the partition number are separate arguments, which is the usual mistake. It comes from the cloud-guest-utils package (sudo apt install -y cloud-guest-utils), and it extends a partition only when the free space follows it directly, so this works for the last partition on the disk. sudo parted /dev/sdb resizepart 1 100% does the same thing by hand.
partx -u tells the kernel to update the size of a partition it already knows about, and it works while the filesystem is mounted. sudo partprobe /dev/sdb re-reads the whole table instead, which the kernel may refuse while any partition on that device is in use.
resize2fs /dev/sdb1 grows ext4 while it is mounted. On XFS the command is sudo xfs_growfs /mnt/data, which takes the mount point rather than the device and also runs online. Neither can shrink. Confirm the result with df -h /mnt/data, then run sudo sgdisk -v /dev/sdb once more: the mismatch is gone once the backup sits where the header says it does.
More layers mean more grow commands. A partition used as a software RAID member or as an LVM physical volume needs mdadm --grow or pvresize and lvextend between the partition and the filesystem, and growing one member does not grow the array by itself, for the reasons in how RAID 10 lays out its members.
Converting an existing MBR volume to GPT
sudo sgdisk -g /dev/sdb converts a table in place. It reads the MBR records and writes GPT structures around them, leaving every partition where it is. Two conditions must hold. There must be room at the start of the device for the header and the entry array, which needs the first partition to begin at sector 34 or later, and anything aligned to 1 MiB satisfies that easily. There must also be 33 free sectors at the end for the backup, which a partition running to the very last sector does not leave.
Back up the table first with sudo sgdisk --backup=/root/sdb-table.bin /dev/sdb, and restore it with sudo sgdisk --load-backup=/root/sdb-table.bin /dev/sdb. That file holds the table only, so it is tiny, and it is worth nothing if you keep it on the disk it describes.
Boot disks are a different question. Converting the disk a machine boots from changes what the firmware has to find: a BIOS system booting from GPT needs a small BIOS boot partition for the bootloader's core image, and a UEFI system needs an EFI system partition formatted FAT32. On a VPS the root disk comes from the provider's image and the firmware is theirs, so convert data volumes and leave the boot disk alone unless you know which firmware the instance uses and have console access for the case where it does not come back.
FAQ
Should a new VPS volume use GPT or MBR?
GPT, at any size. MBR cannot address past 2 TiB with 512-byte sectors, and it offers four partition records where GPT offers 128 by default. sudo parted -s /dev/sdb mklabel gpt is the entire decision. Keep MBR only when firmware you do not control refuses to boot from GPT, which is not a situation an attached data volume can create.
Why does an old tool show my GPT volume as one unknown partition?
That is the protective MBR in sector 0 doing its job. It holds one record of type 0xEE covering the whole device, so software that understands only MBR sees the disk as occupied rather than empty. A tool that does not understand GPT cannot name that type, so it shows a single partition it cannot identify. Do not accept its offer to initialise or repair the device, because that writes an ordinary MBR over the GPT header and entry array. Read the device with lsblk -o NAME,SIZE,TYPE,PTTYPE or sudo sgdisk -p /dev/sdb instead.
My provider grew the volume. Why is the GPT backup table now wrong?
The primary header records the address of the backup header and the last usable sector, and both were written for the old size of the device. Once the device is larger, the backup copy is no longer in the last sector and the header understates where the disk ends, so tools report that the backup table is not at the end. Nothing is damaged. sudo sgdisk -e /dev/sdb moves the backup structures to the real end and updates the primary header, and sudo sfdisk --relocate gpt-bak-std /dev/sdb does the same from util-linux.
The volume is bigger but df still shows the old size. What is left to do?
Each layer carries its own size and each is grown separately. Confirm lsblk shows the disk at its new size, relocate the GPT backup with sudo sgdisk -e /dev/sdb, grow the partition with sudo growpart /dev/sdb 1, refresh the kernel's view with sudo partx -u /dev/sdb, then grow the filesystem with sudo resize2fs /dev/sdb1 for ext4 or sudo xfs_growfs /mnt/data for XFS. df changes only at that last step.
How many partitions does GPT really allow?
The default array holds 128 entries because tools reserve 32 sectors for it, and the header stores that count, so it is a table size chosen when the table is written rather than a fixed rule of the format. The expert menu in gdisk can write a larger array. There is rarely a reason to: a larger array moves the first usable sector further into the disk, and a rented data volume needs one partition, or none.