Skip to main content
RunBook Academy

Proxmox VEV · Storage FundamentalsStorage concepts

The PVE storage model: storage.cfg, types and content

Intermediate⏱ ~26 minpvesm

What you'll learn

  • Read and write an /etc/pve/storage.cfg entry and name each of its properties
  • Explain content types and why "does not support content type" is configuration rather than a fault
  • Distinguish file-level from block-level backends and predict what each can hold
  • State exactly what the shared flag does and does not do
  • Decode a volume identifier and use pvesm to inspect, allocate and free volumes

Prerequisites

Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12

Not yet marked complete on this device.

Every storage lesson that follows — ZFS, Ceph, NFS, iSCSI — is about a technology. This one is about the layer Proxmox VE puts in front of all of them, and it is the layer you actually operate.

When a backup fails with “storage does not support content type”, when a migration refuses to start, when a disk lands somewhere you did not expect, the answer is almost never in the storage technology. It is in the eight lines of storage.cfg that describe it.

The file

Storage configuration lives in /etc/pve/storage.cfg and is therefore cluster configuration: written once, present on every node, and covered by the same quorum rules as everything else in /etc/pve. Its format is deliberately plain:

<type>: <STORAGE_ID>
        <property> <value>
        <property> <value>

A realistic file:

dir: local
        path /var/lib/vz
        content iso,vztmpl,backup
        prune-backups keep-last=3

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

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

pbs: pbs-primary
        datastore main
        server 192.0.2.20
        content backup
        fingerprint aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99
        prune-backups keep-daily=14,keep-weekly=8,keep-monthly=6

nfs: iso-share
        export /export/isos
        server 192.0.2.30
        content iso,vztmpl
        nodes pve-a1,pve-a2,pve-a3
        options vers=4.2

Five entries, five different backends, one grammar. That uniformity is the point of the abstraction: qm, pct, vzdump, the GUI and the API all speak to a storage through the same interface regardless of what is underneath.

Type: what is underneath

The type is the plugin. It determines the storage’s capabilities, and the documentation divides the plugins into two classes that predict most of the behaviour you will meet.

File-level — ZFS (as a filesystem), Directory, BTRFS, NFS, CIFS, CephFS. POSIX filesystems, so they can hold any content type, including ISO images, container templates, backups and snippets. Disk images on them are files: qcow2, raw or vmdk.

Block-level — LVM, LVM-thin, iSCSI, Ceph/RBD, ZFS zvols. They hold raw volumes and nothing else, so a block storage cannot store an ISO or a backup file.

TypePluginLevelSharedSnapshots
ZFSzfspoolbothnoyes
Directorydirfilenoqcow2 only
NFSnfsfileyesqcow2 only
CIFScifsfileyesqcow2 only
LVMlvmblockpossiblevolume chains (PVE 9+)
LVM-thinlvmthinblocknoyes
Ceph/RBDrbdblockyesyes

pvesm add accepts: btrfs, cephfs, cifs, dir, esxi, iscsi, iscsidirect, lvm, lvmthin, nfs, pbs, rbd, zfs, zfspool.

Content: what it is allowed to hold

The content property is a comma-separated list, and it is enforced. The seven content types:

Content typeHolds
imagesQEMU/KVM virtual machine disks
rootdirContainer root filesystems
vztmplContainer templates
isoISO images
backupvzdump backup files
snippetsGuest hook scripts and Cloud-Init snippets
importOVAs and disk images staged for import
Read-only / Safewhat each storage is, holds and has left
pvesm status

pvesm status --content images

pvesm list local-lvm
Read-only / Safe
$ pvesm status
Name              Type     Status           Total            Used       Available        %
iso-share          nfs     active       524288000        31457280       492830720    6.00%
local              dir     active       100663296         8388608        92274688    8.33%
local-lvm      lvmthin     active      1932735283       772294113      1160441170   39.96%
pbs-primary        pbs     active     10737418240      4294967296      6442450944   40.00%
shared-rbd         rbd     active     32212254720      9663676416     22548578304   30.00%

Node restriction, and the shared flag

Two properties that look similar and mean very different things.

