Skip to content

Support VDS (Virtual Dedicated Servers): dedicated CPU/RAM templates with no overcommit #195

Description

@v0l

Goal

Offer VDS products alongside the existing VPS line: VMs with dedicated, non-oversubscribed CPU cores and RAM (host CPU passthrough, optionally pinned cores), sold at a premium. Everything below is based on a deep dive of the current code.

Where the codebase stands today

Area Current state Relevance
VmTemplate (lnvps_db/src/model.rs) Has cpu, memory, cpu_mfg/arch/features, cpu_limit (fractional cap), IOPS/net caps, cost_plan_id, region_id Natural place for a dedicated class flag; cpu_limit is the opposite lever (throttling shared VMs)
VmHost load_cpu / load_memory / load_disk are host-wide overcommit factors Dedicated VMs must bypass these; a host cannot express "N cores reserved"
Capacity engine (lnvps_api_common/src/capacity.rs) available_cpu() = cpu * load_cpu - consumed, summed over all VMs uniformly No way to account shared and dedicated consumption differently on one host
Proxmox client (lnvps_api/src/host/proxmox.rs) VMs created with cpu: "kvm64" (from QemuConfig.cpu), supports cpulimit No cputype=host, no affinity (PVE 7.3+ core pinning), no numa, no cpuunits, no hugepages
Libvirt client (lnvps_api/src/host/libvirt.rs) Plain <vcpu>/<memory> XML No <cputune>/<vcpupin>, no host-passthrough CPU mode, no <numatune>
Provisioner / placement Picks host by region + arch/mfg/features + capacity Needs host-class awareness
Pricing Per-template via cost_plan_id; custom pricing per region Dedicated templates can simply carry their own (pricier) cost plans — little work needed

Design

1. Data model (migration)

  • vm_template.dedicated BOOLEAN NOT NULL DEFAULT 0 (or a template_class enum if more classes are expected later).
  • vm_host.host_class SMALLINT NOT NULL DEFAULT 00 = Shared, 1 = Dedicated. Phase 1 keeps the classes exclusive per host: dedicated templates only place on dedicated hosts, whose load_cpu/load_memory are forced to 1.0.
  • (Phase 3, only if mixing classes on one host) vm_core_assignment (vm_id, host_id, core_idx) table recording pinned cores, so the allocator can hand out disjoint core sets and reclaim them on delete.

2. Capacity engine

Phase 1 (host-class separation) keeps capacity.rs almost unchanged:

  • list_available_vm_templates / placement filter hosts by host.host_class == template.class.
  • Dedicated hosts run with load factors 1.0, so the existing arithmetic already yields no-overcommit accounting.
  • Guard: reject load_cpu/load_memory != 1.0 on dedicated hosts in admin API validation (or clamp).

Mixing shared + dedicated on one host is much harder (shared VMs still steal cycles unless every dedicated VM is pinned and shared VMs are confined to the complementary core set via affinity/cgroup cpusets). Defer.

3. Proxmox host client

For VMs from dedicated templates:

  • cpu: host (CPU passthrough) instead of the configured kvm64 — expose as QemuConfig.dedicated_cpu or derive from the template flag.
  • cpuunits: 10000 so dedicated VMs win scheduling against any co-located workload.
  • Optional core pinning via the PVE affinity: <core-list> config key (PVE >= 7.3), driven by the core allocator (phase 5).
  • Optional: numa: 1 + hugepages for large-memory templates.
  • Note: cputype=host breaks live-migration between heterogeneous hosts — acceptable for VDS (document it), and cpu_mfg/cpu_features matching already exists to keep templates on homogeneous hardware.

4. Libvirt host client (parity, later)

  • <cpu mode="host-passthrough"/>, <cputune><vcpupin vcpu="N" cpuset="M"/></cputune>, <numatune> as needed.

5. API surface

  • User API: dedicated: bool on template/VM responses so the frontend can badge VDS plans; capacity-aware template listing already flows through list_available_vm_templates.
  • Admin API: CRUD for the template flag + host class; validation as above.
  • ApiVmTemplate, admin models, and E2E fixtures updated.

6. Explicitly out of scope (follow-ups)

  • Mixed shared/dedicated hosts with core-set partitioning (needs vm_core_assignment + confining shared VMs to the complement).
  • Converting an existing shared VM to VDS in place (would be a migration/upgrade flow like Transfer VM #178/VM addons #187).
  • Dedicated disk spindles/NVMe namespaces — current VmHostDisk + IOPS caps are considered sufficient.

Suggested phasing (one PR each)

  1. DB + admin API: migration, dedicated template flag, host_class, validation. (S)
  2. Capacity/placement: class-aware host filtering + template availability. (S)
  3. Proxmox: cpu: host + cpuunits for dedicated VMs, config plumbed through FullVmInfo. (M)
  4. User API exposure + E2E test covering provision-on-dedicated-host. (S)
  5. Core pinning allocator (affinity) — optional hardening. (M)
  6. Libvirt parity. (M)

Acceptance criteria (phases 1-4)

  • A template flagged dedicated only ever places on a Dedicated-class host, with 1:1 CPU/RAM accounting (no overcommit).
  • Proxmox VMs from dedicated templates run with host CPU passthrough and elevated cpuunits.
  • Shared templates never place on dedicated hosts and vice versa.
  • Template/VM API responses expose the dedicated flag.

Metadata

Metadata

Assignees

No one assigned

    Labels

    apiUser-facing or admin API changesdatabaseMigration or schema changesenhancementNew feature or requesthost:proxmox

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions