commit 38089ef2699cc2504b98feca915b643709a2012e Author: Julien Calixte Date: Mon Jun 1 20:37:14 2026 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b57fc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.env +.env.* +!.env.example + +# editors +.vscode/ +.idea/ +*.swp +*.swo +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..539664d --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# coolcouch + +CouchDB 3 on Coolify, with CORS enabled and system databases bootstrapped at first start. + +## 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.** The `couchdb-init` one-shot service will wait for CouchDB to be healthy, then `PUT` `_users`, `_replicator`, `_global_changes` and exit. + +## 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 a 2xx with an `Access-Control-Allow-Origin` header. + +## CORS + +Wildcard origin (`*`), `credentials = false` — the only browser-spec-legal combination with a wildcard. If a client needs cookie/credentialed auth, switch `origins` in `couchdb/local.d/cors.ini` to an explicit list and set `credentials = true`. + +## Files + +- `docker-compose.yml` — services, volume, Traefik labels, healthcheck, init sidecar +- `couchdb/local.d/cors.ini` — mounted into `/opt/couchdb/etc/local.d/` so CouchDB picks it up at startup diff --git a/couchdb/local.d/cors.ini b/couchdb/local.d/cors.ini new file mode 100644 index 0000000..ee4b778 --- /dev/null +++ b/couchdb/local.d/cors.ini @@ -0,0 +1,8 @@ +[chttpd] +enable_cors = true + +[cors] +origins = * +credentials = false +methods = GET, PUT, POST, HEAD, DELETE +headers = accept, authorization, content-type, origin, referer, x-csrf-token diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..65fd6c8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,50 @@ +services: + couchdb: + image: couchdb:3 + restart: unless-stopped + environment: + SERVICE_FQDN_COUCHDB_5984: + COUCHDB_USER: ${COUCHDB_USER:-admin} + COUCHDB_PASSWORD: ${COUCHDB_PASSWORD} + expose: + - "5984" + volumes: + - couchdb_data:/opt/couchdb/data + - ./couchdb/local.d/cors.ini:/opt/couchdb/etc/local.d/cors.ini:ro + healthcheck: + test: ["CMD", "curl", "-fsS", "http://localhost:5984/_up"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 30s + labels: + - "traefik.enable=true" + - "traefik.http.routers.couchdb.rule=Host(`${SERVICE_FQDN_COUCHDB_5984}`)" + - "traefik.http.routers.couchdb.entrypoints=https" + - "traefik.http.routers.couchdb.tls=true" + - "traefik.http.services.couchdb.loadbalancer.server.port=5984" + + couchdb-init: + image: curlimages/curl:latest + restart: "no" + depends_on: + couchdb: + condition: service_healthy + environment: + COUCHDB_USER: ${COUCHDB_USER:-admin} + COUCHDB_PASSWORD: ${COUCHDB_PASSWORD} + entrypoint: ["sh", "-c"] + command: + - | + set -eu + base="http://$${COUCHDB_USER}:$${COUCHDB_PASSWORD}@couchdb:5984" + for db in _users _replicator _global_changes; do + code=$$(curl -s -o /dev/null -w "%{http_code}" -X PUT "$$base/$$db") + case "$$code" in + 201|202|412) echo "$$db: ok ($$code)" ;; + *) echo "$$db: failed ($$code)"; exit 1 ;; + esac + done + +volumes: + couchdb_data: