Skip to content

dev stack: keep node_modules out of the shared checkout - #483

Open
runleveldev wants to merge 1 commit into
mainfrom
dev-node-modules-isolation
Open

runleveldev wants to merge 1 commit into
mainfrom
dev-node-modules-isolation

Conversation

@runleveldev

Copy link
Copy Markdown
Collaborator

Problem

The compose dev stack installs node_modules into the bind-mounted checkout, so the host and the stack share one directory. Native modules are compiled against the installing environment's glibc: node:24-trixie (glibc 2.41) builds @vscode/sqlite3 needing GLIBC_2.38, which a typical host can't load (e.g. Rocky 9, glibc 2.34):

Error: /lib64/libm.so.6: version `GLIBC_2.38' not found (required by …/vscode-sqlite3.node)

So every docker compose up breaks host-side tests and tooling, and a host reinstall has to be redone after the next up. The agent's make build (npm ci + npm prune --omit=dev) also strips TypeScript out of the host's agent/node_modules.

Change

  • compose.yml — create-a-container/node_modules, create-a-container/client/node_modules, and agent/node_modules are named volumes layered over the checkout in the node/client services and mounted read-only into proxmox. The host keeps its own node_modules; the stack keeps its own.
  • images/proxmox-ve/create-manager.sh — Proxmox bind mount points are non-recursive (PVE::LXC::__bindmount_do runs mount -o bind), so the Manager LXC's repo mount (mp0) would still show the host's node_modules underneath the volume. The two volumes are now passed to the LXC as their own mount points.
    • The mounts are defined in one list (MANAGER_BIND_MOUNTS) and added by ensure_bind_mounts, which uses the index after the highest one in use. Proxmox mounts mp0, mp1, … in index order (mount_all → foreach_volume), so nested mounts land on top of the repo and never reuse an index another mount depends on.
    • Existing stacks migrate without being recreated: an already-provisioned CT used to exit 0 immediately. It now gets missing mounts added (the CT is stopped and started around the change), and a second run is a no-op.
  • development-workflow.md — documents the host/stack split, the new volumes, and one caveat: running npm ci on the host while the stack is running deletes the directory the volume is mounted on. That detaches the volume from running containers until they restart. npm install is unaffected.

Verification

End-to-end on a fresh stack from this branch (Podman 5.8 in a rootful podman machine). The host node_modules directories held only marker files, so the Manager could only start if it loaded the volumes:

  • node/client installed into the volumes (repeated npm ci into a volume-backed node_modules works on npm 11; no EBUSY); client/dist still written to the checkout.
  • CT 100 got mp0 repo, mp1 create-a-container/node_modules, mp2 agent/node_modules. Inside the CT, both paths are the volumes (396 / 18 entries), not the host markers.
  • container-creator runs nodemon from the volume; @vscode/sqlite3 loads in the CT; Manager HTTPS 200; agent checking in; bootstrap-manager created the site, node, and domain.
  • Host node_modules still contain only their markers after the whole run; checkout clean.
  • Migration path, live: reset a running CT to mp0 only (the state of every current dev stack) and re-ran create-manager.sh. It added mp1/mp2 and restarted the CT; the Manager came back healthy on the volume. A second run was a no-op.
  • The ensure_bind_mounts indexing was also exercised against a stub pct for: a fresh CT, an existing main stack, a Support user-defined shared volumes for containers (#421) #476 stack (which already has mp1 = volumes root → gets mp2/mp3), and an already-migrated CT.

Notes

  • Interaction with Support user-defined shared volumes for containers (#421) #476: that PR adds the volumes root as mp1 directly in create-manager.sh. When it's rebased onto this, its mount should be added to MANAGER_BIND_MOUNTS rather than hard-coding an index. Existing Support user-defined shared volumes for containers (#421) #476 stacks are handled either way (they get mp2/mp3, as tested).
  • Unrelated and not changed here: on hosts that disallow unprivileged binds below 1024 (e.g. ip_unprivileged_port_start=1024 with podman machine), current Podman refuses to start proxmox because of the 127.0.0.1:80/443 mappings. I remapped those locally with an uncommitted override to run the verification.

The compose stack installed node_modules into the bind-mounted checkout, so
the host and the stack shared one directory. Native modules (e.g.
@vscode/sqlite3) are compiled against the installing environment's glibc: the
stack's node:24-trixie (glibc 2.41) produces binaries a typical host (e.g.
Rocky 9, glibc 2.34) cannot load, so every `docker compose up` broke host-side
tests and tooling, and host reinstalls clobbered the stack. The agent's
`make build` (npm ci + npm prune --omit=dev) also stripped TypeScript from the
host's agent/node_modules.

- compose.yml: create-a-container, client, and agent node_modules are named
  volumes layered over the checkout in the node/client services, and mounted
  read-only into the proxmox service. The host keeps its own node_modules.
- create-manager.sh: Proxmox bind mount points are non-recursive
  (`mount -o bind`), so the Manager LXC's repo mount (mp0) would still show the
  host's node_modules underneath. Pass the two node_modules volumes to the LXC
  as their own mount points. Mount points are defined in one list and added by
  ensure_bind_mounts, which takes the index after the highest in use (Proxmox
  mounts in index order, so nested mounts land on top of the repo and never
  reuse another mount's index). Already-provisioned CTs get missing mounts
  added (stopped/started around the change) instead of an early exit, so
  existing dev stacks migrate without being recreated.
- development-workflow.md: document the host/stack split and the caveat that
  host `npm ci` while the stack runs detaches the volume until restart.
Copilot AI balanced review requested due to automatic review settings September 29, 2026 14:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation is coherent and live-verified; only a minor documentation clarification remains.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Separates host and development-stack dependencies to prevent native module ABI conflicts.

Changes:

  • Adds dedicated node_modules volumes.
  • Migrates Manager LXC bind mounts idempotently.
  • Documents dependency isolation and reset behavior.
File Description
compose.yml Defines and mounts isolated dependency volumes.
images/​proxmox-ve/​create-manager.sh Adds and migrates nested LXC bind mounts.
mie-opensource-landing/​docs/​developers/​development-workflow.md Documents dependency-volume behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +98 to +101
`node_modules` with your host. `create-a-container/node_modules`,
`create-a-container/client/node_modules`, and `agent/node_modules` are named
volumes layered over the checkout, and the Manager CT receives them as their own
mount points. The stack installs into those volumes and builds native modules
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants