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
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
- Gitea: Complete first-time setup at https://git.cursedknowledge.org
- 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.shnon-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