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.
| storage | type | level | shared | snapshots | thin | clones | formats |
|---|---|---|---|---|---|---|---|
| dir | file | ~ | ~ | ✓ | ~ | raw, qcow2, vmdk, subvol | |
| lvm | block | ~ | ~ | – | – | raw, qcow2* | |
| lvmthin | block | – | ✓ | ✓ | ✓ | raw | |
| zfspool | both | – | ✓ | ✓ | ✓ | raw (zvol), subvol | |
| zfs | block | ✓ | ✓ | ✓ | ✓ | raw | |
| nfs | file | ✓ | ~ | ✓ | ~ | raw, qcow2, vmdk | |
| cifs | file | ✓ | ~ | ✓ | ~ | raw, qcow2, vmdk | |
| iscsi | block | ✓ | – | – | – | raw | |
| rbd | block | ✓ | ✓ | ✓ | ✓ | raw | |
no images content | cephfs | file | ✓ | ✓ | ✓ | – | files only |
no images content | pbs | both | ✓ | – | ✓ | – | chunked, deduplicated |
| btrfs | file | – | ✓ | ✓ | ✓ | 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 backupThe 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
- guestVM 100 sees /dev/sda (virtio-scsi)
- VM configscsi0: local-lvm:vm-100-disk-0,size=32G/etc/pve/qemu-server/100.conf: <storage id>:<volume name>
- storage pluginlvmthin: local-lvm vgname pve thinpool data
- block device/dev/pve/vm-100-disk-0thin LV: 32 GiB virtual, only written chunks are backed
- thin poolpve/data (data LV + metadata LV, dm-thin)
- 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,pve2keeping 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 likelocal-lvm:vm-100-disk-0are 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.