Deploying notifyd
Read docs/DEPLOYMENTS.md (topology, inventory, runbook) and
docs/CONNECTORS.md (every variable). Principle: one instance per
company, all from the same repository, everything specific in environment
variables, never in the repo.
Minimal production environment
| Variable |
Purpose |
DATABASE_URL (or POSTGRES_PASSWORD with the bundled Postgres) |
Postgres 16 |
JWT_SECRET |
signs subscriber tokens and unsubscribe links (32+ random bytes) |
ADMIN_API_KEY |
operator surface + MCP, full rights |
READONLY_API_KEY (optional) |
digest, listings, metrics, read-only MCP tools |
EMAIL_PROVIDER + credentials |
resend (RESEND_API_KEY), cloudflare (CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_EMAIL_API_TOKEN), smtp (SMTP_HOST…), agentmail, log (dev only) |
EMAIL_FROM, EMAIL_FROM_NAME |
default sender, domain verified at the provider |
EMAIL_FALLBACK_PROVIDER + its credentials |
second provider used on 429/5xx; same sender domain verified there too |
PUBLIC_URL |
public base URL of the instance: hosts the one-click unsubscribe links |
CORS_ORIGINS |
back-office origins allowed to open the in-app stream and call /mcp from a browser |
SMS_PROVIDER + SMS_FROM + TELNYX_API_KEY or TWILIO_* |
optional SMS; TELNYX_WHATSAPP_API_KEY + WHATSAPP_FROM for WhatsApp |
EMAIL_RATE_PER_SEC (8), WORKER_MAX_ATTEMPTS (5) |
pacing under the provider limit, attempts before failed |
Generate secrets with openssl rand -hex 32. Never write a value in a file
tracked by git.
Steps
- Create the compose service from
docker-compose.yml of this repository
(Dokploy: Compose, git source, branch main, autodeploy on; add a push
webhook on the repository → <dokploy>/api/deploy/compose/<token> when
the Dokploy instance has no GitHub App).
- Set the variables above, deploy, check
GET /v1/health: status: ok,
commit equal to main.
- Create the company project:
POST /v1/admin/projects with id, name,
channels, from_email, from_name. Store the returned key as the
application's NOTIFYD_API_KEY. Delete the seeded craie project on any
instance that is not CRAIE's (DELETE /v1/admin/projects/craie).
- Point the application at
http://notifyd:3400 when it runs on the same
Docker network; give browsers the public URL only for the in-app stream.
- Run
GET /v1/admin/digest (or the digest MCP tool): the findings tell
you what is still missing (fallback provider, PUBLIC_URL, sender).
Operating rules
- Migrations run at boot and are additive; every instance migrates on the
same push. Never edit an applied migration.
- Replicas are allowed (SSE fans out through Postgres NOTIFY); pacing is per
replica: divide the provider limit by the replica count.
- Scrape
GET /v1/metrics/prometheus with the admin or read-only key as
bearer_token.
- A push to
main redeploys every instance within minutes: run
cargo test and docker build . before pushing.
1---2name: notifyd-deploy3description: Deploy and configure a notifyd instance (one Rust binary + Postgres) with Docker Compose or Dokploy — one instance per company, providers by environment variables (Resend, Cloudflare Email Service, SMTP, Telnyx, Twilio), fallback provider, pacing, public URL, admin and read-only keys, MCP access. Use when standing up notifyd, adding a provider, tuning rate limits, or diagnosing a deployment.4license: MIT5---67# Deploying notifyd89Read `docs/DEPLOYMENTS.md` (topology, inventory, runbook) and10`docs/CONNECTORS.md` (every variable). Principle: **one instance per11company**, all from the same repository, everything specific in environment12variables, never in the repo.1314## Minimal production environment1516| Variable | Purpose |17|---|---|18| `DATABASE_URL` (or `POSTGRES_PASSWORD` with the bundled Postgres) | Postgres 16 |19| `JWT_SECRET` | signs subscriber tokens and unsubscribe links (32+ random bytes) |20| `ADMIN_API_KEY` | operator surface + MCP, full rights |21| `READONLY_API_KEY` (optional) | digest, listings, metrics, read-only MCP tools |22| `EMAIL_PROVIDER` + credentials | `resend` (`RESEND_API_KEY`), `cloudflare` (`CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_EMAIL_API_TOKEN`), `smtp` (`SMTP_HOST`…), `agentmail`, `log` (dev only) |23| `EMAIL_FROM`, `EMAIL_FROM_NAME` | default sender, domain verified at the provider |24| `EMAIL_FALLBACK_PROVIDER` + its credentials | second provider used on 429/5xx; same sender domain verified there too |25| `PUBLIC_URL` | public base URL of the instance: hosts the one-click unsubscribe links |26| `CORS_ORIGINS` | back-office origins allowed to open the in-app stream and call `/mcp` from a browser |27| `SMS_PROVIDER` + `SMS_FROM` + `TELNYX_API_KEY` or `TWILIO_*` | optional SMS; `TELNYX_WHATSAPP_API_KEY` + `WHATSAPP_FROM` for WhatsApp |28| `EMAIL_RATE_PER_SEC` (8), `WORKER_MAX_ATTEMPTS` (5) | pacing under the provider limit, attempts before `failed` |2930Generate secrets with `openssl rand -hex 32`. Never write a value in a file31tracked by git.3233## Steps34351. Create the compose service from `docker-compose.yml` of this repository36 (Dokploy: Compose, git source, branch `main`, autodeploy on; add a push37 webhook on the repository → `<dokploy>/api/deploy/compose/<token>` when38 the Dokploy instance has no GitHub App).392. Set the variables above, deploy, check `GET /v1/health`: `status: ok`,40 `commit` equal to `main`.413. Create the company project: `POST /v1/admin/projects` with `id`, `name`,42 `channels`, `from_email`, `from_name`. Store the returned key as the43 application's `NOTIFYD_API_KEY`. Delete the seeded `craie` project on any44 instance that is not CRAIE's (`DELETE /v1/admin/projects/craie`).454. Point the application at `http://notifyd:3400` when it runs on the same46 Docker network; give browsers the public URL only for the in-app stream.475. Run `GET /v1/admin/digest` (or the `digest` MCP tool): the findings tell48 you what is still missing (fallback provider, `PUBLIC_URL`, sender).4950## Operating rules5152- Migrations run at boot and are additive; every instance migrates on the53 same push. Never edit an applied migration.54- Replicas are allowed (SSE fans out through Postgres NOTIFY); pacing is per55 replica: divide the provider limit by the replica count.56- Scrape `GET /v1/metrics/prometheus` with the admin or read-only key as57 `bearer_token`.58- A push to `main` redeploys every instance within minutes: run59 `cargo test` and `docker build .` before pushing.