nodes restricts which cluster nodes may access this storage. It is an access-control statement about topology: an NFS export reachable only from the management VLAN, or a local storage that exists on three nodes and not the fourth. Guests using a node-restricted storage cannot be migrated to a node outside the list.

shared tells PVE that every node sees the same data on this storage. It is the flag that makes live migration cheap — with shared storage, migrating a running VM moves memory and leaves the disks in place.

The volume identifier

Every guest disk is named by a volume ID, storage:volume, and this is what appears in a guest configuration:

scsi0: shared-rbd:vm-141-disk-0,size=64G,discard=on,ssd=1
scsi1: local:141/vm-141-disk-1.qcow2,size=500G
rootfs: local-lvm:subvol-205-disk-0,size=8G

Read the second one carefully: on a file-level storage the volume part includes the VMID directory and the file extension, because it is a path relative to the storage. On a block-level storage it is just a volume name, because there is no directory structure to reference.

The storage ID is the first field, which has a consequence worth internalising: a guest’s disk is bound to a storage ID, not to a device. Rename a storage and every guest referencing it breaks. Point a storage ID at different hardware and every guest referencing it silently follows.

Read-only / Saferesolve a volume ID to a path or device
pvesm path local:141/vm-141-disk-1.qcow2

pvesm path local-lvm:vm-141-disk-0

pvesm list shared-rbd --vmid 141
Read-only / Safe
$ pvesm list shared-rbd --vmid 141
Volid                              Format  Type       Size VMID
shared-rbd:vm-141-disk-0           raw     images 68719476736 141
shared-rbd:vm-141-disk-1           raw     images 536870912000 141

Adding a storage properly

Configuration changeadd an NFS storage restricted to three nodes
pvesm add nfs backup-nfs \
--server 192.0.2.30 \
--export /export/backups \
--content backup \
--nodes pve-a1,pve-a2,pve-a3 \
--options vers=4.2 \
--prune-backups keep-daily=14,keep-weekly=8

pvesm status --storage backup-nfs
Configuration changechange a content list safely
pvesm status --storage local | cat

grep -A6 '^dir: local$' /etc/pve/storage.cfg

pvesm set local --content iso,vztmpl,backup,snippets

Key takeaways

  • /etc/pve/storage.cfg is cluster configuration: <type>: <ID> followed by indented properties, identical grammar for every backend.
  • The type is the plugin and it determines capability. File-level backends (dir, nfs, cifs, cephfs, btrfs, zfspool as a filesystem) hold any content type; block-level backends (lvm, lvmthin, iscsi, rbd) hold raw volumes only.
  • content is enforced. “Does not support content type” means the backend cannot hold it, or the list does not include it. pvesm set --content replaces the list.
  • nodes restricts where a storage may be used and constrains migration. shared asserts that every node sees the same data.
  • shared 1 on a storage that is not genuinely shared causes silent data divergence during migration. Set it only where the backend shares: NFS, CIFS, CephFS, RBD, or LVM on a shared LUN.
  • A volume ID is storage:volume, and a guest is bound to the storage ID. File-level volumes carry the VMID directory and an extension; block-level volumes do not.
  • pvesm status for capacity and activation, pvesm list for volumes, pvesm path to resolve a volume to a device, pvesm alloc/free to create and destroy. Check status on more than one node.
  • Removing a storage entry breaks references but keeps data and is recoverable by re-adding it. Freeing a volume is not.

Knowledge check

Knowledge check · 5 questions

  1. Q1. Each node has a directory storage with the same path, backed by that node local disks. Someone sets shared 1 so that live migration works. What actually happens on the first migration?

  2. Q2. A backup job fails with "storage does not support content type". The target is an LVM-thin storage. What is the correct fix?

  3. Q3. Which statements about volume identifiers are correct? Select all that apply.

  4. Q4. Running pvesm remove on a storage that still holds guest disks deletes those disks.

  5. Q5. A storage shows as active on pve-a1 but inactive on pve-a2, and the storage.cfg entry is identical because it is cluster configuration. How is that possible?

Passing score: 75%. Answers are checked in this browser.