developer
How to Deploy a Production-Ready Payment Gateway on AWS or DigitalOcean
Step-by-step production deployment guide for self-hosted UPI payment gateways using Docker Compose, Caddy TLS, PostgreSQL, and Redis caching.
Deploying private payment infrastructure requires high-availability guarantees, immutable logging, encrypted traffic, and isolated microservice boundaries. By utilizing Docker Compose alongside a modern reverse proxy like Caddy, developers can deploy a robust, PCI-compliant, zero-downtime payment switch on AWS EC2 or DigitalOcean in under thirty minutes.
Production Infrastructure Requirements
Before launching containers, verify that your host server meets baseline security and performance standards:
- Host Operating System: Ubuntu 24.04 LTS x64 (Minimal installation)
- Minimum Compute: 4 vCPUs, 8 GB ECC RAM, 100 GB NVMe Storage
- Firewall Configuration (UFW):
- Inbound Port 80 (HTTP redirect to HTTPS)
- Inbound Port 443 (HTTPS secure payment traffic)
- Inbound Port 22 (SSH restricted to internal VPN IP addresses)
- All internal microservice ports (5432, 6379, 3000) bound exclusively to the private Docker bridge network
Docker Compose Production Stack
Below is the production-grade multi-container topology configured for seamless scaling:
# docker-compose.prod.yml
version: '3.8'
services:
caddy:
image: caddy:2.8-alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- gateway_net
depends_on:
- api-gateway
api-gateway:
image: vyapargateway/core-switch:latest
restart: always
env_file: .env.production
environment:
NODE_ENV: production
PORT: 3000
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379/0
networks:
- gateway_net
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
deploy:
resources:
limits:
cpus: '2.0'
memory: 4096M
postgres:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pg_data:/var/lib/postgresql/data
networks:
- gateway_net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7.2-alpine
restart: always
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
volumes:
- redis_data:/data
networks:
- gateway_net
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
gateway_net:
driver: bridge
volumes:
caddy_data:
caddy_config:
pg_data:
redis_data:
Caddy Reverse Proxy & Auto-TLS Configuration
Caddy automatically provisions and renews TLS certificates from Let’s Encrypt or ZeroSSL without requiring cron scripts or certbot interventions:
# Caddyfile
api.yourdomain.com {
encode zstd gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
reverse_proxy api-gateway:3000 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
Database Tuning & Persistence
Default PostgreSQL settings are tuned for low-memory environments. For a high-speed payment ledger on an 8 GB host, mount a custom postgresql.conf tuning parameters for write-heavy transactions:
# postgresql.conf snippet for payment switches
shared_buffers = 2GB
effective_cache_size = 6GB
maintenance_work_mem = 512MB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 10485kB
min_wal_size = 1GB
max_wal_size = 4GB
Automated Backup and Failover Runbook
Payment transaction tables must be preserved with zero data loss. Schedule an hourly automated encrypted backup script using WAL archiving and S3 offsite replication:
#!/bin/bash
# /opt/scripts/backup_ledger.sh
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/opt/backups"
FILENAME="pg_dump_ledger_${TIMESTAMP}.sql.gz"
# Perform compressed dump
docker exec -t $(docker ps -qf "name=postgres") pg_dump -U $POSTGRES_USER $POSTGRES_DB | gzip > "${BACKUP_DIR}/${FILENAME}"
# Encrypt with OpenSSL using server key
openssl enc -aes-256-cbc -salt -in "${BACKUP_DIR}/${FILENAME}" -out "${BACKUP_DIR}/${FILENAME}.enc" -pass file:/etc/ssl/backup.key
# Sync to encrypted offsite cloud storage
aws s3 cp "${BACKUP_DIR}/${FILENAME}.enc" s3://fintech-cold-backups/daily/
# Remove local unencrypted file
rm "${BACKUP_DIR}/${FILENAME}"
System Health and Telemetry Monitoring
To ensure SLA compliance:
- Prometheus Node Exporter: Monitored on localhost for memory starvation and CPU thread saturation.
- Webhook Health Endpoints: Periodic external pinging of
/v1/healthchecking database connection pool viability. - Log Aggregation: Standard Docker stdout shipping to Loki or CloudWatch logs with sensitive payload masking enabled.
Self-hosting your payment gateway provides sovereign control over transaction ledgers, eliminates third-party platform lock-in, and reduces operational infrastructure overhead to a predictable server hosting bill.
Direct answers
Frequently asked questions
- What VPS hardware specifications are recommended for self-hosting a payment switch?
- For processing up to 20,000 daily UPI transactions, a standard 4 vCPU, 8 GB RAM instance (such as DigitalOcean Premium Droplet or AWS EC2 c6i.xlarge) with NVMe SSD storage delivers sub-15ms webhook execution and reliable Redis pub/sub queueing.
- Why use Caddy instead of Nginx for fintech container routing?
- Caddy provides automated zero-configuration Let's Encrypt TLS provisioning, native HTTP/3 support, memory-safe Go concurrency, and dynamic reverse-proxy health checks out of the box with zero downtime SSL certificate renewal.
- How are PostgreSQL database credentials and API secrets protected in production?
- Production secrets should never be committed into docker-compose files. Store sensitive variables in an external `.env.production` file with strict POSIX file permissions (chmod 600) or inject them via AWS Secrets Manager or HashiCorp Vault during container orchestration.
Build your payment flow
Explore the API and browser-only merchant tools.
Create UPI checkout orders, verify signed events, or test the free calculators and generators without exposing credentials.