changemaker.lite/DEPLOYMENT_TEST_REPORT_2026-04-09.md
bunker-admin f8c8a939d7 Add full non-interactive mode to config.sh
New CLI flags for scripted deployments:
  --smtp-host/port/user/pass   Production SMTP configuration
  --pangolin-api-url/key/org-id/endpoint/site  Full Pangolin tunnel setup
  --mapbox-key                 Mapbox API key
  --maxmind-account-id/license-key  MaxMind GeoIP credentials

With --pangolin-site=new, config.sh creates a Pangolin site, fetches
Newt credentials, and creates all resources+targets automatically.
With --pangolin-site=existing, it connects to the first available site.

Bunker Admin
2026-04-09 12:08:05 -06:00

5.6 KiB

Deployment Test Report — 2026-04-09

Target: Fresh curl-install deployment to cursedknowledge.org Server: 100.90.78.47 (bunker-admin, Tailscale) Release: v2.8.1 (commit 82546131) Pangolin Org: cursed-knowledge @ bnkserve.org


Summary

Full end-to-end deployment test: wipe server, build release, curl install, configure, verify all 37 containers and 18 external subdomains. All services operational.


Bugs Found & Fixed

1. config.sh — Pangolin API URL default was correct, but endpoint derivation was wrong

Problem: PANGOLIN_ENDPOINT was derived from PANGOLIN_API_URL by stripping /v1. Since the API lives at api.bnkserve.org but the Newt endpoint is pangolin.bnkserve.org (different hostname), this produced the wrong value.

Fix: Ask for PANGOLIN_ENDPOINT as a separate prompt instead of deriving it.

Commit: 599498fc

2. config.sh — No resources or targets created during Pangolin setup

Problem: config.sh created the Pangolin site and wrote Newt credentials to .env, but never created the HTTP resources (subdomain → target mappings) that tell Pangolin how to route traffic. The message said "Resources will be created automatically via the admin GUI or sync endpoint" — but the admin GUI isn't accessible until the tunnel works, creating a chicken-and-egg problem.

Fix: Added pangolin_create_resources() function that:

  • Looks up the domain ID from registered domains
  • Creates an HTTP resource for each of 18 subdomains
  • Creates a target for each resource pointing to nginx:80
  • Sets each resource as public (no SSO, no blockAccess)

Commit: 599498fc

3. config.sh — Pangolin site creation failed with "Invalid address format"

Problem: pickSiteDefaults returns clientAddress without CIDR notation (e.g., 100.90.128.0 instead of 100.90.128.0/24). Pangolin's site creation API rejects this.

Fix: Omit the address field from the site creation payload — Pangolin auto-assigns a valid address.

Commit: a85e153b

4. MongoDB — User not created on fresh volumes

Problem: The custom entrypoint (exec mongod --replSet rs0 --bind_ip_all --auth --keyFile ...) bypassed Docker's standard docker-entrypoint.sh, which is responsible for reading MONGO_INITDB_ROOT_USERNAME/PASSWORD and creating the root user. On fresh volumes, MongoDB started with auth enabled but no users existed, causing the healthcheck and Rocket.Chat to fail.

Fix: Changed entrypoint to generate the keyfile then delegate to docker-entrypoint.sh:

exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --keyFile /data/replica.key

Docker's entrypoint handles user creation, then starts mongod with our flags.

Commit: 599498fc

5. Missing media subdomain in Pangolin resources (non-issue)

Investigation: The media API is accessed via path routing (api.domain/media/), not a separate subdomain. No nginx server_name block for media.* exists. The CLAUDE.md routing table listing was inaccurate. No fix needed.


Test Results

Container Status (37/37 running)

All containers up and healthy where healthchecks are configured:

  • Core: postgres, redis, api, admin, nginx, media-api — all healthy
  • MongoDB: healthy on first boot (entrypoint fix confirmed)
  • Rocket.Chat: healthy (after MongoDB)
  • All other services: running/healthy

External Access (18/18 subdomains)

Subdomain Service Status Notes
cursedknowledge.org MkDocs 200 Root domain
app.cursedknowledge.org Admin GUI 200
api.cursedknowledge.org API 200 /api/health returns healthy
docs.cursedknowledge.org MkDocs Live 200
git.cursedknowledge.org Gitea 200 Needs first-time setup
home.cursedknowledge.org Homepage 200
db.cursedknowledge.org NocoDB 302 Redirects to dashboard
n8n.cursedknowledge.org n8n 200
grafana.cursedknowledge.org Grafana 302 Redirects to login
draw.cursedknowledge.org Excalidraw 200
vault.cursedknowledge.org Vaultwarden 200
qr.cursedknowledge.org Mini QR 200
code.cursedknowledge.org Code Server 302 Redirects to login
listmonk.cursedknowledge.org Listmonk 403 Expected (auth proxy)
mail.cursedknowledge.org MailHog 200
chat.cursedknowledge.org Rocket.Chat 200
events.cursedknowledge.org Gancio 302 Redirects to home
meet.cursedknowledge.org Jitsi 200

Post-Deploy Manual Steps

  1. Gitea: Complete first-time setup at https://git.cursedknowledge.org
  2. Admin password: Change default admin password at https://app.cursedknowledge.org

Deployment Timeline

Step Duration Notes
Build images (build-and-push.sh) ~3 min 4 services: api, admin, media-api, nginx
Build tarball (build-release.sh) ~5 sec 15MB tarball, 292 files
Upload to Gitea Releases ~2 sec v2.8.1
Download tarball on remote ~1 sec Via curl
config.sh (non-interactive) ~3 sec + manual .env patching for SMTP/Pangolin
docker compose up -d ~2 min Image pulls from Gitea registry
All services healthy ~3 min Including MongoDB init + seed
External access verified immediate All 18 subdomains

Total time from wipe to fully operational: ~10 minutes


Outstanding Items

  • config.sh non-interactive mode should support all variables (SMTP, Pangolin, Mapbox, MaxMind)
  • Newt WireGuard "invalid IP address" warning (cosmetic — TCP proxies work fine, clients feature disabled)
  • Gitea first-time setup should be automated or documented in config.sh next steps