datewise/docker-compose.yml
metatroncubeswdev bd2e3906df chore(deploy): Docker stack for the VPS (db, app, worker; Caddy or existing Nginx)
- multi-stage Dockerfile; worker bundled to build/worker.mjs (no tsx at runtime)
- docker-compose.yml + docker-compose.nginx.yml (127.0.0.1:3320), Nginx site, encrypted backups
- production URLs (datewise.metatronlabz.com) in shopify.app.toml; URL auto-update off
- verified locally: healthz/landing/privacy/terms 200, unsigned proxy/webhook 400, worker starts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:53:08 -04:00

73 lines
2.4 KiB
YAML

# Production stack for one VPS: Postgres + the app + the worker + Caddy (automatic HTTPS from Let's Encrypt).
# Usage: cp .env.example .env (fill in the production section) then docker compose up -d --build
# If the VPS already runs Nginx on 80/443, use docker-compose.nginx.yml instead of Caddy (see that file).
x-app-env: &app-env
NODE_ENV: production
PORT: 3000
DATABASE_URL: postgresql://datewise:${POSTGRES_PASSWORD}@db:5432/datewise?schema=public
SHOPIFY_API_KEY: ${SHOPIFY_API_KEY:?set SHOPIFY_API_KEY in .env}
SHOPIFY_API_SECRET: ${SHOPIFY_API_SECRET:?set SHOPIFY_API_SECRET in .env}
SHOPIFY_APP_URL: https://${APP_DOMAIN:?set APP_DOMAIN in .env}
SCOPES: read_products,write_products,write_publications,read_orders,write_orders,read_customers,read_locations,read_inventory,write_validations,write_delivery_customizations,write_payment_customizations
APP_ENCRYPTION_KEY: ${APP_ENCRYPTION_KEY:?set APP_ENCRYPTION_KEY in .env (openssl rand -base64 32)}
GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY:-}
RESEND_API_KEY: ${RESEND_API_KEY:-}
EMAIL_FROM_DOMAIN: ${EMAIL_FROM_DOMAIN:-}
services:
db:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: datewise
POSTGRES_USER: datewise
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U datewise -d datewise"]
interval: 5s
retries: 10
app:
build: .
image: datewise:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment: *app-env
expose:
- "3000"
# Sweeps expired holds, sends reminders, offers freed places to the waitlist, reconciles missed orders.
worker:
image: datewise:latest
restart: unless-stopped
depends_on:
app:
condition: service_healthy # the app has applied migrations
environment: *app-env
command: ["node", "build/worker.mjs"]
healthcheck:
disable: true
caddy:
image: caddy:2-alpine
restart: unless-stopped
depends_on: [app]
ports:
- "80:80"
- "443:443"
environment:
APP_DOMAIN: ${APP_DOMAIN}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
volumes:
pgdata:
caddy_data:
caddy_config: