diff --git a/docs/hosting/checkmate-so/README.md b/docs/hosting/checkmate-so/README.md new file mode 100644 index 000000000..d2a89cf09 --- /dev/null +++ b/docs/hosting/checkmate-so/README.md @@ -0,0 +1,51 @@ +# checkmate.so hosting (demo + staging) + +Config for BlueWave's hosted instances at `demo.checkmate.so` and +`staging.checkmate.so`: one host, one nginx gateway terminating TLS, two +independent Checkmate stacks running the mono image +(`ghcr.io/bluewave-labs/checkmate`), plus a shared +[Capture](https://github.com/bluewave-labs/capture) agent. It replaces the old +`docker/unified/` setup (nginx client images + separate backend images + a +republished Mongo image) and doubles as a worked example of running the mono +image behind a TLS reverse proxy. + +## How deploys work + +`.github/workflows/build-image.yml` builds and pushes the image, then SSHes in +and runs `docker compose pull && docker compose up -d` in this directory: + +- push to `develop` → image tagged `:develop` → `checkmate-staging` recreated +- push to `demo` → image tagged `:demo` → `checkmate-demo` recreated + +The host keeps a git checkout of this repo; the deploy does `git pull` first so +compose/gateway changes land with the code. + +## Host-local files (not committed) + +Kept next to this compose file on the host: + +- `server-prod.env` / `server-staging.env` — Checkmate server env + (`DB_CONNECTION_STRING`, `JWT_SECRET`, `CLIENT_HOST=https://demo.checkmate.so` + etc.). The old `UPTIME_APP_*` client vars are no-ops now and can be removed. +- `mongo-prod.env` / `mongo-staging.env` — Mongo credentials. +- `mongo/data-prod` / `mongo/data-staging` — Mongo data directories. + +TLS certs live in `/etc/letsencrypt` (see `certbot-compose.yaml`; certbot +answers ACME challenges on port 8080 behind the gateway). + +## One-time migration from docker/unified + +1. `cd ~/checkmate && git pull` (must be on a branch containing this directory). +2. Stop the old stack: `cd docker/unified && docker compose down`. +3. Move host-local files here: + `mv docker/unified/{server-prod.env,server-staging.env,mongo-prod.env,mongo-staging.env} docs/hosting/checkmate-so/` + `mv docker/unified/mongo docs/hosting/checkmate-so/mongo` +4. **Check the Mongo data version before first start.** The old + `checkmate:mongo-*` images were built `FROM mongo` (floating latest), so the + data files follow whatever version was current at their last image build. Run + `db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })` + against the old container first and make the compose's `mongo:` tag match + (checked 2026-07-09: both stacks report FCV 8.2, hence the `mongo:8.2` pin + here). A lower pin than the data's FCV will refuse to start; a higher one + should step up one release at a time. +5. `cd docs/hosting/checkmate-so && docker compose up -d`. diff --git a/docs/hosting/checkmate-so/certbot-compose.yaml b/docs/hosting/checkmate-so/certbot-compose.yaml new file mode 100755 index 000000000..9220d07a1 --- /dev/null +++ b/docs/hosting/checkmate-so/certbot-compose.yaml @@ -0,0 +1,19 @@ +version: "3" + +services: + webserver: + image: nginx:latest + ports: + - 80:80 + - 443:443 + restart: always + volumes: + - ./nginx/conf.d/:/etc/nginx/conf.d/:ro + - ./certbot/www/:/var/www/certbot/:ro + certbot: + image: certbot/certbot:latest + volumes: + - ./certbot/www/:/var/www/certbot/:rw + - ./certbot/conf/:/etc/letsencrypt/:rw + depends_on: + - webserver diff --git a/docs/hosting/checkmate-so/docker-compose.yaml b/docs/hosting/checkmate-so/docker-compose.yaml new file mode 100644 index 000000000..65bee76d1 --- /dev/null +++ b/docs/hosting/checkmate-so/docker-compose.yaml @@ -0,0 +1,122 @@ +# BlueWave's hosted demo + staging instances: one nginx gateway terminating TLS +# in front of two independent Checkmate stacks, both running the mono image +# (ghcr.io/bluewave-labs/checkmate). Doubles as a worked example of putting a +# TLS proxy in front of the image. +# +# Deployed by .github/workflows/build-image.yml (deploy-staging / deploy-demo +# jobs). Env files (server-prod.env, server-staging.env, mongo-prod.env, +# mongo-staging.env) live on the host next to this file and are not committed. + +services: + # Gateway (single entry point) + gateway: + image: nginx:1.27.1-alpine + container_name: checkmate-gateway + restart: always + ports: + - "80:80" + - "443:443" + volumes: + - /etc/letsencrypt:/etc/nginx/ssl:ro + - ./gateway/nginx.conf:/etc/nginx/conf.d/default.conf:ro + + # Demo: demo.checkmate.so ----- + checkmate-demo: + image: ghcr.io/bluewave-labs/checkmate:demo + container_name: checkmate-demo + restart: always + env_file: + - server-prod.env + stop_grace_period: 60s + healthcheck: + test: + [ + "CMD", + "node", + "-e", + "require('http').get('http://localhost:52346/livez',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))", + ] + interval: 15s + timeout: 3s + start_period: 60s + start_interval: 2s + retries: 3 + depends_on: + mongodb-prod: + condition: service_healthy + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + + mongodb-prod: + image: mongo:8.2 + container_name: mongodb-prod + restart: always + command: ["mongod", "--quiet", "--bind_ip_all"] + volumes: + - ./mongo/data-prod:/data/db + env_file: + - mongo-prod.env + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] + interval: 5s + timeout: 30s + start_period: 0s + start_interval: 1s + retries: 30 + + # Staging: staging.checkmate.so ----- + checkmate-staging: + image: ghcr.io/bluewave-labs/checkmate:develop + container_name: checkmate-staging + restart: always + env_file: + - server-staging.env + stop_grace_period: 60s + healthcheck: + test: + [ + "CMD", + "node", + "-e", + "require('http').get('http://localhost:52346/livez',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))", + ] + interval: 15s + timeout: 3s + start_period: 60s + start_interval: 2s + retries: 3 + depends_on: + mongodb-staging: + condition: service_healthy + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + + mongodb-staging: + image: mongo:8.2 + container_name: mongodb-staging + restart: always + command: ["mongod", "--quiet", "--bind_ip_all"] + volumes: + - ./mongo/data-staging:/data/db + env_file: + - mongo-staging.env + healthcheck: + test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"] + interval: 5s + timeout: 30s + start_period: 0s + start_interval: 1s + retries: 30 + + # Shared utils + capture: + image: ghcr.io/bluewave-labs/capture:latest + container_name: capture + restart: unless-stopped + ports: + - "59232:59232" + environment: + - API_SECRET=my_secret + - GIN_MODE=release + volumes: + - /etc/os-release:/etc/os-release:ro diff --git a/docs/hosting/checkmate-so/gateway/nginx.conf b/docs/hosting/checkmate-so/gateway/nginx.conf new file mode 100644 index 000000000..3523872c5 --- /dev/null +++ b/docs/hosting/checkmate-so/gateway/nginx.conf @@ -0,0 +1,54 @@ +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; + + +server { + listen 80; + server_name demo.checkmate.so staging.checkmate.so; + + location /.well-known/acme-challenge/ { + # Forward the challenge to Certbot listening on 8080 + proxy_pass http://172.17.0.1:8080; + } + + location / { + return 301 https://$host$request_uri; + } +} + + +# --- DEMO: demo.checkmate.so --- +server { + listen 443 ssl; + server_name demo.checkmate.so; + + ssl_certificate /etc/nginx/ssl/live/demo.checkmate.so/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/live/demo.checkmate.so/privkey.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + # The mono image serves both the SPA and the API + proxy_pass http://checkmate-demo:52345; + } +} + +# --- STAGING: staging.checkmate.so --- +server { + listen 443 ssl; + server_name staging.checkmate.so; + + ssl_certificate /etc/nginx/ssl/live/staging.checkmate.so/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/live/staging.checkmate.so/privkey.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + # The mono image serves both the SPA and the API + proxy_pass http://checkmate-staging:52345; + } +}