Skip to main content
Development preview — this is not a live application; it is still being built.
ProxmoxVEx
↑ ↓ to navigate Enter to open

Deploying ProxmoxVEx — resources & sizing

docker-compose.yml ships recommended resource bounds so the app can't

docker-compose.yml ships recommended resource bounds so the app can’t OOM or starve its neighbors — and neighbors can’t starve it.

Shipped defaults

Service CPU limit Memory limit Memory reservation
proxmoxvex 2.0 2G 512M
postgres 4.0 2G 256M
  • limits cap worst-case usage — a runaway query loop or a huge API response can’t take the whole host.
  • reservations (k8s: requests) reserve baseline capacity — a busy host won’t evict the app below its floor.

The compose file also sets shm_size: 256m on postgres: parallel workers and parallel VACUUM allocate dynamic shared-memory (DSM) segments under /dev/shm, and the docker default of 64MB fails with could not resize shared memory segment on large tables. The 4.0 CPU limit reflects co-hosted databases — the evlog app’s dashboard issues ~14 concurrent aggregate queries per page load, which serialized to ~2.8s of wall time on a single core. For an install where postgres serves only this app, 1.0/1G remains adequate (that is still what docker-compose.template.yml ships).

Right-sizing the app container

The 2G/512M default covers a typical install (a few clusters, UI + API traffic, background schedulers). Scale memory for:

  • P2V/V2V conversion jobs — add ~512M per concurrent conversion. Conversions stream disk images through the app; two parallel jobs → ~3G limit.
  • Large fleets — every connected cluster holds a live manager session and RRD caches; expect roughly +100M per ~5 additional clusters.
  • Report/export bursts — the audit-integrity walk and large exports run inside request workers; keep ~256M headroom over observed p95 RSS.

Scale CPU for concurrent conversion/backup transfers and busy websocket consoles — 2.0 handles the UI easily; add cores for heavy parallel guest actions rather than for web traffic (the gevent server is IO-bound, not CPU-bound).

Kubernetes equivalent

resources:
  requests: { memory: 512Mi, cpu: 250m }
  limits:   { memory: 2Gi,   cpu: "2" }

Verifying limits under load

scripts/load-tests.sh runs the k6 suite against a running instance — with limits applied (compose honors deploy.resources under the compose spec), watch docker stats proxmoxvex during the run: steady-state RSS should stay well under the limit and the container must not OOM-restart. If it does, raise memory first — CPU pressure shows as latency, memory pressure shows as restarts.

Read-only root filesystem (hardened deployments)

The compose template runs the app with read_only: true — a compromised process cannot persist inside the image. Every write path is an explicit mount, so verify all of them exist before removing mounts:

  • proxmoxvex_config:/app/config — settings, keys, db backups
  • proxmoxvex_logs:/app/logs — rotating log files
  • proxmoxvex_plugins:/app/plugins — plugin install/update state. A fresh volume starts empty and is seeded from the image’s /app/plugins-seed; on every later boot the shipped plugin dirs are reconciled with the image copy — new plugin dirs are copied in, changed files are synced byte-for-byte, and files the image no longer ships are pruned — while plugin-declared mutable_paths state, __pycache__, and integrity.json are preserved, and installed dirs reporting a newer manifest version are never downgraded (paid plugins arrive via license-server artifacts, so the seed never contains them). The catalog metadata file directory.json is likewise app-owned and refreshed from the seed on every boot so image upgrades can’t leave a stale catalog (retired paid plugins would otherwise keep resurfacing in /api/plugins as undeletable “download” entries).
  • tmpfs /tmp and tmpfs /run — scratch for tempfiles and the ssh_pool socket dir

At startup the app runs a writable-path check and logs [STARTUP] ... is not writable naming any missing mount — check docker logs if a hardened deployment misbehaves. App updates on read-only deployments go through image pulls (scripts/deploy-rolling.sh), not in-place writes.

Was this page helpful?

ProxmoxVEx Source-Available License
Checking status…

© 2025-2026 ProxmoxVEx contributors