VMDK File Documentation


Summary

A Virtual Machine Disk File is a container that stores a complete virtual hard disk — the partition table, filesystem, operating system and every file of a VMware virtual machine — in one or more host files. Created by VMware in 2003, it uses the .vmdk extension and the MIME type application/x-vmdk. You attach it to a virtual machine rather than opening it as a document.

Technical details

FeatureValue
Full nameVMware Virtual Machine Disk
File extension.vmdk
MIME typeapplication/x-vmdk
Format typeVirtual hard-disk image (container for a full guest disk)
DeveloperVMware (now part of Broadcom)
Introduced2003 (VMware Workstation / GSX Server)
SpecificationVirtual Disk Format 5.0 (VMware); descriptor grammar contributed to the DMTF OVF effort
Open standardPartial — format documented and widely re-implemented, but VMware-controlled
Byte orderLittle-endian (sparse-extent header fields)
Magic number (hex)4B 44 4D 56 (ASCII KDMV) — sparse extents only
Descriptor markerASCII line # Disk DescriptorFile at the start of a text descriptor
Sector size512 bytes
Sparse header size512 bytes (one sector)
Default grain size128 sectors (64 KB)
Grain table entries512 per grain table (numGTEsPerGT)
SubtypesmonolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, vmfs, vmfsSparse, seSparse
ProvisioningThin (sparse, grows on demand) or thick (pre-allocated / flat)
Max disk size2 TB (classic); 62 TB with SE-sparse (raised 2013)
CompressionOptional deflate on stream-optimized sparse extents
Related extensions.vmx, .ova, .ovf, .vhd, .vdi, .iso
Specification URLvmware.com/app/vmdk/
File signature (magic bytes)
4B 44 4D 56

Offset 0, 4 bytes, ASCII KDMV (the little-endian spelling of “VMDK”). This signature marks a sparse / hosted extent only — the magicNumber field of the 512-byte SparseExtentHeader. Not every .vmdk has it. A descriptor-only .vmdk is plain text and begins with the ASCII line # Disk DescriptorFile, while a monolithic-flat or ESXi -flat.vmdk extent is a raw sector image with no signature at all: byte 0 is simply the first byte of the guest disk (often the boot sector). So the presence of a signature depends on the subtype.

What is a VMDK file?

VMDK stands for Virtual Machine Disk, VMware’s format for a virtual hard disk. VMware introduced it in 2003 with VMware Workstation and GSX Server, and it has been the disk format across Workstation, Fusion, Player and ESXi/vSphere ever since. A single .vmdk represents a whole guest disk: its partition table, its filesystem, the installed operating system and every file inside it. A virtual machine is usually one .vmx configuration file plus one or more VMDK disks, so you attach a VMDK to a VM rather than opening it like a document.

The format is not a single fixed layout. VMDK is a small family of related on-disk structures selected by a create type. Some VMDK files are a compact text descriptor that only points at the real data; others are the binary data extents themselves, either allocated on demand (sparse) or reserved up front (flat). The rest of this article works through both halves: the text descriptor grammar and the binary sparse-extent header, then the subtypes, provisioning modes, size limits and how OVF/OVA wraps a VMDK. VMware documents the layout in its Virtual Disk Format specification, and the descriptor grammar was contributed to the DMTF’s OVF work, which is why VirtualBox, QEMU and Proxmox all read and write it.

The descriptor and the data extents: why a .vmdk can be 1 KB

The single most common point of confusion is finding a .vmdk that is only a kilobyte or two. That file is a text descriptor, not the disk. The actual sectors live in separate extent files beside it: a large disk-flat.vmdk on ESXi, or a set of disk-s001.vmdk, disk-s002.vmdk pieces for a split disk. The descriptor is the map; the extents are the territory. If you copy the descriptor without its extents, the disk is unusable, so the two must always travel together.

A descriptor is plain ASCII and starts with a fixed comment line. A minimal monolithic-flat descriptor looks like this:

# Disk DescriptorFile
version=1
CID=fb183c20
parentCID=ffffffff
createType="monolithicFlat"

# Extent description
RW 20971520 FLAT "disk-flat.vmdk" 0

# The Disk Data Base
#DDB
ddb.adapterType = "lsilogic"
ddb.geometry.cylinders = "1305"
ddb.geometry.heads = "255"
ddb.geometry.sectors = "63"
ddb.virtualHWVersion = "18"

