﻿# Proxmox VE Installer — Disk Partitioning Scheme

Reference documentation extracted from the `pve-installer` Rust/Perl codebase.
Describes the exact, hardcoded partitioning layout generated during an automated bare-metal PVE installation.

**Source files:**
- `Proxmox/Sys/Block.pm` — `partition_bootable_disk()` — GPT partition table creation
- `Proxmox/Install.pm` — `create_lvm_volumes()`, `compute_swapsize()`, `zfs_create_rpool()`, `btrfs_create()`
- `proxmox-installer-common/src/options.rs` — Rust-side option structs and defaults

---

## 1. GPT Partition Table Layout

**Function:** `partition_bootable_disk($target_dev, $maxhdsizegb, $ptype)` in `Block.pm:218`

Every disk receives the same 3-partition GPT layout via `sgdisk`:

| # | Name            | Start      | Size                        | Type Code | Purpose                                                        |
|---|-----------------|------------|-----------------------------|-----------|----------------------------------------------------------------|
| 1 | BIOS Boot       | Sector 34  | ~1 MB (sectors 34–2047)     | `EF02`    | GRUB stage2 (legacy BIOS). **Only created if block size ≠ 4096** |
| 2 | EFI System (ESP)| 1 MiB      | 512 MB or 1024 MB           | `EF00`    | EFI boot, formatted FAT32                                      |
| 3 | OS/Data         | End of ESP | Rest of disk (or capped)    | Varies    | Main partition                                                 |

### Partition 3 type code by filesystem

| Filesystem | Type Code | Meaning              |
|------------|-----------|----------------------|
| ext4 / XFS | `8E00`   | Linux LVM            |
| ZFS        | `BF01`   | Solaris /usr & ZFS   |
| Btrfs      | `8300`   | Linux filesystem     |

### ESP size formula

```perl
# Block.pm:229 — $hdsize is in KB
my $esp_size = $hdsize > 100 * 1024 * 1024 ? 1024 : 512; # in MB
```

- Disk ≤ 100 GB → **512 MB** ESP
- Disk > 100 GB → **1024 MB** ESP (allows multiple kernels/UKIs)

### Disk size limits

- **Hard limit:** 2 GB minimum (installation aborts)
- **Soft limit:** 8 GB minimum (warning prompt, user can override)

### Exact sgdisk commands

```bash
sgdisk -Z /dev/sdX                                                        # zap all GPT/MBR
sgdisk -n2:1M:+${esp_size}M -t2:EF00 \
       -n3:${esp_end}M:${restricted_hdsize_mb} -t3:$ptype /dev/sdX        # ESP + OS partition
# If logical block size != 4096:
sgdisk -a1 -n1:34:2047 -t1:EF02 /dev/sdX                                 # BIOS boot partition
```

After partitioning, both ESP and OS partitions are zero-filled:
```bash
dd if=/dev/zero of=$part bs=1M count=256
```

---

## 2. LVM Layout (ext4/XFS)

**Function:** `create_lvm_volumes($lvmdev, $os_size, $swap_size)` in `Install.pm:450`

### Physical volume and volume group

```bash
pvcreate --metadatasize 250k -y -ff /dev/sdX3    # pe_start = 512 (128k-aligned for SSDs)
vgcreate pve /dev/sdX3                            # VG name = product name ("pve", "pmg", "pbs")
```

### VG free-space reservation

```perl
# Install.pm:473 — $hdgb = os_size in GB
my $space = $hdgb <= 32  ? 4 * 1024                            # 4 MB
          : $hdgb > 128  ? 16 * 1024 * 1024                    # 16 GB
          :                ($hdgb / 8) * 1024 * 1024;           # hdgb/8 GB
```

| Disk (OS partition) | Reserved free space |
|---------------------|---------------------|
| ≤ 32 GB             | 4 MB                |
| 32–128 GB           | disk_GB / 8 GB      |
| > 128 GB            | 16 GB               |

### Logical volumes (PVE product)

| LV             | Device           | Size Formula                | Filesystem / Type |
|----------------|------------------|-----------------------------|-------------------|
| **swap**       | `/dev/pve/swap`  | `compute_swapsize()`        | swap              |
| **root**       | `/dev/pve/root`  | See root-size formula below | ext4 or XFS       |
| **data**       | `/dev/pve/data`  | Remainder − `minfree`       | LVM thin-pool     |

### Root size formula (PVE only)

```
rest    = os_size - swap_size       (in KB)
rest_mb = rest / 1024               (in MB)

if rest_mb < 12 GB:
    rootsize_mb = (rest_mb - 4) aligned down to 4 MB       # use almost everything
elif rest_mb < 48 GB:
    rootsize_mb = (rest_mb / 2) aligned down to 4 MB       # half
else:
    rootsize_mb = rest_mb / 4 + 12 GB                      # quarter + 12 GB

rootsize_mb = min(rootsize_mb, maxroot)                     # default maxroot = 96 GB
rootsize    = rootsize_mb * 1024, aligned down to 4 MB
```

**Default `maxroot`:** 96 GB (configurable via installer UI or answer file).

### Root size formula (non-PVE: PMG, PBS)

