Container Code Companion by Parallax Group is a Proxmox LXC provisioner that creates a lean, headless dev workstation for Claude Code, OpenAI Codex, and Gemini CLI. No desktop GUI, no Docker, no bloat. Everything is installed into a browser-accessible, CLI-first Linux container.
Parallax Group: pllx.group
Built on Proxmox VE — free, open-source server virtualization (community edition).
bash <(curl -fsSL https://raw.githubusercontent.com/oculus-pllx/CCC/main/ccc-bootstrap.sh)- Ubuntu 26.04 LTS or Debian 13 (Trixie) in a Proxmox LXC container
- Non-root
claude-codeuser with passwordless sudo - Full dev stack — Node.js 22 LTS, Python 3, Go, Rust, build essentials
- Claude Code native install, all tools pre-approved, zero permission prompts, statusline active
- OpenAI Codex and Gemini-ready config from the shared
oculus-configsrepo - First-login onboarding —
ccc-onboarding/ccc-setupfor git identity, SSH keygen, GitHub - Three update paths — OS packages, Container Code Companion tooling, and shared agent configs are updated separately
- Health check —
ccc-doctorchecks network, runtimes, services, disk - code-server / VS Code Web on port 8080 — multi-terminal tabs, file editor, welcome guide
- Container Code Companion UI on port 9090 — native headless management dashboard with Parallax branding, mobile drawer navigation, 7 accent color presets, optional CRT display effects, system overview, services, logs, networking, accounts, files, notes, terminal, projects, updates, app catalog, map drives, provider configs, and GitHub SSH key management
- Native terminal tabs — browser PTY sessions backed by Go, xterm.js, and tmux-capable shells
- Custom statusline at
~/.claude/bin/statusline-command.sh ccchelp command — full reference available on every login- SSH hardened — root login disabled, key auth ready
- IPv6 disabled — avoids apt/curl failures in containers without IPv6 routing
- Optional Proxmox HA — register with
ha-managerat provision time (cluster only) - oculus-configs — shared Claude/Codex/Gemini config, rules, skills, and templates synced from oculus-configs
- Zero Docker — pure native toolchain, minimal overhead
- Weekly Container Code Companion tooling updates — Sundays 3 AM ET; OS and agent config updates stay explicit
- Proxmox VE 8.x+ host
- Run as root on the Proxmox host
- Internet access from the host and container
- Recommended: 4 vCPU / 10GB RAM / 30GB disk
SSH into your Proxmox host as root and run:
bash <(curl -fsSL https://raw.githubusercontent.com/oculus-pllx/CCC/main/ccc-bootstrap.sh)Or download and inspect first:
curl -fsSL https://raw.githubusercontent.com/oculus-pllx/CCC/main/ccc-bootstrap.sh \
-o /tmp/ccc.sh && bash /tmp/ccc.shThe script is interactive. You'll be prompted for:
| Prompt | Default | Notes |
|---|---|---|
| Container ID | next available | Auto-detected via pvesh |
| Hostname | ccc-dev |
|
| Username | claude-code |
Your working user — used for SSH, Container Code Companion, code-server |
| Root password | — | Temporary, setup only |
| User password | — | Password for your chosen username |
| code-server password | codeserver |
Web VS Code UI |
| CPU cores | 4 |
|
| RAM | 10240 MB |
|
| Swap | 2048 MB |
|
| Disk | 30 GB |
|
| Storage | auto-detected | Active rootdir-capable pools listed; defaults to local-lvm if present, else first found |
| IP | dhcp |
Or x.x.x.x/xx for static — CIDR prefix required, re-prompts if missing |
| Gateway | — | Required for static IP — plain IPv4, re-prompts if missing or has CIDR |
| DNS | 1.1.1.1 |
Plain IPv4, re-prompts on invalid format |
| SSH public key | optional | Installed for chosen username |
| High Availability | — | Cluster only — lists HA groups, optional group selection |
OS choice is the first prompt — Ubuntu 26.04 LTS (default) or Debian 13 (Trixie). Useful fallback when Ubuntu/Canonical is having issues.
After OS selection, the script checks:
- Canonical status API (
status.canonical.com) — Ubuntu only, warns on active outages, suggests switching to Debian on major/critical - Direct reachability of the apt mirror (
archive.ubuntu.comordeb.debian.org) — prompts to abort if unreachable
Provisioning takes 10–15 minutes. Each of the 29 steps prints [N/29] progress, and the host prints elapsed time every 30 seconds so you can tell it's still running.
# 1. SSH in as the working user
ssh claude-code@<container-ip>
# 2. Run first-login onboarding (auto-prompts on first interactive login)
ccc-onboarding
# 3. Authenticate Claude Code
claude
# 4. Sync shared Claude/Codex/Gemini configs from oculus-configs
ccc-sync-agent-configs
# 5. Install Playwright + headless Chromium (optional, takes 5–15 min)
ccc-install-playwright
# 6. Install Codex CLI or jCodeMunch MCP (optional)
ccc-install-codex
ccc-install-jcodemunch
# 7. Full help and command reference
ccc- Node.js 22 LTS — npm, typescript, ts-node, tsx
- Python 3 — pip (
--break-system-packages), venv - Go (latest) — via official tarball, on PATH
- Rust (latest) — via rustup, installed for claude-code user
- Search — ripgrep (
rg), fd (fdfind), fzf, bat (batcat) - Data — jq, yq (mikefarah Go binary), sqlite3
- GitHub CLI — official
ghpackage fromcli.github.com - Codex sandboxing — bubblewrap (
bwrap) installed for Codex sandbox prerequisites - DB clients — psql, redis-cli
- Env — direnv (per-directory
.envrc) - Terminal — tmux, screen, nano, vim, htop
- Build — gcc, clang, make, cmake, pkg-config, autoconf
- Redis — server available, disabled at boot:
sudo systemctl start redis-server
- All permissions pre-approved —
Bash(*),Read(*),Write(*),Edit(*),WebFetch(*),WebSearch(*),Task(*),mcp__* - Agent teams enabled
- Extended thinking always on
- 64k output tokens
- Remote control enabled
- Config at
~/.claude/settings.json
- Claude —
~/.claude/CLAUDE.md,~/.claude/rules/, and MCP template fromoculus-configs - Codex —
~/.codex/AGENTS.mdand optional~/.codex/skills/ - Gemini —
~/.gemini/GEMINI.mdand optional~/.gemini/skills/ - Templates — copied from
oculus-configs/templates/into~/Templates/ - Sync manually with
ccc-sync-agent-configs
Python, Go, Rust Analyzer, Prettier, GitLens, TypeScript Next, Playwright, Vitest Explorer, YAML, TOML, JSON
Open http://<container-ip>:9090 after provisioning and sign in with the working user and password you entered during install.
The native UI is built into the Go service, not Cockpit and not a Node dashboard. It currently includes:
- Overview — host, IP, uptime, services, projects, resource gauges, update status, and recent logs
- Updates — separate App and OS tabs; App updates stream
ccc-self-updateoutput and reconnect after service restart - App Catalog — install/update common workstation tools: Node.js, Go, Python, uv, Playwright, Codex, Claude Code, Gemini CLI, GitHub CLI, bubblewrap, ripgrep, jq, fzf, build-essential, and Aider
- Files — browse directories, open/edit text files, create files/folders, rename, and delete
- Map Drives — CIFS mount helper with LXC/Proxmox guidance for permission-denied mount failures
- Projects — create projects from templates, initialize git, open in Files, open in code-server, rename, and delete
- Terminal — browser PTY tabs backed by xterm.js, adjustable terminal height, and tmux quick actions
- Notes — persistent notes stored in the workstation home directory
- Accounts — create users, change passwords, shells, groups, and delete users
- Logs, Network, Services — inspect service state, live network activity, and system logs; network configuration changes should be made from the Proxmox side for LXC containers
- Provider Configs — edit Claude, Codex, Gemini, and MCP config files inline
- GitHub — copy the public key, test GitHub SSH access, and generate a new SSH key
- Settings — theme swatches, editable header message, time/location, mobile-friendly controls, and CRT display effects
Display effects are local browser preferences. Monitor flicker is enabled by default; sync drift can be enabled from Settings.
The original GUI punchlist is implemented and the current build is functional for daily workstation use. The remaining work should come from fresh field-testing notes, not the original cleanup list.
Current state:
- Bootstrap provisions the LXC, native UI, code-server, shell helpers, update scripts, and agent config sync.
- Native UI replaces the old Cockpit-style/backend remnants.
- App and OS updates are separated.
- App Catalog can query installed tools and run install/update actions.
- Mobile navigation uses a collapsible drawer.
- GitHub SSH key workflow is ordered copy, test, generate.
- Map Drives documents the Proxmox/LXC mount limitation and reports CIFS permission failures clearly.
The ccc command prints the full reference. Quick shortcuts:
# Maintenance
ccc-onboarding # first-login wizard: git identity, SSH key, GitHub
ccc-setup # same wizard, safe to re-run
ccc-update-status # show installed vs GitHub provisioner version
ccc-self-update # update Container Code Companion tooling from GitHub
ccc-update # update Container Code Companion tooling + app CLIs
ccc-os-update # update OS packages with apt
ccc-sync-agent-configs # update Claude/Codex/Gemini configs from oculus-configs
ccc-doctor # health check: network, runtimes, services, disk
ccc-install-playwright # install Playwright + headless Chromium (optional)
ccc-install-codex # install OpenAI Codex CLI (optional)
ccc-install-jcodemunch # install jCodeMunch MCP — 95% token reduction (optional)
# Git
gs # git status
gl # git log --oneline -20
gd # git diff
ga # git add -A
gc # git commit -m
gp # git push
# Dev
py # python3
ll # ls -lah
# Services
sudo systemctl status code-server@claude-code
sudo systemctl restart code-server@claude-code
sudo systemctl start redis-server
sudo systemctl status container-code-companion.service
sudo systemctl restart container-code-companion.serviceA default statusline script is installed at ~/.claude/bin/statusline-command.sh.
Output format:
claude-code@ccc-dev:~/projects (main) [sonnet-4 | think] [ctx:42%] 3:14pm
To replace with your own:
cp ~/my-statusline.sh ~/.claude/bin/statusline-command.sh
chmod +x ~/.claude/bin/statusline-command.shTo test:
echo '{"model":{"id":"claude-sonnet-4"},"thinking":{"enabled":true}}' \
| ~/.claude/bin/statusline-command.shContainer Code Companion separates updates so OS packages, workstation tooling, and shared agent behavior can move independently.
sudo ccc-os-update # OS packages only: apt update/upgrade/autoremove/clean
ccc-update-status # show installed vs GitHub provisioner version
sudo ccc-self-update # Container Code Companion tooling: commands, MOTD, native UI service
sudo ccc-sync-agent-configs # shared Claude/Codex/Gemini config from oculus-configs
ccc-update # convenience: tooling + app CLI updates, no apt upgrade
claude update # Claude Code onlyccc-update-status shows the installed provisioner commit, latest GitHub commit, behind count, and recent commits. ccc-self-update uses the GitHub raw URL first, then falls back to cloning the configured repo. Override CCC_SELF_UPDATE_REPO, CCC_SELF_UPDATE_REF, or CCC_SELF_UPDATE_SCRIPT in /etc/ccc/config for forks or private repos.
ccc-self-update can be run from the CLI or triggered from the native Updates page in the GUI. The GUI streams live build output via SSE and automatically reconnects after the service restarts. A successful tooling update records /etc/ccc/version; a failed build exits non-zero and leaves the build error in the log.
ccc-sync-agent-configs pulls /opt/oculus-configs and re-copies managed Claude, Codex, Gemini, and template files. It does not run the oculus-configs installer, does not install configure.py, and does not add another web UI/service.
From your Proxmox host:
pct stop <CT_ID>
pct destroy <CT_ID>Ubuntu 26.04 template not found
pveam update
pveam available --section system | grep ubuntu-26If still missing, check that your Proxmox host can reach download.proxmox.com.
code-server not loading
pct exec <CT_ID> -- systemctl status code-server@claude-code
pct exec <CT_ID> -- journalctl -u code-server@claude-code -n 50Playwright (not installed at provision time) Playwright is skipped during provisioning — Chromium download hangs in LXC. Install manually after first login:
ccc-install-playwrightClaude Code binary not found after provision
# Inside the container:
find /home/claude-code -name "claude" -type f 2>/dev/null
# Then symlink manually:
sudo ln -sf <found-path> /usr/local/bin/claudeStorage pool name mismatch
Storage pools are auto-detected via pvesm status --content rootdir. The prompt lists available pools and defaults to local-lvm if present, else the first found. If detection returns nothing, override manually — run pvesm status on the host to see pool names.
Static IP: gateway required If you enter a static IP, you must also enter a gateway. DHCP has no such requirement.
apt fails with IPv6 / "Network is unreachable"
IPv6 is disabled inside the container via sysctl at provision start, and apt is forced to IPv4 via /etc/apt/apt.conf.d/99force-ipv4. If you see IPv6 errors in an existing container:
# Inside the container:
echo 'Acquire::ForceIPv4 "true";' | sudo tee /etc/apt/apt.conf.d/99force-ipv4Ubuntu infrastructure down Check https://status.canonical.com/ — the script checks this automatically after OS selection. If Ubuntu is down, re-run and select option 2 (Debian 13) to bypass Canonical entirely.
HA registration failed Add manually from the Proxmox host:
ha-manager add ct:<CT_ID> --state started --group <group>Container Code Companion UI not loading (port 9090)
pct exec <CT_ID> -- systemctl status container-code-companion.service
pct exec <CT_ID> -- systemctl restart container-code-companion.serviceIf container-code-companion.service cannot bind port 9090, check what owns the port:
pct exec <CT_ID> -- ss -ltnp | grep ':9090'Older CCC installers used a standalone Node dashboard on port 9090. Container Code Companion does not use that service. Remove the legacy service/process, then start the native UI:
pct exec <CT_ID> -- systemctl disable --now ccc-dashboard cockpit.socket cockpit.service
pct exec <CT_ID> -- rm -f /etc/systemd/system/ccc-dashboard.service
pct exec <CT_ID> -- systemctl daemon-reload
pct exec <CT_ID> -- systemctl restart container-code-companion.serviceOpen http://<container-ip>:9090 and sign in with the workstation username and the user password entered during install. The service stores those credentials in /etc/container-code-companion/env so the native UI and LXC user stay aligned.
Self-update fails during "Building Container Code Companion binary" The failing compiler output is the real error. Inspect the log:
sudo tail -160 /var/log/ccc-self-update.logThen rerun:
sudo ccc-self-update
ccc-update-statusMap Drives fails with mount: /mnt/share: permission denied
This usually means the LXC container is not allowed to perform CIFS mounts. The GUI can call sudo mount, but Proxmox controls whether the container has the required mount capability.
Recommended options:
- Mount the SMB/CIFS share on the Proxmox host and bind-mount it into the container.
- Or update the LXC configuration on the Proxmox side to allow the needed mount behavior.
If the error mentions unknown filesystem type or bad option, confirm cifs-utils is installed inside the container.
- Root login is disabled. Use
ssh claude-code@<ip>. - The Ubuntu 26.04 LXC template is auto-resolved via
pveam— runpveam updateon your Proxmox host if it can't be found. yqis the mikefarah Go binary, not the apt Python wrapper.- Redis server is installed but disabled at boot. Start it when tests need it.
- Rust is installed twice (root + claude-code user). Root install is a known cleanup candidate.
PRs welcome. Keep the design values:
- No Docker — native toolchain only
- Everything provisioned at container creation time, not lazily
- Single-file installer — the whole script must be self-contained
- Default prompts should work for a TrueNAS-backed Proxmox homelab
To test changes: provision a throwaway container, run through First Steps, verify ccc output and code-server load.
Copyright 2026 Parallax Group.
MIT — use, modify, fork freely.