[PR #18] [CLOSED] Move ephemeral CTs into the PVE VMID >= 10000 range #19

Closed
opened 2026-10-15 12:51:20 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/games-on-whales/LXC2Docker/pull/18
Author: @JBailes
Created: 4/18/2026
Status: ❌ Closed

Base: docker-lxc-daemon ← Head: fix/ephemeral-ct-vmid-range


📝 Commits (2)

  • 230adfc api: advertise OSType + Builder-Version in /_ping
  • 8786034 lxc: ephemeral containers become PVE CTs at VMID >= 10000

📊 Changes

2 files changed (+80 additions, -95 deletions)

View changed files

📝 internal/api/system.go (+2 -0)
📝 internal/lxc/manager.go (+78 -95)

📄 Description

Summary

Two fixes, both from a debug session getting Wolf → Steam → Valheim working end-to-end on a Proxmox host via the daemon.

1. /_ping advertises OSType + Builder-Version

Docker CLI 26.x inspects the /_ping HEAD response's OSType header before executing commands that touch host-path resources. Without the header, it rejected docker create --device /dev/dri/renderD128 with:

docker: unknown server OS: .

--network, --restart, --device-cgroup-rule, and even --device on simpler paths all worked — the gap only bit when a real device node was specified. Setting OSType: linux + Builder-Version: 1 matches what upstream Docker emits and makes every affected command work without a client workaround.

2. Ephemeral containers become PVE CTs at VMID ≥ 10000

Previously "ephemeral" meant a raw-LXC container living in /var/lib/lxc/<id>/ with a ZFS clone at large/lxc-<hash> — invisible to Proxmox tooling, and with unreliable networking because PVE enforces its own netns setup on CTs it knows about and ignores lxc.* passthrough on ones it doesn't. docker run --network=host produced a container with no network interface at all.

The GC also relied on a name-based "session vs support" heuristic (underscore in name = session, no underscore = support) and killed "orphaned support" containers whenever no "session" was running. A plainly-named docker run --name wolf ... got GC'd as orphaned support ~9 seconds after launch, with its PulseAudio sidecar.

New model: ephemeral containers are regular PVE CTs too, just allocated from a reserved high VMID range (≥ 10000) so operators can tell user workloads apart from daemon-managed ephemerals at a glance. Rule:

VMID Meaning GC touches?
0 legacy (non-PVE) ephemeral yes
1 – 9999 user-created permanent CT no
≥ 10000 daemon-managed ephemeral yes

Changes

  • allocateEphemeralVMID() scans /etc/pve/lxc/*.conf and returns the smallest free VMID at or above ephemeralVMIDBase (10000). allocateVMID() (via pvesh nextid) stays for explicitly-requested permanent CTs (gow.pve=true).
  • createEphemeralPVE now uses pct clone --full <template> <ephVMID> --storage <pool>, same shape as createPVEContainer. Rootfs at <pool>:subvol-<vmid>-disk-0; normal /etc/pve/lxc/<vmid>.conf via writePVEConfig.
  • Store record gets the real VMID, so StartContainer / StopContainer / RemoveContainer / containerExists / reconcile flow through the existing pct paths unchanged.
  • gc() loses the session/support split and the orphan-kill logic. New body: skip permanent CTs (0 < VMID < ephemeralVMIDBase) and compose-managed, reap anything else in state exited. Fixes the "Wolf got orphan-killed" regression.

Test plan

  • Verified on a live PVE host: docker run --name wolf ... ghcr.io/games-on-whales/wolf:stable creates /etc/pve/lxc/10000.conf, boots, survives past the 30s GC tick, and stays up under --restart=unless-stopped.
  • End-to-end: Moonlight pairs with Wolf on its LAN IP, Wolf-UI spawns (ephemeral CT 10001), Steam spawns (ephemeral CT 10003), Valheim launches.
  • docker create --device /dev/dri/card0 ghcr.io/games-on-whales/wolf:stable no longer fails with "unknown server OS: .".
  • Fresh clone on a host with no prior 10000+ CTs — allocator picks 10000 for the first ephemeral.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/games-on-whales/LXC2Docker/pull/18 **Author:** [@JBailes](https://github.com/JBailes) **Created:** 4/18/2026 **Status:** ❌ Closed **Base:** `docker-lxc-daemon` ← **Head:** `fix/ephemeral-ct-vmid-range` --- ### 📝 Commits (2) - [`230adfc`](https://github.com/games-on-whales/LXC2Docker/commit/230adfc2aa638aa1ba963031f0e7dc29073a11b4) api: advertise OSType + Builder-Version in /_ping - [`8786034`](https://github.com/games-on-whales/LXC2Docker/commit/8786034ca2a596557efc27897477d53eab62f88b) lxc: ephemeral containers become PVE CTs at VMID >= 10000 ### 📊 Changes **2 files changed** (+80 additions, -95 deletions) <details> <summary>View changed files</summary> 📝 `internal/api/system.go` (+2 -0) 📝 `internal/lxc/manager.go` (+78 -95) </details> ### 📄 Description ## Summary Two fixes, both from a debug session getting Wolf → Steam → Valheim working end-to-end on a Proxmox host via the daemon. ### 1. `/_ping` advertises OSType + Builder-Version Docker CLI 26.x inspects the `/_ping` HEAD response's `OSType` header before executing commands that touch host-path resources. Without the header, it rejected `docker create --device /dev/dri/renderD128` with: ``` docker: unknown server OS: . ``` `--network`, `--restart`, `--device-cgroup-rule`, and even `--device` on simpler paths all worked — the gap only bit when a real device node was specified. Setting `OSType: linux` + `Builder-Version: 1` matches what upstream Docker emits and makes every affected command work without a client workaround. ### 2. Ephemeral containers become PVE CTs at VMID ≥ 10000 Previously "ephemeral" meant a raw-LXC container living in `/var/lib/lxc/<id>/` with a ZFS clone at `large/lxc-<hash>` — invisible to Proxmox tooling, and with unreliable networking because PVE enforces its own netns setup on CTs it knows about and ignores `lxc.*` passthrough on ones it doesn't. `docker run --network=host` produced a container with no network interface at all. The GC also relied on a name-based "session vs support" heuristic (underscore in name = session, no underscore = support) and killed "orphaned support" containers whenever no "session" was running. A plainly-named `docker run --name wolf ...` got GC'd as orphaned support ~9 seconds after launch, with its PulseAudio sidecar. New model: ephemeral containers are regular PVE CTs too, just allocated from a reserved high VMID range (≥ 10000) so operators can tell user workloads apart from daemon-managed ephemerals at a glance. Rule: | VMID | Meaning | GC touches? | |------|---------|-------------| | 0 | legacy (non-PVE) ephemeral | yes | | 1 – 9999 | user-created permanent CT | no | | ≥ 10000 | daemon-managed ephemeral | yes | ### Changes - `allocateEphemeralVMID()` scans `/etc/pve/lxc/*.conf` and returns the smallest free VMID at or above `ephemeralVMIDBase` (10000). `allocateVMID()` (via `pvesh nextid`) stays for explicitly-requested permanent CTs (`gow.pve=true`). - `createEphemeralPVE` now uses `pct clone --full <template> <ephVMID> --storage <pool>`, same shape as `createPVEContainer`. Rootfs at `<pool>:subvol-<vmid>-disk-0`; normal `/etc/pve/lxc/<vmid>.conf` via `writePVEConfig`. - Store record gets the real VMID, so `StartContainer` / `StopContainer` / `RemoveContainer` / `containerExists` / `reconcile` flow through the existing `pct` paths unchanged. - `gc()` loses the session/support split and the orphan-kill logic. New body: skip permanent CTs (`0 < VMID < ephemeralVMIDBase`) and compose-managed, reap anything else in state `exited`. Fixes the "Wolf got orphan-killed" regression. ## Test plan - [x] Verified on a live PVE host: `docker run --name wolf ... ghcr.io/games-on-whales/wolf:stable` creates `/etc/pve/lxc/10000.conf`, boots, survives past the 30s GC tick, and stays up under `--restart=unless-stopped`. - [x] End-to-end: Moonlight pairs with Wolf on its LAN IP, Wolf-UI spawns (ephemeral CT 10001), Steam spawns (ephemeral CT 10003), Valheim launches. - [x] `docker create --device /dev/dri/card0 ghcr.io/games-on-whales/wolf:stable` no longer fails with "unknown server OS: .". - [ ] Fresh clone on a host with no prior 10000+ CTs — allocator picks 10000 for the first ephemeral. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-15 12:51:20 +00:00
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/LXC2Docker#19
No description provided.