DocsSelf HostingProduction
Back to Self-Hosting

Production Deployment

Hardening guide for running OpenPay in production. Follow these steps before accepting real payments.

1. Enable HTTPS

Traefik supports automatic TLS certificates via Let's Encrypt. Add the following to your docker-compose.yml under the Traefik service:

docker-compose.yml
# In docker-compose.yml → traefik service
command:
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
  - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=https"
  - "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
  - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
labels:
  - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
  - "traefik.http.routers.dashboard.tls.domains[0].main=your-domain.com"
  - "traefik.http.routers.dashboard.tls.domains[0].sans=*.your-domain.com"

2. Generate Strong Secrets

Never use default or weak passwords in production. Generate secure values:

generate secrets
# Generate a random 32-character password
openssl rand -base64 32

# Use these for:
POSTGRES_PASSWORD=<generated>
REDIS_PASSWORD=<generated>
HYPERSWITCH_API_KEY=<generated>
KILLBILL_API_KEY=<generated>
KILLBILL_API_SECRET=<generated>

3. Configure Rate Limiting

Traefik rate-limit middleware protects against abuse. The default is 100 requests per second per IP. Adjust for your use case:

traefik.yml
# In traefik.yml (static config)
entryPoints:
  https:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: "30s"
        writeTimeout: "30s"

# Or via Docker labels:
labels:
  - "traefik.http.middlewares.ratelimit.ratelimit.average=100"
  - "traefik.http.middlewares.ratelimit.ratelimit.burst=50"
  - "traefik.http.middlewares.ratelimit.ratelimit.period=1s"

4. Verify Webhook Signatures

Always verify webhook signatures to ensure events come from OpenPay and haven't been tampered with:

Node.js
// Node.js webhook signature verification
import crypto from "crypto";

function verifyWebhookSignature(payload, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
app.post("/webhooks/openpay", (req, res) => {
  const signature = req.headers["x-openpay-signature"];
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SIGNING_SECRET
  );

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Process the event...
  res.status(200).json({ received: true });
});

5. Set Up Monitoring

Health Checks

Monitor these endpoints and alert on failures:

  • GET http://hyperswitch:8080/health — Hyperswitch
  • GET http://killbill:8082/1.0/healthcheck — Kill Bill
  • GET http://nats:8222/healthz — NATS

Log Aggregation

All services log to stdout. Use Docker's logging drivers or a log aggregator (Loki, ELK, CloudWatch) to centralize logs. Set log levels to info in production.

Uptime Monitoring

Set up an external uptime monitor (e.g., UptimeRobot, Checkly) to ping your public endpoints and alert via email/Slack if the platform goes down.

6. Enable Database Backups

Schedule regular PostgreSQL backups:

bash
# Backup script (add to cron: 0 2 * * *)
docker exec postgres pg_dump -U postgres hyperswitch > \
  /backups/hyperswitch-$(date +%Y%m%d).sql

# Restore from backup
docker exec -i postgres psql -U postgres hyperswitch < \
  /backups/hyperswitch-20260726.sql

7. Restrict Network Access

Only expose ports that must be public. All internal services (PostgreSQL, Redis, NATS) should only be accessible via the Docker network:

PortExposureReason
443PublicHTTPS traffic (Traefik)
80PublicHTTP → HTTPS redirect
3000InternalDashboard (behind auth)
8080InternalTraefik dashboard
8081InternalHyperswitch API
5432Docker onlyPostgreSQL — never expose
6379Docker onlyRedis — never expose
4222Docker onlyNATS — never expose

Production Checklist

  • HTTPS enabled with valid TLS certificate
  • All default passwords replaced with strong, unique values
  • Webhook signature verification enabled
  • Rate limiting configured
  • Health check monitoring set up
  • Log aggregation configured
  • Database backups scheduled
  • Firewall rules restrict internal ports
  • Paystack live API keys configured
  • Uptime monitoring with external pinger