Tools: Stop Shipping Fat Images: Practical `systemd-repart` for First-Boot Partition Growth on Linux (2026)

Tools: Stop Shipping Fat Images: Practical `systemd-repart` for First-Boot Partition Growth on Linux (2026)

What systemd-repart actually does

When this is a good fit

Anti-duplication note

Example layout: small image, bigger disk

/usr/lib/repart.d/10-root.conf

/usr/lib/repart.d/20-var.conf

Make the filesystem growth explicit

Safer dry run against an image file

A more deterministic fixed-size variant

Where people get tripped up

1. Expecting it to resize filesystems automatically

2. Using it on the wrong class of system

3. Forgetting that matching is type-based and ordered

4. Assuming it is destructive by default

A practical VM workflow

Verification commands after first boot

Final take If you build Linux images for VMs, cloud instances, or appliances, you usually face the same tradeoff: systemd-repart gives you a cleaner option. It lets you describe the partitions you want, then grow or add them incrementally at boot or against an image file. The useful part is not just automation. It is that the behavior is declarative, repeatable, and incremental. In this guide, I will show a practical pattern for: According to the systemd-repart(8) documentation, it is intended for image-based OS deployments and works in a mostly incremental way: it grows existing partitions and adds new ones, but does not shrink, move, or delete partitions during normal operation. That matters because it makes first-boot layout changes much less fragile than home-grown parted scripts. A few facts worth keeping straight: That last point is the one people often miss. Growing a root partition is only half the job. You also need the filesystem inside it to grow. I like systemd-repart for cases like these: I would not reach for it first on an already hand-managed server with a messy MBR layout, LVM stack, or years of manual partition edits. This is strongest when the image layout is intentional from day one. This article intentionally avoids overlap with my recent posts on systemd-sysext, systemd-delta, systemd-tmpfiles, systemd-oomd, and needrestart. The angle here is specifically first-boot GPT partition growth and image adaptation with systemd-repart, plus the operational boundary between repartitioning and filesystem growth. Let us say your image ships with: But on the target machine you want: A minimal repart.d layout could look like this. The matching behavior is filename ordered. The repart.d(5) docs are explicit that definition files are sorted by filename and matched against existing partitions of the same GPT type in that order. Even though GrowFileSystem= exists, I prefer making root filesystem growth obvious in the mount configuration too. For /etc/fstab, that usually means: If the filesystem is already at full size, systemd-growfs does nothing. Before baking this into a boot path, test it against an image file. Create a working directory: Add the definition files: Then run a dry run against an image file: Once the output looks right, you can repeat against a disposable VM image and boot it. If you do not want elastic sizing, set the minimum and maximum size to the same value. Per repart.d(5), when SizeMinBytes= and SizeMaxBytes= are equal, the weight no longer matters because the partition size becomes fixed. That is useful when you need predictability more than full-disk consumption. By default, systemd-repart is mostly about the partition table. If you enlarge a partition but forget filesystem growth, df -h may still show the old size. This is ideal for image-based, GPT-first designs. It is not my first choice for a snowflake server with years of manual storage history. repart.d definitions are matched to existing partitions by GPT type UUID, then by filename order among definitions of the same type. If you define multiple partitions of the same type, naming and ordering matter. Normal systemd-repart operation is intentionally incremental. That said, options like --empty=force are destructive. Treat those as lab-only until you are absolutely sure what you are doing. If I were rolling this out for real, I would use this sequence: That workflow is boring, and boring is exactly what you want from storage automation. After a test boot, I would check: Those four commands usually tell you whether: I like systemd-repart because it replaces a pile of first-boot storage glue with something declarative and reviewable. If your Linux images are meant to land on disks of different sizes, this is one of the cleanest ways to keep the shipped image small while still letting the installed system take ownership of real capacity on first boot. Just keep one boundary in mind: Get that split right, and the whole setup becomes much easier to reason about. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