Three header fields set the identity of the disk. version is always 1 in current VMware products. CID is a random 32-bit content identifier, written as eight hex digits, that VMware bumps the first time the disk content changes after it is opened; snapshots use it to check that a child still matches its parent. parentCID names the parent’s CID for a snapshot delta, or the sentinel ffffffff (−1) for a base disk with no parent. createType selects the subtype, discussed below.

Extent description lines, field by field

After the header comes one extent description line per data file. Each line has the form:

ACCESS  SIZE_IN_SECTORS  TYPE  "FILENAME"  [offset]

RW 20971520 FLAT "disk-flat.vmdk" 0
RW 4192256  SPARSE "disk-s001.vmdk"
RW 4192256  SPARSE "disk-s002.vmdk"

The first token is the access mode: RW for read-write, RDONLY for read-only, or NOACCESS for a masked region. The second is the extent’s size as a count of 512-byte sectors: 20971520 sectors is exactly 10 GB. The third is the extent type. FLAT means the file is a raw, contiguous sector image with no header. SPARSE means the file carries the KDMV binary header and allocates on demand. VMFS and VMFSSPARSE are the ESXi equivalents that live on a VMFS datastore. The fourth token is the extent filename in quotes; a trailing offset (in sectors) is present for flat extents and points at where the data begins in that file. Summing the sector counts of all extents gives the total virtual disk size.

The #DDB section is the disk database: free-form ddb.* key/value pairs holding the disk geometry (cylinders, heads, sectors), the emulated controller (ddb.adapterType, for example lsilogic, buslogic or ide) and the virtual hardware version. Geometry here is a compatibility hint for the guest BIOS, not a physical constraint.

The sparse extent header: KDMV and its fields

A SPARSE extent begins with a 512-byte binary structure, the SparseExtentHeader, that occupies the first sector of the file. Its first four bytes are the magic number 4B 44 4D 56, ASCII KDMV — the little-endian spelling of “VMDK”. All multi-byte integers in this header are little-endian, and every offset it records is measured in 512-byte sectors, not bytes. The field layout is:

Offset  Size  Field              Meaning
  0      4    magicNumber        0x564D444B  "KDMV"
  4      4    version            1, 2 or 3
  8      4    flags              feature bits (valid newline test, etc.)
 12      8    capacity           virtual disk size, in sectors
 20      8    grainSize          sectors per grain (default 128 = 64 KB)
 28      8    descriptorOffset   embedded descriptor start (0 = none)
 36      8    descriptorSize     embedded descriptor length, in sectors
 44      4    numGTEsPerGT       grain-table entries per table (512)
 48      8    rgdOffset          redundant grain directory offset
 56      8    gdOffset           primary grain directory offset
 64      8    overHead           sectors of metadata before the grains
 72      1    uncleanShutdown    1 if the disk was not closed cleanly
 73      4    newlineDetect      '\n' ' ' '\r' '\n' corruption test
 77      2    compressAlgorithm  0 = none, 1 = DEFLATE
 79    433    pad                zero fill to 512 bytes

Several fields deserve a note. capacity times 512 is the disk size the guest sees. grainSize is the allocation unit and defaults to 128 sectors (64 KB); it must be a power of two and greater than 8. descriptorOffset and descriptorSize are non-zero when the descriptor is embedded inside the sparse file itself (as in a monolithic-sparse disk) rather than sitting in a separate .vmdk. The four-byte newline test at offset 73 stores \n, a space, \r, \n: if a broken FTP transfer rewrites CR/LF pairs, these bytes change and the reader knows the file is damaged, the same defensive trick that PNG uses in its signature. compressAlgorithm is 1 for the stream-optimized variant that DEFLATE-compresses each grain, which is what makes a VMDK small enough to ship inside an OVA.

Grain directory, grain tables and GTEs

A sparse extent does not store empty regions of the disk. It maps only the grains that have been written, using a two-level lookup that resembles a page table. The three structures are:

grain directory (GD)   array of 32-bit sector offsets, one per grain table
      |
      v
grain table (GT)       512 entries (numGTEsPerGT), each a 32-bit sector offset
      |
      v
grain table entry(GTE) points at the actual 64 KB grain of guest data
                       0 = grain not allocated (reads back as zeros)

To resolve a guest sector, the reader divides by grainSize to get a grain number, splits that into a directory index and a table index, follows gdOffset to the grain directory to find the grain table, then reads the grain table entry. A GTE of 0 means the grain was never written, so the read returns zeros without touching disk. That indirection is exactly why a thin 100 GB disk holding 4 GB of data occupies roughly 4 GB on the host. The header also stores rgdOffset, a redundant copy of the grain directory, so a torn metadata write can be recovered. The grain directory and tables together are the overHead sectors that sit ahead of the first grain.

