Safeguard
Developer Tools•

Why do VM and disk image builds differ, and how do you fix it?

Filesystem timestamps, UUIDs, labels and bootloaders vary between runs. See the documented fixes for ext4, XFS, ISO and SquashFS image builds.

Safeguard Security Team
4 min read

Disk and VM images differ between builds mostly because filesystems store creation times, UUIDs and labels, and because files inside carry timestamps. Fixes exist: set SOURCE_DATE_EPOCH, set UUIDs and labels explicitly, and use tools that build the filesystem in one pass from a normalised directory.

The usual causes

The Reproducible Builds documentation on system images lists the general problems:

  • the filesystem needs to be created in one go,
  • filesystems have creation or modification timestamps,
  • filesystems contain a UUID or label that is not set explicitly,
  • included files have timestamps,
  • included files may be generated or updated at build time in a non reproducible way,
  • the bootloader may have timestamps,
  • the initramfs inside the image may itself be built non reproducibly.

Timestamps that do not depend on SOURCE_DATE_EPOCH are not reproducible. The docs give an example: if a filesystem is created with mkfs.ext4, mounted, modified and saved as an image, the result is not reproducible, because the allocation of inodes is undefined.

If your team builds golden images, appliance images or installer ISOs, these are the same kinds of problems as in a tarball, just deeper.

Documented fixes by format

FormatWhat the docs recommend
ext2, ext3, ext4Use make_ext4fs, which creates the whole filesystem at once and has an option to set the modification time from SOURCE_DATE_EPOCH. Or use mkfs.ext2 from e2fsprogs 1.47.1 or later with a normalised tarball as input, a fixed hash seed, UUID and label
XFSUse a recent mkfs.xfs from xfsprogs 6.18.0 or later. Set SOURCE_DATE_EPOCH and DETERMINISTIC_SEED=1, give UUID and label explicitly, and populate the filesystem from a directory
ISOUse a recent xorriso that honours SOURCE_DATE_EPOCH, pass it as the modification date to clamp all mtimes, set a fixed isohybrid id, and build initrd images reproducibly
SquashFSUse a mksquashfs that honours SOURCE_DATE_EPOCH, clamps content timestamps and does not reorder fragments based on multithreading; squashfs-tools 4.5.1 is noted as working

For the ext2 route, the docs show packing the root directory with a sorted, clamped tar first, then handing that tar to the filesystem tool, so tar normalises metadata. That connects to making a tar archive reproducible.

Steps for an image pipeline

  1. Build the root filesystem content in a directory, not on a mounted image you keep editing.
  2. Normalise that directory: fixed timestamps, sorted order, fixed owners and modes.
  3. Choose a tool that creates the filesystem in one step from that directory.
  4. Set UUIDs and labels explicitly in configuration, not by generation.
  5. Set SOURCE_DATE_EPOCH, and any seed variables the tool supports.
  6. Remove or regenerate files that change on every build, such as caches and logs. The docs mention excluding unneeded files from the root filesystem image.
  7. Build the image twice and compare. Use diffoscope, which can open filesystem images, to find leftovers.

Version matters

Several of these fixes depend on tool versions: e2fsprogs 1.47.1 for the ext2 route, xfsprogs 6.18.0 for XFS, squashfs-tools 4.5.1 for SquashFS. Pin the versions of the image tools in your build environment, or you may lose reproducibility after an update. Record them with the rest of the build inputs. See what to record about a build.

Quick check

Why not just mount the image and copy files in?

Mounting and modifying a filesystem leaves undefined allocation details and fresh timestamps. Building it from a directory in one step avoids that.

Do I need to set the UUID myself?

Yes, if you want identical images. A generated UUID differs every build, so set it explicitly along with the label.

Safeguard's SBOM and dependency scanning is generally available, and the free scan at gold.safeguard.sh/scan can inventory dependencies in a repository.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.