--- type: action id: action.kontra-day.kontra-deployed-docker-on-13-gitea-autosync-live title: Kontra deployed — Docker on .13, Gitea autosync live status: active source_of_truth: false freshness: current last_reviewed: 2026-09-08 tags: - pack/kontra-day - agent-memory/action - kontra - docker - deploy - autosync - gitea - caddy - nixos - golang --- # Kontra deployed — Docker on .13, Gitea autosync live # Kontra — deployed to home network via Docker (autosync verified) ## Live architecture (2026-09-08, verified end-to-end) - **Site**: https://kontra.lab.audasmedia.com.au (and admin.kontra.lab.audasmedia.com.au/web/admin/ = Decap) - **Chain**: master Caddy (Docker on .35, /Docker/Containers/caddy/Caddyfile) → `reverse_proxy 192.168.20.13:8600` → **Kontra Docker container** on .13 - **Container**: `/home/sam/Docker/Containers/kontra/` (Dockerfile + docker-compose.yml + entrypoint.sh). Image `kontra-kontra`, port 8600:8600. Mounts `/var/www/kontra_day/content` (rw) + `/home/sam/.ssh/id_ed25519`→/root/.ssh (ro). - **Content origin**: Gitea `sam/kontra-content` (.35). .13 clone at /var/www/kontra_day/content (mounted into container). - **App repo**: Gitea `sam/kontra` (branch main, SSH remote from .27). ## Autosync (the hard-won piece) entrypoint.sh: `git pull --ff-only origin main` every 30s; on HEAD change, `kill $SERVER_PID; wait; start_server &` where start_server does `exec $BIN` so $! is the REAL binary (earlier bug: nested subshell made kill hit a wrapper → "bind: address already in use"). Also critical: `git config --global --add safe.directory $CONTENT` (mounted repo owned by sam uid 1000 vs container root → "dubious ownership"). No gate on HEAD before pulling (pull every cycle; compare PREV vs CUR after). ## NixOS gotchas (.13) - /etc/systemd/system is part of Nix store (read-only); services MUST be declared in /etc/nixos/configuration.nix + nixos-rebuild switch. - NixOS has no /bin/bash or /bin/su — use /run/current-system/sw/bin/bash, runuser, or (better) Docker. - I had first added systemd units + caddy vhosts into configuration.nix but reverted (restored backup configuration.nix.bak-kontra-20260908-195305) in favour of the Docker approach. Revert scripts: deploy/revert-kontra-nix.sh. - Configuration.nix restores cleanly; leftover home-manager-sam failure (`/home/sam/.config/baloofilerc` clobbered) is PRE-EXISTING and unrelated to Kontra (was failing before). ## Build - Go binary built locally on .27 (`app/build.sh`: templ generate → go build), OR inside Docker build (golang:1.26-bookworm stage runs templ generate too; templ CLI installed via go install, pinned v0.3.1020). - Docker build context = /home/sam/Docker/Containers/kontra with src/{go.mod,go.sum,src/...}. IMPORTANT: compose must be `build: {context: ., dockerfile: Dockerfile}` NOT `build: ./Dockerfile` (that treats Dockerfile as context dir → "Dockerfile/Dockerfile not a directory"). - `.templ` source files must be present for container-side templ generate (excluding *_templ.go from rsync is fine; .templ must be included). ## Deploy commands (on .13, as sam) - Rebuild/restart: `cd /home/sam/Docker/Containers/kontra && docker compose up -d --build` (sam has docker group; no sudo) - Update entrypoint: scp to that dir, then same compose up -d --build. - Gitea deploy token lives in gitignored .env at repo root (never committed). ## Verified flows - Push new article → 30s → live (200). Push delete → 30s → 404. Clean restart (no port bind errors). - Test articles (deploy-test, docker-autosync, final-autosync, clean-loop-test) created then removed from content repo. ## Files - /home/sam/home_network/web_sites/kontra_day/PLAN.md, DESIGN.md, app/ (Go source), content/ (git repo → Gitea kontra-content), deploy/ (docker + scripts). - Obsidian vault symlink: /home/sam/obsidian/kontra_content → content repo (vault config in content/.obsidian). ## Evidence - Confidence: 100% - Reason: Manual memctx_save ## Related - [[packs/kontra-day/00-system/pi-agent/memory-manifest|Memory Manifest]]