diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..8cdf55a4 --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# /opt/zabbu-sign/.env on the VPS — never commit a filled-in copy. + +# Postgres password for the docuseal-db container. +# Generate once with: openssl rand -hex 24 +POSTGRES_PASSWORD=change-me + +# Rails secret. Generate ONCE with: openssl rand -hex 64 +# Rotating this invalidates session cookies and any encrypted columns. +SECRET_KEY_BASE=change-me diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 00000000..2db81976 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,150 @@ +# Gold Leaf DocuSeal — deployment notes + +DocuSeal as a fourth user-facing service in the eDMS stack at **https://sign.zabbu.co**. Forked from `docusealco/docuseal` and tracked on the `goldleaf-custom` branch so monthly upstream merges (`git merge upstream/master`) stay clean. + +## What this fork adds + +Only deployment scaffolding — no Rails source-code changes in Phase 1: + +- `docker-compose.prod.yml` — production stack (app + Postgres) routed via the shared nginx-proxy. +- `.env.example` — documented secrets template. +- `.github/workflows/deploy.yml` — CI/CD: build the upstream `Dockerfile`, push `servedigital/docuseal:latest`, SSH-deploy to `/opt/zabbu-sign/`. + +Branding (app name, logo) is applied through DocuSeal's own admin UI after the first boot. + +## Architecture + +``` +Internet → nginx-proxy + acme-companion (/opt/goldleaf-dms/) + │ proxy network (external) + ▼ + docuseal:3000 (servedigital/docuseal:latest) + │ docuseal-internal network (internal-only) + ▼ + docuseal-db (postgres:18) + +Outbound mail: + docuseal --SMTP:25--> invoice-reminder --REST--> Mailgun + (proxy network, no TLS, no auth) +``` + +DocuSeal's bundled Caddy is **not used** — nginx-proxy handles SSL for every other service in this droplet, and we want one termination point. + +## Email + +DigitalOcean blocks outbound SMTP, so DocuSeal points at the existing `invoice-reminder` container (extended with an SMTP→Mailgun relay) instead of contacting an SMTP server directly. Required reading: `edms-invoice-reminder-service/README.md` → "Internal SMTP relay". + +DocuSeal env vars used: + +| Var | Value | Source | +|---|---|---| +| `SMTP_ADDRESS` | `invoice-reminder` | container DNS on `proxy` network | +| `SMTP_PORT` | `25` | the relay's listen port | +| `SMTP_DOMAIN` | `zabbu.co` | EHLO domain | +| `SMTP_ENABLE_STARTTLS` | `false` | local network, no need | +| `SMTP_ENABLE_SSL` / `SMTP_ENABLE_TLS` | `false` | same | +| `SMTP_USERNAME` / `SMTP_PASSWORD` | (unset) | relay accepts unauthenticated mail | + +DocuSeal's default From header is `DocuSeal `. Mailgun rejects mail whose From domain isn't on a verified domain, so **after first boot** set the per-account "Send from Email" in DocuSeal's admin UI (Account settings → Send from Email) to a verified Mailgun address (e.g. `sign@mg.servedigital.io`). The relay also rewrites unmatched From addresses to `MAILGUN_FROM` as a safety net, but explicit configuration is cleaner. + +## First-time VPS setup (one-shot) + +```bash +ssh zabbu@46.101.144.7 +sudo mkdir -p /opt/zabbu-sign +sudo chown zabbu:zabbu /opt/zabbu-sign +cd /opt/zabbu-sign +``` + +Copy the compose file from your laptop: + +```bash +scp docker-compose.prod.yml zabbu@46.101.144.7:/opt/zabbu-sign/docker-compose.yml +``` + +Create `/opt/zabbu-sign/.env`: + +```bash +cat > /opt/zabbu-sign/.env <> .env < /opt/zabbu-sign/pgdata-backup.sql +tar -czf docuseal-$(date +%F).tar.gz \ + /opt/zabbu-sign/data /opt/zabbu-sign/pgdata-backup.sql +# then s3cmd put → DO Spaces +``` diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 00000000..5a1076a6 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,66 @@ +# Production — deploy to /opt/zabbu-sign/ on VPS (46.101.144.7). +# +# Routing/SSL: handled by the shared nginx-proxy + acme-companion stack at +# /opt/goldleaf-dms/. We bypass DocuSeal's bundled Caddy entirely. +# +# Email: outbound mail goes to the invoice-reminder container on the same +# `proxy` network, port 25 (no TLS, no auth — it's a local-only relay). +# That container forwards to Mailgun's REST API. See +# edms-invoice-reminder-service/README.md → "Internal SMTP relay". +services: + docuseal: + image: servedigital/docuseal:latest + container_name: docuseal + restart: unless-stopped + depends_on: + docuseal-db: + condition: service_healthy + env_file: .env + environment: + - VIRTUAL_HOST=sign.zabbu.co + - VIRTUAL_PORT=3000 + - LETSENCRYPT_HOST=sign.zabbu.co + - HOST=sign.zabbu.co + - FORCE_SSL=true + - DATABASE_URL=postgresql://docuseal:${POSTGRES_PASSWORD}@docuseal-db:5432/docuseal + - SMTP_ADDRESS=invoice-reminder + - SMTP_PORT=25 + - SMTP_DOMAIN=zabbu.co + - SMTP_ENABLE_STARTTLS=false + - SMTP_ENABLE_SSL=false + - SMTP_ENABLE_TLS=false + volumes: + - ./data:/data/docuseal + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:3000/up"] + interval: 30s + timeout: 10s + start_period: 60s + retries: 5 + networks: + - proxy + - docuseal-internal + + docuseal-db: + image: postgres:18 + container_name: docuseal-db + restart: unless-stopped + environment: + - POSTGRES_USER=docuseal + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=docuseal + volumes: + - ./pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U docuseal"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - docuseal-internal + +networks: + proxy: + external: true + docuseal-internal: + internal: true