# coolcouch CouchDB 3 on Coolify. The `couchdb-init` sidecar waits for the server to come up, creates the three system databases, and enables CORS via the config API — no file mounts required. ## Deploy 1. In Coolify: **New Resource → Docker Compose** and point it at this repository. 2. In the resource's **Environment Variables** tab, set: - `COUCHDB_PASSWORD` — required - `COUCHDB_USER` — optional, defaults to `admin` 3. In the resource's **Domains** tab, assign a domain to the `couchdb` service on port `5984`. Coolify fills `SERVICE_FQDN_COUCHDB_5984` from this. 4. **Deploy.** `couchdb-init` runs once, exits, and won't restart. ## Verify Replace `$DOMAIN` and `$PASS`: ```bash curl -fsS https://$DOMAIN/_up curl -fsS -u admin:$PASS https://$DOMAIN/ curl -fsS -u admin:$PASS https://$DOMAIN/_all_dbs curl -fsS -I -X OPTIONS https://$DOMAIN/ \ -H "Origin: https://example.com" \ -H "Access-Control-Request-Method: GET" ``` Expected: `_up` returns `{"status":"ok",...}`; `/` returns the CouchDB welcome JSON; `_all_dbs` returns `["_global_changes","_replicator","_users"]`; the preflight returns 2xx with an `Access-Control-Allow-Origin` header. If the init logs show CORS errors, you can re-trigger only that step by redeploying — the PUTs against `/_node/_local/_config/...` are idempotent. ## CORS `credentials = true` with an explicit origin list — required for PouchDB-style cookie sessions (`POST /_session`). Wildcard origin is incompatible with credentials per the CORS spec, so allowed origins must be enumerated. Update the `put_config cors origins` line in `docker-compose.yml` (comma-separated, no spaces) and redeploy when you add a new frontend. Or apply live: ``` curl -fsS -X PUT -u admin:$PASS \ -H "Content-Type: application/json" \ https://couch.apoena.dev/_node/_local/_config/cors/origins \ -d '"http://localhost:8080,https://new-frontend.example.com"' ``` ## Hardcoded values (cloning to another Coolify resource) Two strings in `docker-compose.yml` are pinned to this specific Coolify deployment and must be updated if you redeploy as a new resource: - The host in `traefik.http.routers.coolcouch.rule=Host(`couch.apoena.dev`)` - The per-resource Coolify network name, appearing twice: `traefik.docker.network=lvw8efvnvsxduodrkg68zul3` and `networks.coolify.name: lvw8efvnvsxduodrkg68zul3` They're hardcoded because Coolify does not interpolate `${...}` inside the `labels:` block (it does inside `environment:` and `networks..name:`). The network UUID matches `COOLIFY_RESOURCE_UUID` for the resource — find it in Coolify's UI or in `docker network ls` after first deploy. A `sed` swap before push, or a Coolify "Custom Build Command" running `sed -i` over the file, is the simplest way to keep this template portable. ## Signup service A small Gleam HTTP service in `signup/` exposes one endpoint that creates CouchDB users gated by a shared invite code. Lives on `signup-couch.apoena.dev`. ### Env vars (set in Coolify UI) - `INVITE_CODE` — required. The shared code users must present to sign up. The signup service reuses `COUCHDB_USER` / `COUCHDB_PASSWORD` for its CouchDB admin call (already required by the couchdb service). ### Endpoint ``` POST https://signup-couch.apoena.dev/signup Content-Type: application/json {"username": "julien", "password": "at-least-8-chars", "invite_code": ""} ``` Responses: - `201 {"ok": true, "username": "..."}` — user created - `400 {"error": "invalid_payload" | "invalid_username" | "password_too_short"}` - `401 {"error": "invalid_invite_code"}` - `409 {"error": "user_exists"}` - `502 {"error": "couchdb_unreachable" | "couchdb_error"}` The frontend should then `POST https://couch.apoena.dev/_session` with the same credentials to obtain the AuthSession cookie. ### CORS `SIGNUP_ALLOWED_ORIGINS` (already wired in `docker-compose.yml`) holds the comma-separated origin allow-list. Edit it there or override via Coolify env vars. ## Files - `docker-compose.yml` — services, volume, Traefik labels, init sidecar - `signup/` — Gleam signup service (Dockerfile, gleam.toml, src/)