Skip to content

Deployment

sarmakska edited this page May 3, 2026 · 2 revisions

Deployment

Three good options. Pick by ops preference.

Path A: Docker on a VPS (recommended)

Cheapest, predictable, full control.

Build and run

docker build -t webhook-to-email .
docker run -d \
  --name webhook \
  --restart unless-stopped \
  -p 3000:3000 \
  --env-file .env \
  webhook-to-email

docker-compose

docker-compose up -d

docker-compose.yml ships in the repo with a healthcheck on /health.

Recommended VPS

Provider Spec Monthly cost Notes
Hetzner CX22 2 vCPU, 4GB €4 Cheapest sane option
Fly.io shared 1x $0 (small) - $5 Global edge, good for multi-region
Render starter $7 Heroku-feel, simple deploys
Railway hobby $5 Easy Postgres if you add a queue
DigitalOcean basic droplet $6 Familiar

Hetzner is the price/perf winner in 2026.

Front with Caddy (auto-HTTPS)

Caddyfile:

your-domain.com {
  reverse_proxy localhost:3000
}

Run Caddy via systemd. HTTPS provisioned automatically by Let's Encrypt.

Path B: Fly.io

Auto multi-region. Free tier covers small apps.

Initial deploy

fly launch
# follow prompts, accept defaults
fly secrets set RESEND_API_KEY=re_... NOTIFY_EMAIL=you@... WEBHOOK_SECRET=...
fly deploy

fly.toml is auto-generated. Default config is reasonable: 1 instance, auto-stop on idle.

Scale up

fly scale count 2  # 2 instances for HA
fly scale memory 256  # 256MB per instance

Cost

Free tier covers ~3 small apps. Beyond that, ~$2/month per shared 1x instance.

Path C: Render

Web service, auto-deploy on push.

  1. New → Web Service → Connect repo
  2. Environment: Node
  3. Build: npm install
  4. Start: npm start
  5. Add env vars in Settings
  6. Free tier sleeps after 15 min idle (wakes in ~20s)

For always-on, upgrade to $7/month starter.

Custom domain

All three platforms accept custom domains. DNS:

CNAME hooks.your-domain.com → your-app.fly.dev (or render URL or VPS IP via A record)

Then point Stripe / GitHub / Cal.com webhooks at https://hooks.your-domain.com/hooks/<source>.

Smoke test

curl https://your-domain.com/health
# {"ok":true}

curl -X POST https://your-domain.com/hooks/test \
  -H "Content-Type: application/json" \
  -d '{"hello":"world"}'
# {"ok":true}

Email should arrive in 2-3 seconds.

Monitoring

Track:

Metric Why
Request rate per source Detect anomalies
Email send error rate Resend issues
401 rate Wrong secret or attack
500 rate Bugs in templates
P95 latency Performance regression

Tools:

  • BetterStack Logs — structured log aggregation, free tier good
  • Axiom — same idea, cheaper at scale
  • Sentry — error tracking, useful for template exceptions
  • Plausible / Umami — overkill for a webhook receiver

Cost

Real numbers for a typical setup:

Item Per month
Hetzner CX22 €4
Resend (3,000 emails free, then $20 for 50k) £0 - £15
Domain £0.50
Total £4 - £20

For 10,000 webhooks/month with email forwarding, well under £20 all-in.

Clone this wiki locally