```
rootsize = os_size - minfree - swap_size    (aligned down to 4 MB)
```

No data LV is created for non-PVE products.

### Data thin-pool creation

Only created if `datasize > 4 GB`:

```perl
metadatasize = datasize / 100                               # 1% of data
metadatasize = clamp(1 GB, metadatasize, 16 GB)             # at least 1 GB, at most 16 GB
metadatasize = align_down_4MB(metadatasize)

datasize -= 2 * metadatasize                                # metadata from data space
datasize -= 4 MB                                            # 1 PE for rounding
```

```bash
lvcreate -Wy --yes -L${datasize}K -ndata pve
lvconvert --yes --type thin-pool --poolmetadatasize ${metadatasize}K pve/data
```

If `datasize ≤ 4 GB`, the thin-pool is skipped with a warning message.

---

## 3. Swap Size Calculation

**Function:** `compute_swapsize($hdsize)` in `Install.pm:575`

If the user specifies `swapsize` explicitly, that value is used directly.

Otherwise, automatic calculation based on total RAM (`ss` in MB) and disk size (`hdgb` in GB):

```
ss = total_memory (MB)

if ss < 4096 && hdgb >= 64:   ss = 4096      # bump up for large disks
if ss < 2048 && hdgb >= 32:   ss = 2048      # bump up for medium disks
if ss >= 2048 && hdgb <= 16:  ss = 1024      # reduce for small disks
if ss < 512:                  ss = 512        # minimum 512 MB
if ss > hdgb * 128:           ss = hdgb * 128 # cap proportional to disk
if ss > 8192:                 ss = 8192       # hard cap 8 GB

swapsize_kb = (ss * 1024) aligned down to 4 MB
```

**In practice:** swap ≈ RAM, capped at 8 GB, scaled down for small disks.

---

## 4. ZFS Layout

**Function:** `zfs_create_rpool($vdev, $pool_name, $root_volume_name)` in `Install.pm:207`

Each disk gets the same 3-partition GPT layout with partition type `BF01`.

### Pool creation

```bash
zpool create -f -o cachefile=none -o ashift=12 rpool <vdev-spec>
```

### ZFS datasets (PVE)

```
rpool/ROOT                    # container dataset
rpool/ROOT/pve-1              # root filesystem (mountpoint=/)
rpool/data                    # VM/container data
rpool/var-lib-vz              # mountpoint=/rpool/ROOT/pve-1/var/lib/vz
```

### ZFS properties

```bash
zfs set atime=on relatime=on rpool
zfs set compression=on rpool                    # default: on (lz4)
zfs set checksum=on rpool                       # default: on
zfs set copies=1 rpool                          # default: 1
zfs set acltype=posix rpool/ROOT/pve-1
```

### Default ZFS options (from `options.rs:348-362`)

| Option     | Default |
|------------|---------|
| `ashift`   | 12      |
| `compress`| on      |
| `checksum` | on      |
| `copies`   | 1       |

No swap partition or LVM is used with ZFS.

---

## 5. Btrfs Layout

**Function:** `btrfs_create($partitions, $mode)` in `Install.pm:158`

Each disk gets the same 3-partition GPT layout with partition type `8300`.

```bash
mkfs.btrfs -f -d <mode> -m <mode> /dev/sdX3 [/dev/sdY3 ...]
```

Where `mode` is one of: `single`, `raid0`, `raid1`, `raid10`.

For PVE, a subvolume is additionally created:
```bash
btrfs subvolume create $targetdir/var/lib/pve/local-btrfs
```

Mounted with options: `noatime,nobarrier`.

No swap partition or LVM is used with Btrfs.

---

## 6. Concrete Example: Single 500 GB Disk, LVM ext4, 16 GB RAM

| Component        | Start     | Size         | Type / FS   | Mount Point |
|------------------|-----------|--------------|-------------|-------------|
| GPT part 1       | sector 34 | ~1 MB        | BIOS boot   | —           |
| GPT part 2       | 1 MiB     | 1024 MB      | FAT32       | `/boot/efi` |
| GPT part 3       | 1025 MiB  | ~498 GB      | LVM PV      | —           |
| LV `pve/swap`    | —         | ~8 GB        | swap        | [swap]      |
| LV `pve/root`    | —         | ~96 GB       | ext4        | `/`         |
| LV `pve/data`    | —         | ~378 GB      | thin-pool   | VM storage  |
| (VG free)        | —         | ~16 GB       | —           | reserved    |

### Derivation for the example

- ESP: disk > 100 GB → **1024 MB**
- OS partition: ~498 GB ≈ 509,952 MB ≈ 522,190,848 KB
- Swap: RAM = 16 GB, hdgb ≥ 64 → ss = 16384 → capped at 8192 → **8 GB** (8,388,608 KB)
- rest = 522,190,848 - 8,388,608 = 513,802,240 KB ≈ 490 GB (> 48 GB)
- rootsize = 490/4 + 12 = 134.5 GB → capped at maxroot **96 GB**
- minfree (default): hdgb > 128 → **16 GB**
- datasize = 490 - 96 - 16 = **~378 GB** (minus thin-pool metadata overhead)