[Partition] Type=root Label=root SizeMinBytes=2G GrowFileSystem=yes [Partition] Type=root Label=root SizeMinBytes=2G GrowFileSystem=yes [Partition] Type=root Label=root SizeMinBytes=2G GrowFileSystem=yes [Partition] Type=var Label=var SizeMinBytes=4G Weight=1000 Format=ext4 [Partition] Type=var Label=var SizeMinBytes=4G Weight=1000 Format=ext4 [Partition] Type=var Label=var SizeMinBytes=4G Weight=1000 Format=ext4 LABEL=root / ext4 defaults,x-systemd.growfs 0 1 LABEL=var /var ext4 defaults 0 2 LABEL=root / ext4 defaults,x-systemd.growfs 0 1 LABEL=var /var ext4 defaults 0 2 LABEL=root / ext4 defaults,x-systemd.growfs 0 1 LABEL=var /var ext4 defaults 0 2 mkdir -p repart-demo/repart.d cd repart-demo mkdir -p repart-demo/repart.d cd repart-demo mkdir -p repart-demo/repart.d cd repart-demo cat > repart.d/10-root.conf <<'EOF' [Partition] Type=root Label=root SizeMinBytes=2G GrowFileSystem=yes EOF cat > repart.d/20-var.conf <<'EOF' [Partition] Type=var Label=var SizeMinBytes=4G Weight=1000 Format=ext4 EOF cat > repart.d/10-root.conf <<'EOF' [Partition] Type=root Label=root SizeMinBytes=2G GrowFileSystem=yes EOF cat > repart.d/20-var.conf <<'EOF' [Partition] Type=var Label=var SizeMinBytes=4G Weight=1000 Format=ext4 EOF cat > repart.d/10-root.conf <<'EOF' [Partition] Type=root Label=root SizeMinBytes=2G GrowFileSystem=yes EOF cat > repart.d/20-var.conf <<'EOF' [Partition] Type=var Label=var SizeMinBytes=4G Weight=1000 Format=ext4 EOF systemd-repart \ --dry-run=yes \ --empty=create \ --size=12G \ --definitions=repart.d \ demo.img systemd-repart \ --dry-run=yes \ --empty=create \ --size=12G \ --definitions=repart.d \ demo.img systemd-repart \ --dry-run=yes \ --empty=create \ --size=12G \ --definitions=repart.d \ demo.img [Partition] Type=var Label=var SizeMinBytes=8G SizeMaxBytes=8G Format=ext4 [Partition] Type=var Label=var SizeMinBytes=8G SizeMaxBytes=8G Format=ext4 [Partition] Type=var Label=var SizeMinBytes=8G SizeMaxBytes=8G Format=ext4 lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINTS,PARTLABEL,PARTTYPE lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINTS,PARTLABEL,PARTTYPE lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINTS,PARTLABEL,PARTTYPE findmnt -no SOURCE,FSTYPE,OPTIONS / findmnt -no SOURCE,FSTYPE,OPTIONS / findmnt -no SOURCE,FSTYPE,OPTIONS / journalctl -b -u systemd-repart.service journalctl -b -u systemd-repart.service journalctl -b -u systemd-repart.service - ship a large image that wastes space everywhere, or - ship a small image and rely on ad hoc first-boot scripts to resize partitions - growing the root partition on first boot, - adding a dedicated /var partition when extra disk space exists, - growing the filesystem itself with x-systemd.growfs, and - dry-running the whole layout safely against an image file before rollout. - systemd-repart works with GPT partition tables. - It is typically run in the initrd at boot through systemd-repart.service. - It can operate on the running system's backing device when invoked without arguments. - It can also operate on an image file with --image=. - By default, it changes the partition table, not the filesystem inside the partition, unless you explicitly use formatting features or pair it with filesystem growth. - a golden image for KVM, Proxmox, or cloud VMs - an appliance-style image that should expand to the target disk - a Linux image where /var, /home, or swap should appear only when space is available - A/B-style images where the secondary partition is created on first boot - one root partition - the root partition to grow beyond its shipped size - a separate /var partition to be created if extra disk space is available - Type=root matches the architecture-appropriate GPT root partition type. - SizeMinBytes=2G ensures the root partition is at least 2 GiB. - GrowFileSystem=yes marks the partition so tools that honor the flag grow the filesystem on first mount. - Type=var declares a /var partition using the Discoverable Partitions Specification type. - Format=ext4 tells systemd-repart to create a filesystem for that new partition. - it is visible during review, - it makes the filesystem-growth step explicit, and - it uses the documented [email protected] path that systemd exposes for mounted filesystems. - a new GPT image file is created, - the partition plan is computed from your .conf files, - and no real disk is modified because --dry-run=yes is in effect. - GrowFileSystem=yes where appropriate, - x-systemd.growfs for mounts, or - a documented filesystem-specific growth step if your setup requires it. - build the image with a deliberately small root partition, - include repart.d files in /usr/lib/repart.d/, - ensure root mounts with x-systemd.growfs if needed, - test with systemd-repart --dry-run against an image file, - boot a disposable VM on a larger virtual disk, - verify partition layout with lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS,PARTLABEL, - verify filesystem growth with findmnt / and df -h /, - only then promote the image. - the partition exists, - the expected filesystem exists, - the mount options include the growth path you expected, and - systemd-repart did what the layout files said. - systemd-repart manages partition intent - systemd-growfs or equivalent handles filesystem expansion - systemd-repart(8): https://manpages.debian.org/testing/systemd/systemd-repart.8.en.html - repart.d(5): https://manpages.debian.org/testing/systemd/repart.d.5.en.html - systemd.io, Safely Building Images: https://systemd.io/BUILDING_IMAGES/ - Discoverable Partitions Specification: https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ - systemd-growfs(8): https://manpages.debian.org/testing/systemd/systemd-growfs.8.en.html