Preview Environment
The preview environment runs the develop branch on a stack of its own, so unreleased work, data migrations included, can be tried without going near production data.
How it works
Section titled “How it works”Every push to develop runs .github/workflows/deploy-preview.yml, which builds the api, couchdb, web and admin images and pushes them to Docker Hub as mbret/oboku-*:preview. Releases keep latest and the version tags, so nothing pulls a preview image unless it asks for the preview tag by name.
The preview stack runs those images in Cosmos:
| URL | Container | Port |
|---|---|---|
https://app.preview.oboku.me |
oboku-preview-web |
80 |
https://api.preview.oboku.me |
oboku-preview-api |
3000 |
https://admin.preview.oboku.me |
oboku-preview-admin |
80 |
With auto-update on, Cosmos checks for new preview images every 6 hours. Update the containers by hand to pick up a push sooner.
The web app and the API share the oboku.me site on purpose. The auth cookies are SameSite=Lax (see Authentication), so the browser only sends them when the page and the API belong to the same site. Vercel preview deployments live on *.vercel.app, a public suffix that makes every deployment a site of its own, so they can never sign in against an API we host.
Keeping production out of reach
Section titled “Keeping production out of reach”The preview stack shares no data, credentials or containers with production. The template below follows these rules; keep them when editing it.
- Its own secrets volume. CouchDB generates a new JWT key pair on first start. Never mount production's secrets: the API signs CouchDB admin tokens with that private key, so production's key would give the preview full admin access to production's CouchDB if it could reach it.
- Explicit database hosts.
COUCH_DB_URLandPOSTGRES_HOSTname the preview containers. The API's defaults,couchdbandpostgres, are generic names that could resolve to production containers if the two stacks ever shared a Docker network. - New passwords, its own network and volumes, no host ports. Cosmos routes by hostname, and host ports would clash with production's anyway.
- No production S3 settings. Covers stay on the preview's disk, the default
fsstorage. With production's settings they would be written into production's bucket. - No
SENTRY_DSN. Preview errors would otherwise land in production's Sentry project. - A memory budget. The stack runs on the same server as production. Cap the preview containers' memory in Cosmos when headroom is tight, so a busy preview cannot starve production.
Setting up the stack in Cosmos
Section titled “Setting up the stack in Cosmos”- Point
app.preview.oboku.me,api.preview.oboku.meandadmin.preview.oboku.meat the Cosmos server before creating their URLs, so Cosmos can get their certificates. - In ServApps, use Import Docker Compose with the template below. Replace the
CHANGE_ME_*values with new passwords. The Postgres one appears twice and both must match. - Create three new URLs from the table above. On the API URL, turn on Disable Header Hardening as on production's API URL, or the browser rejects every API response (see Installation with Cosmos). Match production's Smart Shield settings there too, since replication sends many requests.
- Turn on auto-update for the four
oboku-preview-*containers running oboku images. - Check the API log on start. It should print
Browser origins — app: any port on app.preview.oboku.me; admin: https://admin.preview.oboku.me. Any other hostname there meansAPP_PUBLIC_URLorADMIN_PUBLIC_URLis wrong. If it logsUnable to ensure user db indexes at startup, the API started before CouchDB was ready: restart the API. - Create accounts from the preview admin panel, since the template configures no email provider (see Account Sign in / Sign up). Adding the
EMAIL_*variables to the API works too: their links point atAPP_PUBLIC_URL.
Google, Dropbox and OneDrive stay off, because the template sets none of their client IDs. Register the preview hostnames with those providers before turning them on.
services: oboku-preview-couchdb: image: mbret/oboku-couchdb:preview container_name: oboku-preview-couchdb restart: always environment: COUCHDB_USER: admin COUCHDB_PASSWORD: CHANGE_ME_COUCHDB JWT_PRIVATE_KEY_FILE: /secrets/jwt_private_key.pem JWT_PUBLIC_KEY_FILE: /secrets/jwt_public_key.pem volumes: - oboku-preview-secrets:/secrets - oboku-preview-couchdb-data:/opt/couchdb/data - oboku-preview-couchdb-config:/opt/couchdb/etc/local.d healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5984/_up"] interval: 10s timeout: 5s retries: 10 start_period: 20s networks: - oboku-preview
oboku-preview-postgres: image: postgres:17 container_name: oboku-preview-postgres restart: always environment: POSTGRES_DB: oboku POSTGRES_PASSWORD: CHANGE_ME_POSTGRES volumes: - oboku-preview-postgres:/var/lib/postgresql/data networks: - oboku-preview
oboku-preview-api: image: mbret/oboku-api:preview container_name: oboku-preview-api restart: always depends_on: oboku-preview-couchdb: condition: service_healthy oboku-preview-postgres: condition: service_started environment: NODE_ENV: production APP_PUBLIC_URL: https://app.preview.oboku.me ADMIN_PUBLIC_URL: https://admin.preview.oboku.me COUCH_DB_URL: http://oboku-preview-couchdb:5984 POSTGRES_HOST: oboku-preview-postgres POSTGRES_DB: oboku POSTGRES_PASSWORD: CHANGE_ME_POSTGRES JWT_PRIVATE_KEY_FILE: /secrets/jwt_private_key.pem JWT_PUBLIC_KEY_FILE: /secrets/jwt_public_key.pem API_DATA_DIR: /var/lib/oboku/data API_CONFIG_DIR: /var/lib/oboku/config ADMIN_LOGIN: admin ADMIN_PASSWORD: CHANGE_ME_ADMIN volumes: - oboku-preview-secrets:/secrets:ro - oboku-preview-api-data:/var/lib/oboku/data - oboku-preview-api-config:/var/lib/oboku/config networks: - oboku-preview
oboku-preview-web: image: mbret/oboku-web:preview container_name: oboku-preview-web restart: always environment: VITE_API_URL: https://api.preview.oboku.me networks: - oboku-preview
oboku-preview-admin: image: mbret/oboku-admin:preview container_name: oboku-preview-admin restart: always environment: VITE_API_URL: https://api.preview.oboku.me networks: - oboku-preview
networks: oboku-preview:
volumes: oboku-preview-secrets: oboku-preview-couchdb-data: oboku-preview-couchdb-config: oboku-preview-postgres: oboku-preview-api-data: oboku-preview-api-config:Resetting
Section titled “Resetting”Data survives image updates, and migrations only move forward. When a branch leaves the preview data in the way, delete the oboku-preview-* containers and volumes and import the template again. Clear the site data of app.preview.oboku.me in the browser as well: its local database would otherwise replicate the old documents into the new stack.