Build a QCOW2 image locally with QEMU/KVM + autoinstall, then provision
with the existing Ansible playbook. Allows testing changes locally before
deploying to production. Outputs a ready-to-upload image for hosting providers.
Usage: cp server.pkrvars.hcl.example server.pkrvars.hcl, fill in values,
then run ./build.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Disable unnecessary kernel modules to reduce attack surface: algif_aead
(Copy Fail), unused filesystems, DMA vectors, unused network protocols,
and USB storage. Rebuild initramfs on change to apply at boot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove opencode; install nvm, Claude Code, and pi-coding-agent for aicoder
- Add muxplex as web terminal service behind Pangolin (auth: none, port 8088)
- Add JuiceFS (Docker container with FUSE) backed by S3 + Redis for Nextcloud storage
- Add Nextcloud + MariaDB with JuiceFS mount via depends_on chain
- Add autoheal container to restart unhealthy services (covers stale FUSE mounts)
- Add SSH key for aicoder user, uv for aicoder, cron cleanup script
- Pin images: major for stable projects, minor for Pangolin/Gerbil
- Query JuiceFS S3 credentials and cache size during init
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dedicated aicoder user for AI coding tools. OpenCode and Claude Code
installed per-user, service runs from ~/codeprojects. Also fixes
forgejo-shell and SSH passthrough ordering in playbook.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docker exec needs -u git to avoid running as root (Forgejo refuses root),
and the config path inside the container is /data/gitea/conf/app.ini.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automated server provisioning with Pangolin reverse proxy, Forgejo git
server with SSH passthrough, and OpenCode dev environment. Includes
server hardening (UFW, fail2ban, SSH lockdown), Docker, Rust, Python/uv,
and unattended security upgrades.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>