10

Storage types

Proxmox does not have one storage engine; it has a plugin per backend, all described in one file, /etc/pve/storage.cfg. Each entry gets an id (like local-lvm), a type, and a list of content types it may hold. A VM disk is then just storage-id:volume-name, and the plugin knows how to create, snapshot, clone, migrate and delete it. Which plugin you pick decides what those operations cost.

Pick your requirements · see what fits

I need:
10 of 12 storage types fit. Click a row for details.
storagetypelevelsharedsnapshotsthinclonesformats
dirfile~~✓~raw, qcow2, vmdk, subvol
lvmblock~~––raw, qcow2*
lvmthinblock–✓✓✓raw
zfspoolboth–✓✓✓raw (zvol), subvol
zfsblock✓✓✓✓raw
nfsfile✓~✓~raw, qcow2, vmdk
cifsfile✓~✓~raw, qcow2, vmdk
iscsiblock✓–––raw
rbdblock✓✓✓✓raw
no images content
cephfsfile✓✓✓–files only
no images content
pbsboth✓–✓–chunked, deduplicated
btrfsfile–✓✓✓raw, subvol
LVM-thinlvmthin

Thin LVs carved from a thin pool. Blocks are allocated on first write, snapshots are cheap. The default local-lvm on ext4/xfs installs.

content types it accepts
imagesrootdirisovztmplbackupsnippetsimport

✓ supported · ~ conditional (hover for the condition) · – not supported. “Shared” means every node sees the same volume, so live migration and HA need no disk copy; it does not make a local disk shared.

the plugin model

Content types: what a storage is allowed to hold

The same NFS share can hold ISOs and backups but not VM disks, simply because images is missing from its content list. PVE uses the list to decide which storages to offer in each dialog, and the directory layout follows it: images/, template/iso/, template/cache/ (CT templates), dump/ (backups), snippets/, import/. Block storages (LVM, RBD, zvols) can only hold images and rootdir, because there is no filesystem to put an ISO on.
/etc/pve/storage.cfg — a typical small cluster
# /etc/pve/storage.cfg  (cluster-wide: pmxcfs replicates it to every node)
dir: local
	path /var/lib/vz
	content iso,vztmpl,backup

lvmthin: local-lvm
	thinpool data
	vgname pve
	content rootdir,images

# a ZFS install creates this instead of local-lvm:
# zfspool: local-zfs
# 	pool rpool/data
# 	sparse 1
# 	content images,rootdir

nfs: nas
	server 10.0.0.20
	export /export/pve
	path /mnt/pve/nas
	content iso,backup,snippets
	prune-backups keep-daily=7,keep-weekly=4

rbd: ceph-vm
	pool vm-pool
	content images,rootdir
	krbd 0

pbs: pbs
	server 10.0.0.30
	datastore store1
	username backup@pbs
	fingerprint 4b:9e:…:c1
	content backup
The default install
The installer creates local (a directory at /var/lib/vz for ISOs, templates and backups) plus either local-lvm (an LVM-thin pool pve/data, when you install on ext4/xfs) or local-zfs (the dataset rpool/data, when you install on ZFS). Guests go on the second one.
Local is local
storage.cfg is cluster-wide, but local-lvm on node A and local-lvm on node B are two different pools with the same name. Migrating a VM on local storage copies its disks over the network; HA cannot restart it elsewhere unless you replicate (ZFS) or use shared storage.
New in 9.x: snapshots on thick LVM
Shared thick LVM on a SAN historically had no snapshots. PVE 9 adds snapshots as volume chains (technology preview): with snapshot-as-volume-chain 1, disks are qcow2-formatted on top of the LV and each snapshot becomes a new layer in the chain. The same mechanism is available for directory, NFS and CIFS storages.
volume id → device → media

Where do the bytes actually live?

Follow one VM disk from what the guest sees down to the physical media. File-level storages end in a file on some filesystem; block-level storages end in a block device (or, for RBD and ZFS over iSCSI, in a network protocol QEMU speaks itself).

