Common tasks
Short recipes to copy and adapt. Each one assumes a connection: see Connection:
Connect-PveCluster -HostsAndPorts pve01 -ApiToken $env:PVE_API_TOKENThe outputs come from a two-node test cluster, with names and addresses changed.
Guests
Section titled “Guests”Running guests per node
Section titled “Running guests per node”Get-PveGuest | Where-Object status -eq 'running' | Group-Object node | Select-Object Name, CountName Count---- -----pve01 9pve02 6Guests using the most memory
Section titled “Guests using the most memory”Get-PveGuest | Where-Object status -eq 'running' | Sort-Object mem -Descending | Select-Object -First 3 vmid, name, node, @{ n = 'memGB'; e = { [math]::Round($_.mem / 1GB, 1) } }, @{ n = 'maxmemGB'; e = { [math]::Round($_.maxmem / 1GB, 1) } }vmid name node memGB maxmemGB---- ---- ---- ----- --------1107 erp01 pve01 12.20 16.001104 gitlab pve01 9.50 10.001106 app02 pve01 5.90 8.00mem and maxmem are in bytes: 1GB is a PowerShell constant for 1073741824.
Find a VM by its IP address
Section titled “Find a VM by its IP address”The IP addresses of a VM are known to the guest agent, not to Proxmox VE: ask every running VM that has one.
$ip = '192.0.2.21'Get-PveGuest | Where-Object { $_.type -eq 'qemu' -and $_.status -eq 'running' } | Where-Object { $r = Get-PveNodesQemuAgentNetworkGetInterfaces -Node $_.node -Vmid $_.vmid $r.IsSuccessStatusCode -and ($r.Response.data.result.'ip-addresses'.'ip-address' -contains $ip)} | Select-Object vmid, name, nodevmid name node---- ---- ----1006 dc01 pve01VMs without a running agent answer with an error and are skipped.
Guests with a tag
Section titled “Guests with a tag”Get-PveGuest -VmIdOrName '@tag-production' | Select-Object vmid, name, tags@tag- finds the guests that have the tag among their others. The same selection takes exclusions:
'@tag-production,-@node-pve03': see Finding VMs.
CPU, memory and uptime
Section titled “CPU, memory and uptime”Get-PveNode | Select-Object node, status, @{ n = 'cpu%'; e = { [math]::Round($_.cpu * 100, 1) } }, @{ n = 'memGB'; e = { [math]::Round($_.mem / 1GB, 1) } }, @{ n = 'maxmemGB'; e = { [math]::Round($_.maxmem / 1GB, 1) } }, @{ n = 'uptime'; e = { [timespan]::FromSeconds($_.uptime).ToString('d\d\ hh\h') } }node status cpu% memGB maxmemGB uptime---- ------ ---- ----- -------- ------pve02 online 1.50 78.40 125.50 90d 15hpve01 online 4.20 188.40 251.50 90d 15hcpu is a fraction of all the cores of the node, 0.042 is 4.2%.
Pending updates
Section titled “Pending updates”foreach ($node in Get-PveNode) { $updates = (Get-PveNodesAptUpdate -Node $node.node).Response.data "$($node.node): $(@($updates).Count) updates"}
(Get-PveNodesAptUpdate -Node pve01).Response.data | Select-Object Package, OldVersion, Versionpve02: 8 updatespve01: 8 updates
Package OldVersion Version------- ---------- -------tzdata 2026b-0+deb12u1 2026c-0+deb12u1liblzma5 5.4.1-1+deb12u1 5.4.1-1+deb12u2xz-utils 5.4.1-1+deb12u1 5.4.1-1+deb12u2The list is the one of the last apt update on the node, the same shown in Node → Updates. It needs
Sys.Modify on the node: see Permissions.
Cluster and quorum
Section titled “Cluster and quorum”(Get-PveClusterStatus).Response.data | Select-Object type, name, online, quorate, nodestype name online quorate nodes---- ---- ------ ------- -----cluster cluster01 1 2node pve02 1node pve01 1Storage
Section titled “Storage”Space used
Section titled “Space used”(Get-PveClusterResources -Type storage).Response.data | Where-Object maxdisk -gt 0 | Sort-Object storage, node | Select-Object node, storage, @{ n = 'usedGB'; e = { [math]::Round($_.disk / 1GB) } }, @{ n = 'sizeGB'; e = { [math]::Round($_.maxdisk / 1GB) } }, @{ n = 'used%'; e = { [math]::Round($_.disk / $_.maxdisk * 100, 1) } }node storage usedGB sizeGB used%---- ------- ------ ------ -----pve01 datapool 1016.00 5068.00 20.00pve02 datapool 8172.00 31690.00 25.80pve01 archive 0.00 4053.00 0.00pve02 archive 1.00 23518.00 0.00A shared storage appears once per node, with the same values.
Snapshots and backups
Section titled “Snapshots and backups”Snapshots older than 30 days
Section titled “Snapshots older than 30 days”$limit = ConvertTo-PveUnixTime (Get-Date).AddDays(-30)foreach ($vm in Get-PveGuest) { (Get-PveGuestSnapshot -VmIdOrName $vm.vmid).Response.data | Where-Object { $_.name -ne 'current' -and $_.snaptime -lt $limit } | Select-Object @{ n = 'vmid'; e = { $vm.vmid } }, name, @{ n = 'taken'; e = { (ConvertFrom-PveUnixTime $_.snaptime).ToLocalTime() } }}vmid name taken---- ---- ----- 105 before-update 30/05/2025 11:03:10current is not a snapshot: it is the running state of the guest, and has no snaptime.
Guests not in any backup job
Section titled “Guests not in any backup job”(Get-PveClusterBackupInfoNotBackedUp).Response.datavmid type name---- ---- ---- 203 qemu test-debian 204 qemu test-debian29999 qemu vdi-testThese are the guests that no backup job of the datacenter includes, the same list as Datacenter → Backup → Guests without backup job.
Failed tasks of the last week
Section titled “Failed tasks of the last week”$since = ConvertTo-PveUnixTime (Get-Date).AddDays(-7)foreach ($node in Get-PveNode) { (Get-PveNodesTasks -Node $node.node -Errors $true -Since $since -Limit 50).Response.data | Select-Object node, type, id, user, status, @{ n = 'start'; e = { (ConvertFrom-PveUnixTime $_.starttime).ToLocalTime() } }}node type id user status start---- ---- -- ---- ------ -----pve01 qmsnapshot 9999 root@pam snapshot name 'before-update' already used 29/09/2026 17:12:32pve01 vncproxy 1007 root@pam connection timed out 28/09/2026 18:25:26-Errors $true keeps only the tasks that failed; status is their error. A token sees only its own
tasks unless it has Sys.Audit on the node.
Users and tokens
Section titled “Users and tokens”Tokens and when they expire
Section titled “Tokens and when they expire”(Get-PveAccessUsers -Full $true).Response.data | ForEach-Object { $user = $_ $_.tokens | Select-Object @{ n = 'user'; e = { $user.userid } }, tokenid, privsep, @{ n = 'expires'; e = { if ($_.expire) { (ConvertFrom-PveUnixTime $_.expire).ToLocalTime() } else { 'never' } } }}user tokenid privsep expires---- ------- ------- -------automation@pve scripts 0 neverroot@pam admin 0 neverWithout -Full $true the users come without their tokens.
Act on guests
Section titled “Act on guests”These change the cluster: they are shown, not run for this page.
Start every guest with a tag
Section titled “Start every guest with a tag”$stopped = (Get-PveGuest -VmIdOrName '@tag-lab' | Where-Object status -eq 'stopped').vmid -join ','if ($stopped) { Start-PveGuest -VmIdOrName $stopped | Wait-PveTaskIsFinish -Timeout 120000 }Start-PveGuest starts every guest of the selection, VMs and
containers, and returns one response per guest; Wait-PveTaskIsFinish waits for each task in turn.
Migrate a VM to another node
Section titled “Migrate a VM to another node”$vm = Get-PveGuest -VmIdOrName web01$r = New-PveNodesQemuMigrate -Node $vm.node -Vmid $vm.vmid -Target pve02 -Online $trueWait-PveTaskIsFinish -Upid $r.Response.data -Timeout 1800000 # up to 30 minutes-Online $true migrates a running VM without stopping it; its disks must be on shared storage, or add
-WithLocalDisks $true. Containers use New-PveNodesLxcMigrate -Target pve02 -Restart $true: a running container is stopped, moved and started again on the target.
Delete snapshots older than 30 days
Section titled “Delete snapshots older than 30 days”$limit = ConvertTo-PveUnixTime (Get-Date).AddDays(-30)foreach ($vm in Get-PveGuest) { (Get-PveGuestSnapshot -VmIdOrName $vm.vmid).Response.data | Where-Object { $_.name -ne 'current' -and $_.snaptime -lt $limit } | ForEach-Object { $r = Remove-PveGuestSnapshot -VmIdOrName $vm.vmid -Snapname $_.name Wait-PveTaskIsFinish -Upid $r.Response.data -Timeout 300000 | Out-Null }}Deleting one snapshot at a time, waiting for each, keeps the storage from queueing many deletions at once. For snapshots on a schedule with automatic retention, see cv4pve-autosnap.