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
| Feature | Value |
|---|---|
| Full name | VMware Virtual Machine Disk |
| File extension | .vmdk |
| MIME type | application/x-vmdk |
| Format type | Virtual hard-disk image (container for a full guest disk) |
| Developer | VMware (now part of Broadcom) |
| Introduced | 2003 (VMware Workstation / GSX Server) |
| Specification | Virtual Disk Format 5.0 (VMware); descriptor grammar contributed to the DMTF OVF effort |
| Open standard | Partial — format documented and widely re-implemented, but VMware-controlled |
| Byte order | Little-endian (sparse-extent header fields) |
| Magic number (hex) | 4B 44 4D 56 (ASCII KDMV) — sparse extents only |
| Descriptor marker | ASCII line # Disk DescriptorFile at the start of a text descriptor |
| Sector size | 512 bytes |
| Sparse header size | 512 bytes (one sector) |
| Default grain size | 128 sectors (64 KB) |
| Grain table entries | 512 per grain table (numGTEsPerGT) |
| Subtypes | monolithicSparse, monolithicFlat, twoGbMaxExtentSparse, twoGbMaxExtentFlat, vmfs, vmfsSparse, seSparse |
| Provisioning | Thin (sparse, grows on demand) or thick (pre-allocated / flat) |
| Max disk size | 2 TB (classic); 62 TB with SE-sparse (raised 2013) |
| Compression | Optional deflate on stream-optimized sparse extents |
| Related extensions | .vmx, .ova, .ovf, .vhd, .vdi, .iso |
| Specification URL | vmware.com/app/vmdk/ |
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.
| createType | Layout |
|---|---|
monolithicSparse | One growing file; descriptor embedded in the KDMV header |
monolithicFlat | Text descriptor + one pre-allocated -flat.vmdk raw image |
twoGbMaxExtentSparse | Split sparse: many -s001…-s00N files, each capped at 2 GB |
twoGbMaxExtentFlat | Split flat: many -f001…-f00N pre-allocated 2 GB pieces |
vmfs | ESXi thick disk on a VMFS datastore (descriptor + -flat) |
vmfsSparse | ESXi snapshot / redo-log delta disk |
seSparse | Space-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
- VMware — Virtual Disk Format (VMDK) specification
- Oracle VirtualBox — Virtual storage and supported disk image formats
- QEMU — qemu-img disk image utility
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.