Sparse allocation lets a large disk image occupy only the blocks that contain data. Copying every zero-filled region as ordinary data can consume the image's full logical size on the receiving volume, which matters when moving virtual machine disks, database images, or sparse archives.

The rsync --sparse option turns long sequences of zero bytes into holes on the receiving side. The destination filesystem must support sparse files, and its block layout can differ from the source, so verify the destination's allocation instead of expecting identical block counts.

Copy a quiescent file whose contents are no longer changing. Do not add --preallocate unless the destination kernel and filesystem have been tested with sparse preallocation, because reserved extents can leave zero-filled regions allocated.

Steps to preserve sparse files with rsync:

  1. Stop or freeze the workload that writes to the source image.

    A virtual machine or database that writes during the transfer can leave the destination internally inconsistent even when rsync preserves its holes.

  2. Record the source file's logical size and allocated block count.
    $ stat --format='%n %s bytes %b blocks' /srv/source/vm-image.raw
    /srv/source/vm-image.raw 1073741824 bytes 16 blocks

    stat reports %s as logical bytes and %b as allocated 512-byte blocks. The small block count confirms that most of this 1 GiB file is a hole.

  3. Measure the source file's allocated disk space.
    $ du --block-size=1 /srv/source/vm-image.raw
    8192	/srv/source/vm-image.raw

    du reports allocated bytes by default. The exact value depends on the source filesystem's block and extent layout.

  4. Copy the file with sparse handling enabled.
    $ rsync --archive --sparse --itemize-changes /srv/source/vm-image.raw /srv/destination/
    >f+++++++++ vm-image.raw
  5. Compare the source and destination logical sizes and block counts.
    $ stat --format='%n %s bytes %b blocks' /srv/source/vm-image.raw /srv/destination/vm-image.raw
    /srv/source/vm-image.raw 1073741824 bytes 16 blocks
    /srv/destination/vm-image.raw 1073741824 bytes 16 blocks

    The logical byte counts must match. The allocated block counts do not need to be identical across filesystems, but the destination count should remain far below the number needed for a fully allocated file.

  6. Compare allocated space on both files.
    $ du --block-size=1 /srv/source/vm-image.raw /srv/destination/vm-image.raw
    8192	/srv/source/vm-image.raw
    8192	/srv/destination/vm-image.raw

    A destination value close to the 1 GiB logical size means its filesystem or mount did not preserve the holes.

  7. Run a checksum dry run against the completed copy.
    $ rsync --archive --sparse --checksum --dry-run --itemize-changes /srv/source/vm-image.raw /srv/destination/

    No output means rsync found no file content or requested metadata changes to send. Large images take longer to verify because both copies must be read.
    Related: How to preview rsync changes before syncing