Files
proxmox-scripts/README.md
T
l.kirchner a397ade6e1
CI / Shell-Lint (bash -n, source-check, Validierungs-Tests) (push) Successful in 1s
Merge pull request 'K-114: Input-Validierung in build.func + Mini-CI' (#5) from k114/input-validierung-mini-ci into main
K-114: Input-Validierung in build.func + Mini-CI (PR #5) — SSH-Root-Login-Prompt, locale-Fix, runs-on homelab
2026-06-12 16:40:37 +02:00

5.9 KiB

luki-net / proxmox-scripts

Community-script-style installers for LXC services in the luki-net Proxmox VE homelab. Each script creates an unprivileged Debian 12 LXC and installs one specific app — sensible defaults, interactive prompts, env-overridable for non-interactive runs.

Inspired by community-scripts/ProxmoxVE, but minimal, self-hosted and tailored to this stack.

Script catalog

App What it provisions Status
devpi Private PyPI cache/mirror — saves time on CUDA/torch rebuilds stable
webapp Next.js site with deploy-as-code via a repo-scoped Gitea Actions runner (host mode, no inbound port) stable
nexus App LXC for nexus (Family Knowledge Hub): host-mode runner (label nexus, CI + deploy), service skeleton, /opt/nexus layout. Runtime is provisioned/extended via install/nexus-runtime.sh (idempotent, re-runnable) in production
nexus-db PostgreSQL 16 + pgvector for nexus — least-privilege role, pg_hba allowlist (only the nexus LXC), DSN handed over via credentials file in production
authentik Central homelab IdP (official Docker Compose via LXC nesting) — headless bootstrap admin and API token for agent-driven blueprint configuration, blueprints mount, permanent auth domain (WebAuthn RP-ID) in production
runner General instance-wide Gitea Actions runner (label homelab) — Docker for throwaway CI test containers, deliberately no sudoers/deploy rights 🔄 in review (PR #6)

Run any one-liner on a Proxmox VE host as root:

bash -c "$(curl -fsSL https://gitea.luki-net.org/luki-net/proxmox-scripts/raw/branch/main/ct/<app>.sh)"

Usage

Interactive prompts cover CTID, hostname, disk, RAM, CPU, bridge/VLAN, storage, IP/gateway/DNS — plus app-specific values. Defaults are sane. Non-interactive override via env vars:

CTID=200 HOSTNAME=devpi DISK_SIZE=30 RAM=4096 CORES=4 IPCFG=dhcp \
  bash -c "$(curl -fsSL https://gitea.luki-net.org/luki-net/proxmox-scripts/raw/branch/main/ct/devpi.sh)"

All defaults (DEFAULT_HOSTNAME, DEFAULT_DISK, …) and app config values are settable per call via env vars.

The pattern

Two files per app, both sourcing the shared libs via curl:

  • ct/<app>.sh runs on the PVE host: prompts → unprivileged LXC → pushes a config env file into the container → bootstraps the installer. Apps that need Docker (authentik, runner) enable nesting+keyctl automatically.
  • install/<app>-install.sh runs inside the LXC: packages, unprivileged app user, secrets generated on-host (never printed), systemd units, a /root/<app>.credentials notes file — then shreds the bootstrap env. Idempotent where it matters: re-runs skip what exists.

Shared libs: lib/build.func (host-side: prompts, LXC create, bootstrap) and lib/install.func (in-container: apt, users, systemd, http-wait).

Security conventions

  • Unprivileged LXCs only; app processes run as dedicated system users.
  • Least privilege everywhere: narrow sudoers (exact-match commands — sudoers compares verbatim incl. arguments), DB allowlists, LAN-only binds for inference ports.
  • Secrets are generated on the target host and live in 0600 files — never in the repo, the wiki or chat logs.
  • Runner separation: the nexus runner is repo-scoped and lives on the production LXC because it holds deploy rights; the general runner LXC is instance-wide because it holds none. Don't mix these scopes.

Contributing

All changes go through a pull request with cross-review (Claude Code ↔ Codex, or a human) — no direct pushes to main. This rule exists because of two real incidents where an un-reviewed script shipped a missing source build.func (see wiki → Lessons). CI (bash -n, source-check, validation suite) is being introduced with PR #5 and runs on the homelab runner.

How to add a script: docs/adding-a-script.md.

Contributing (verbindlich seit K-114)

Alle Änderungen laufen als PR mit Cross-Review — keine Direkt-Commits auf main. Hintergrund: Der „build.func nicht gesourct"-Bug hat es einmal bis in die Produktion geschafft (authentik-Anlage), während dieselbe Fehlerklasse im nexus-db-PR vom Review gefangen wurde. Die CI (.gitea/workflows/ci.yml, Runner-Label homelab — instanzweiter Runner aus ct/runner.sh) erzwingt zusätzlich: bash -n über alle Scripts, „jedes ct/*.sh sourct build.func" (tests/check_ct_source.sh) und die Validierungs-Unit-Tests (tests/test_validation.sh).

Eingaben in prompt_lxc_config sind validiert (Ziffern-Checks, IP/CIDR/Gateway-Format, Re-Prompt bei unsichtbaren Steuer-/Non-ASCII-Zeichen — Lesson vom 2026-06-11). Neue App-Prompts bitte über prompt_validated/require_valid aus lib/build.func bauen statt nacktem read.

Repo layout

.
├── ct/                 # Host-side scripts, one per app
├── install/            # In-container installers (+ nexus-runtime.sh re-provisioner)
├── lib/
│   ├── build.func      # Shared host-side helpers (prompts, LXC create, bootstrap)
│   └── install.func    # Shared in-container helpers (apt, systemd, users, http-wait)
├── docs/
│   └── adding-a-script.md
├── README.md
└── LICENSE
  • nexus-hub — Family Knowledge Hub (main consumer of nexus/nexus-db/authentik/runner)
  • Wiki — per-app runbook pointers, conventions, lessons learned

License

MIT