Disk subtypes: monolithic, split, flat and VMFS

The createType in the descriptor picks one of several physical arrangements. The practical differences are how many files there are and whether space is allocated on demand.

createTypeLayout
monolithicSparseOne growing file; descriptor embedded in the KDMV header
monolithicFlatText descriptor + one pre-allocated -flat.vmdk raw image
twoGbMaxExtentSparseSplit sparse: many -s001…-s00N files, each capped at 2 GB
twoGbMaxExtentFlatSplit flat: many -f001…-f00N pre-allocated 2 GB pieces
vmfsESXi thick disk on a VMFS datastore (descriptor + -flat)
vmfsSparseESXi snapshot / redo-log delta disk
seSparseSpace-efficient sparse (2013+): supports up to 62 TB, reclaims freed blocks

The split (twoGbMaxExtent) types exist for filesystems that cannot hold a single very large file, historically FAT32 with its 4 GB ceiling. Each piece is capped at 2 GB and the descriptor lists them in order. Monolithic types keep everything in one file, which is simpler on modern filesystems. On ESXi the descriptor and the -flat data are always separate, which is why an ESXi VM shows a tiny disk.vmdk next to a huge disk-flat.vmdk.

Thin versus thick provisioning

Provisioning is orthogonal to the file count. A thin (sparse) disk allocates grains only as the guest writes them, so it starts small and grows toward its declared capacity. A thick (flat / pre-allocated) disk reserves the full size on the host immediately: a 100 GB thick disk is a 100 GB file from the moment it is created. Thick disks avoid the small write-time cost of allocating a new grain and updating the grain table, and they cannot fragment the host filesystem as they grow. ESXi adds a further split within thick: lazy-zeroed disks reserve the space but only zero each block on first write, while eager-zeroed disks zero the whole file at creation, which is required for some clustering features. The seSparse type, introduced in 2013, is a thin format that can also hand freed blocks back to the host, something the classic sparse layout could not do.

Size limits: 2 TB classic and 62 TB SE-sparse

The original hosted-sparse layout uses 32-bit sector offsets in its grain tables, which caps a single VMDK at 2 TB (2^32 sectors of 512 bytes, minus overhead). For years that was the hard per-disk limit. In September 2013, with vSphere 5.5, VMware raised the maximum virtual disk to 62 TB using the space-efficient sparse (seSparse) format and 64-bit VMFS addressing. A guest that needs more than 62 TB has to span several disks, for example with a volume manager inside the guest. Grain size also affects the practical ceiling of the classic format: a larger grainSize lets one grain table cover more of the disk, which is one reason large disks are sometimes created with grains bigger than the 64 KB default.

How OVA and OVF embed a VMDK

A VMDK describes one disk, but a whole machine is more than its disks: it has a virtual hardware definition, network settings and metadata. That machine-level description is OVF (Open Virtualization Format), a DMTF standard whose XML .ovf descriptor references one or more VMDK disks by filename and records a SHA checksum of each in a companion .mf manifest. An OVA is simply that whole set — the .ovf, the .mf and the .vmdk disks — concatenated into a single uncompressed TAR archive, with the .ovf stored first so a reader can parse the manifest before streaming the large disks. The VMDKs inside an OVA are almost always the stream-optimized (DEFLATE-compressed) monolithic-sparse variant, which is why extracting an OVA with an archiver drops one or more .vmdk files that any hypervisor can then import. To move a disk to a different hypervisor you convert it: VHD/VHDX for Microsoft Hyper-V, or VDI for VirtualBox, though VirtualBox and QEMU can attach a VMDK directly without conversion.

Frequently asked questions

Why is my .vmdk only a few kilobytes?

Because it is the text descriptor, not the disk. The real sectors are in the matching -flat.vmdk (ESXi) or -s001.vmdk … -s00N.vmdk split files next to it. Keep the descriptor and its extents together and point the VM at the descriptor; the descriptor alone is useless.

Do all VMDK files start with the KDMV signature?

No. Only SPARSE extents carry the 4B 44 4D 56 header at offset 0. A descriptor-only .vmdk is plain text starting with # Disk DescriptorFile, and a FLAT extent is a raw sector image with no signature — its first byte is the guest disk’s first byte, usually a boot sector.

What is a grain, and why 64 KB?

A grain is the smallest unit a sparse disk allocates, set by grainSize and defaulting to 128 sectors (64 KB). Larger grains mean fewer grain-table entries and less metadata for a big disk, at the cost of allocating more space than a small write strictly needs. It must be a power of two and larger than 8 sectors.

References