Trace a VM disk

  1. guestVM 100 sees /dev/sda (virtio-scsi)
  2. VM configscsi0: local-lvm:vm-100-disk-0,size=32G
    /etc/pve/qemu-server/100.conf: <storage id>:<volume name>
  3. storage pluginlvmthin: local-lvm vgname pve thinpool data
  4. block device/dev/pve/vm-100-disk-0
    thin LV: 32 GiB virtual, only written chunks are backed
  5. thin poolpve/data (data LV + metadata LV, dm-thin)
  6. mediaVG pve on PV /dev/sda3
block-level: a raw block devicebytes live on this node only
LVM-thin, sparse zvols, RBD, qcow2

Thin provisioning: promises versus disk

Thin storages allocate a block the first time a guest writes it. That makes snapshots cheap and lets you hand out more virtual disk than you own. It also means the pool can fill up while every guest still sees free space. Push the sliders until it breaks.

Overcommit a 1000 GiB thin pool

promised (virtual)
1400 GiB
pool size
1000 GiB
overcommit
1.4×
data used
63%
what each VM believes it has
100
101
102
103
104
105
106
107
108
109
110
111
112
113
thin pool pve/data · data635 / 1000 GiB
thin pool · metadata (block mappings)57%
Overcommit is fine while guests are mostly empty
The pool only backs blocks a guest has actually written. You promised 1400 GiB on 1000 GiB of disk, and that holds as long as someone watches the real usage.

Without discard, deleting files in a guest frees nothing on the host: the filesystem just marks blocks free. With discard=on on the virtual disk and periodic fstrim inside the guest, unmapped blocks are returned. Snapshots pin the old version of every block overwritten since they were taken. The same arithmetic applies to sparse zvols and Ceph RBD images.

pvesm, lvs

The CLI

pvesm — the storage manager
# list storages and their usage on this node
pvesm status

# what is on a storage (volume ids)
pvesm list local-lvm

# add storages from the CLI (same as Datacenter → Storage → Add)
pvesm add nfs nas --server 10.0.0.20 --export /export/pve --content iso,backup
pvesm add lvm san-lvm --vgname vg-san --shared 1 --content images

# volume id → real path on this node
pvesm path local-lvm:vm-100-disk-0      # /dev/pve/vm-100-disk-0

# restrict a local storage to the nodes that actually have it
pvesm set local-zfs --nodes pve1,pve2
keeping a thin pool healthy
# thin pool usage: watch Data% and Meta%
lvs -o lv_name,lv_size,data_percent,metadata_percent pve
#  LV            LSize   Data%  Meta%
#  data          <1.0t   63.12  2.41
#  vm-100-disk-0 100.00g 54.80
#  vm-101-disk-0 100.00g 32.17

# let lvm grow the pool automatically from free VG space (lvm.conf)
#   thin_pool_autoextend_threshold = 80
#   thin_pool_autoextend_percent   = 20

# give freed guest blocks back: discard on the disk, trim in the guest
qm set 100 --scsi0 local-lvm:vm-100-disk-0,discard=on,ssd=1
# inside the guest:  fstrim -av   (or enable fstrim.timer)
  • why it mattersA storage is a plugin + an id + allowed content types in /etc/pve/storage.cfg. Volume ids like local-lvm:vm-100-disk-0 are what VM configs reference, so moving a disk is a plugin operation, not a file copy you do by hand.
  • why it mattersFeatures come from the backend: shared storage (NFS, RBD, SAN LVM) makes migration and HA cheap; LVM-thin, ZFS and RBD give fast snapshots and linked clones; file storages get them from qcow2.
  • why it mattersThin provisioning is a promise, not capacity. Monitor real usage (lvs, zfs list, ceph df), enable discard, and grow the pool before it fills, because a full thin pool pauses every VM on it at once.