The Problem
Provisioning Linux Containers (LXC) on Proxmox VE requires repetitive manual steps and ad‑hoc scripts. Teams that rely on a monorepo for all container definitions struggle to keep the runtime state in sync with source control, leading to drift, undocumented changes, and fragile bootstrap processes.
What This Does
Proxmox‑GitOps implements a self‑contained GitOps pipeline that treats the repository itself as the single source of truth for LXC lifecycles. The top‑level local/Dockerfile builds a deterministic container image; local/run.sh launches it, invoking the Ruby‑based orchestration layer found under config/recipes/ and libs/. Core definitions live in base/roles/ (e.g., base/roles/container/tasks/create.rb) and are reused by library modules such as libs/proxy/ and libs/assistant/. The README.md and docs/ folder provide usage guidance and visualisation of the pipeline.
How It Is Wired
Entry point – local/run.sh
#!/bin/sh
docker build -t proxmox‑gitops:dev local/
docker run --rm proxmox‑gitops:dev
The script builds the image defined in local/Dockerfile and runs it. No explicit command‑line arguments are required; the container’s default CMD (implicit Ruby entry) starts the orchestration.
Container start – Dockerfile → CMD (Ruby interpreter) local/Dockerfile installs Ruby, copies the entire repo, and sets the working directory to /app. The image’s default command executes ruby config/recipes/default.rb, which loads the core orchestration framework.
Core orchestration – config/recipes/default.rb → config/libraries/* default.rb requires the shared libraries (config/libraries/common.rb, config/libraries/constants.rb) and then iterates over recipe classes defined in config/recipes/. Each recipe (e.g., config/recipes/deploy.rb, config/recipes/repo/init.rb) encapsulates a stage of the pipeline: Git submodule resolution, container creation, environment injection, and final health checks.
Effectors – base/roles/* and libs/*
base/roles/container/tasks/create.rbinvokes Proxmox CLI (pct create …) to materialize an LXC.libs/proxy/recipes/default.rbrenderslibs/proxy/templates/Caddyfile.erband writes it into the container’s filesystem.libs/assistant/recipes/default.rbpopulatesassistant/container.envfor runtime configuration.
Output – Files written under the container’s root filesystem and optional Git pushes performed by config/recipes/repo/push.rb. No external database is touched; all state is reflected in the Proxmox host and the repository itself.
Hot spots –
base/roles/base/files/profile.sh,config/libraries/default.rb, andconfig/libraries/env.rbcontain high branching density (≈37 branches/88 lines).- Duplicate 6‑line blocks appear in
config/recipes/repo/init.rbandconfig/recipes/task.rb. These modules have the widest blast radius because they are invoked early in every pipeline run.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/Proxmox-GitOps
cd Proxmox-GitOps
# Build and run the local Docker environment
chmod +x local/run.sh
./local/run.sh
Configuration – Edit local/config.json to point at your Proxmox API endpoint and credentials (the file is already present; its schema is consumed by config/libraries/clients.rb). No additional environment variables are required.
Running a specific pipeline – Modify config/recipes/default.rb to require only the desired recipe files (e.g., comment out require_relative 'deploy' to skip deployment).
Real‑World Use
A SaaS provider stores each microservice’s LXC definition as a submodule under libs/. When a new version is merged, the CI triggers local/run.sh; the pipeline pulls the submodule, creates a fresh container on the Proxmox cluster, applies the proxy Caddy configuration, and pushes the updated repo state back to Git. The entire rollout is repeatable with a single Docker command.
Code Health & Issues
- High – Add a test suite – 40 source files, no test files.
- Medium – Pin Docker base image –
local/Dockerfileuses mutable tagdebian:trixie. - Medium – Move large binaries –
docs/demo.gif(9.2 MiB) exceeds typical repo size. - Medium – Add non‑root USER – Dockerfile lacks a
USERdirective. - Low – Set workflow timeout –
.github/workflows/build.ymldeclares notimeout-minutes.
Additional observations: CI is present via GitHub Actions; a LICENSE file exists; no lockfile (e.g., Gemfile.lock) is committed; no secrets were detected.
The Bottom Line
Proxmox‑GitOps delivers a concrete, container‑based GitOps loop for LXC provisioning on Proxmox, with clear separation between core recipes and reusable library modules. The lack of automated tests and some Docker hygiene gaps limit confidence for production use; addressing those will make the framework more maintainable for teams that need repeatable container bootstrapping. Suitable for engineers comfortable with Ruby/Chef‑style code who can extend the existing recipes.