Self-Hosted VPS Deployment

Deploy your ChainPay backend to a single VPS (Virtual Private Server) using Docker Compose and Caddy. Frontends are served statically via Vercel for maximum reliability and global edge delivery.

1. The Container Stack

The production stack runs in a isolated virtual network. The reverse proxy container exposes ports 80 and 443 to handle HTTPS routing and SSL certificates automatically:

version: '3.8'
services:
  # Database
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: chainpay
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: chainpay
    volumes:
      - postgres_prod_data:/var/lib/postgresql/data
    restart: always

  # Redis Queue Cache
  redis:
    image: redis:alpine
    restart: always

  # Inngest Server
  inngest:
    image: inngest/inngest:latest
    command: >
      inngest start --host 0.0.0.0 --port 8288 
      --postgres-uri postgresql://chainpay:${DB_PASSWORD}@postgres:5432/chainpay?sslmode=disable 
      --redis-uri redis://redis:6379/0
    environment:
      - INNGEST_EVENT_KEY=${INNGEST_EVENT_KEY}
      - INNGEST_SIGNING_KEY=${INNGEST_SIGNING_KEY}
    restart: always

  # Express REST API
  api:
    image: ${API_IMAGE:-chainpay-api-prod}
    environment:
      - DATABASE_URL=postgres://chainpay:${DB_PASSWORD}@postgres:5432/chainpay
      - INNGEST_BASE_URL=http://inngest:8288
      - INNGEST_EVENT_KEY=${INNGEST_EVENT_KEY}
      - INNGEST_SIGNING_KEY=${INNGEST_SIGNING_KEY}
    command: sh -c "pnpm --filter api db:push && pnpm --filter api run start"
    restart: always

  # Caddy reverse proxy for SSL
  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    restart: always

2. Setting up Credentials & Starting

Use our built-in script to generate database passwords, secure API keys, and Caddy password hashes:

# Run the setup script to generate secure keys and basic-auth credentials
node scripts/generate-env.js

# Edit `.env.production` to insert your domains and RPC base urls:
# API_DOMAIN=api.nodecheckout.com
# INNGEST_DOMAIN=inngest.nodecheckout.com
# RPC_BASE_URL=https://base-sepolia.g.alchemy.com/v2/YOUR_ALCHEMY_KEY

# Spin up all containerized backend services
docker compose -f docker-compose.prod.yml up -d --build
🔒 Dashboard Basic Authentication

By default, the self-hosted Inngest Dashboard exposes event telemetry, function logs, and payment states without authentication. We place it behind Caddy's basic authentication. You must configure the hashed password generated by the script inside your VPS environments.

3. Syncing Inngest Functions

Once the VPS container stack is running:

  1. Access your secure dashboard at https://inngest.nodecheckout.com.
  2. Login using username admin and your generated dashboard cleartext password.
  3. In the "Apps" configuration page, trigger a sync by entering your Express API SDK endpoint: http://api:4000/api/inngest.
  4. Inngest will scan the endpoints and register your payment workflows.