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:
# 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 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:
# 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 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— HyperswitchGET http://killbill:8082/1.0/healthcheck— Kill BillGET 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:
# 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.sql7. Restrict Network Access
Only expose ports that must be public. All internal services (PostgreSQL, Redis, NATS) should only be accessible via the Docker network:
| Port | Exposure | Reason |
|---|---|---|
443 | Public | HTTPS traffic (Traefik) |
80 | Public | HTTP → HTTPS redirect |
3000 | Internal | Dashboard (behind auth) |
8080 | Internal | Traefik dashboard |
8081 | Internal | Hyperswitch API |
5432 | Docker only | PostgreSQL — never expose |
6379 | Docker only | Redis — never expose |
4222 | Docker only | NATS — 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