Run OFFER-HUB Orchestrator on your own server with full control over data, uptime, and configuration. This guide covers everything from initial setup to ongoing maintenance using Docker.
If you haven't configured your environment variables yet, review the Configuration page first.
Prerequisites
Before you begin, make sure your host machine meets the following requirements:
v20.10 or later
v2.0 or later
15+ (or managed service like Supabase, Neon, Railway)
7+ (or managed service like Upstash, Railway)
A registered domain name (recommended for production)
At least and
Verify that Docker is installed and running:
$ docker --version && docker compose version
On older installations you may need to use docker-compose (with a hyphen) instead of docker compose.
Architecture Overview
The OFFER-HUB deployment consists of:
Docker Setup
Create a deployment directory:
$ mkdir offer-hub-deploy && cd offer-hub-deploy
Your directory will contain:
offer-hub-deploy/
├── docker-compose.yml
├── .env
├── nginx.conf (optional)
└── certs/ (optional, for TLS)
Create a docker-compose.yml file:
version: "3.9"
services:
postgres:
image: postgres:15-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: offerhub
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-offerhub_secret}
POSTGRES_DB: offerhub_db
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U offerhub"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- offerhub
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- offerhub
orchestrator:
image: offerhub/orchestrator:latest
ports:
- "4000:4000"
env_file:
- .env
environment:
- DATABASE_URL=postgresql://offerhub:${POSTGRES_PASSWORD:-offerhub_secret}@postgres:5432/offerhub_db
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped
networks:
- offerhub
frontend:
image: offerhub/frontend:latest
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://orchestrator:4000
- NEXT_PUBLIC_STELLAR_NETWORK=${STELLAR_NETWORK:-testnet}
depends_on:
orchestrator:
condition: service_healthy
restart: unless-stopped
networks:
- offerhub
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- frontend
- orchestrator
restart: unless-stopped
networks:
- offerhub
volumes:
postgres_data:
redis_data:
networks:
offerhub:
driver: bridge
The nginx service is optional for development but strongly recommended for production. It handles TLS termination and reverse-proxying.
If you prefer managed databases, remove postgres and redis services and update the environment:
services:
orchestrator:
image: offerhub/orchestrator:latest
ports:
- "4000:4000"
environment:
# Supabase PostgreSQL
- DATABASE_URL=postgresql://postgres:password@db.xxxx.supabase.co:5432/postgres?sslmode=require
# Upstash Redis
- REDIS_URL=rediss://default:xxx@us1-xxx.upstash.io:6379
# ... rest of config
To build images locally instead of pulling pre-built ones:
$ git clone https://github.com/OFFER-HUB/offer-hub-orchestrator.git && cd offer-hub-orchestrator
$ docker build -t offerhub/orchestrator:latest .
Environment Variables
Create a .env file in the same directory as your docker-compose.yml.
Variable Description NODE_ENVRuntime environment: development, staging, production DATABASE_URLPostgreSQL connection string REDIS_URLRedis connection string OFFERHUB_MASTER_KEYMaster key for creating API keys WALLET_ENCRYPTION_KEY64 hex chars - AES-256-GCM key for wallet encryption TRUSTLESS_API_KEYTrustless Work API key for escrow PLATFORM_USER_IDPlatform user ID for escrow operations PUBLIC_BASE_URLYour Orchestrator's public URL
Variable Default Description PORT4000HTTP server port LOG_LEVELinfoLogging level: debug, info, warn, error PAYMENT_PROVIDERcryptoPayment mode: crypto or airtm STELLAR_NETWORKtestnetStellar network: testnet or mainnet STELLAR_USDC_ISSUERTestnet issuer USDC asset issuer address
Never commit your .env file to version control. Treat every value as a secret.
# Server
NODE_ENV=production
PORT=4000
LOG_LEVEL=info
# Database (PostgreSQL)
DATABASE_URL=postgresql://offerhub:your_password@postgres:5432/offerhub_db
POSTGRES_PASSWORD=your_password
# Redis
REDIS_URL=redis://redis:6379
# Authentication
OFFERHUB_MASTER_KEY=your-secure-master-key-at-least-32-chars
# Payment Provider
PAYMENT_PROVIDER=crypto
WALLET_ENCRYPTION_KEY=your-64-character-hex-key-here-generate-with-crypto-randomBytes
# Trustless Work (Escrow)
TRUSTLESS_API_KEY=your_trustless_api_key
TRUSTLESS_WEBHOOK_SECRET=your_webhook_secret
# Platform Identity
PLATFORM_USER_ID=usr_platform
# Stellar
STELLAR_NETWORK=mainnet
STELLAR_USDC_ISSUER=GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN
# Public URL
PUBLIC_BASE_URL=https://api.yourdomain.com
# Frontend (if running frontend in same compose)
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
NEXT_PUBLIC_STELLAR_NETWORK=mainnet
# Generate OFFERHUB_MASTER_KEY
openssl rand -base64 32
# Generate WALLET_ENCRYPTION_KEY (64 hex characters)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Running in Production
Once your .env file is ready, bring everything up in detached mode:
Watch the logs to confirm all services start cleanly:
Apply Prisma migrations before the first launch (or after pulling a new version):
$ docker compose exec orchestrator npx prisma migrate deploy
Always back up your database before running migrations in production.
Make sure NODE_ENV=production is set in your .env file. This enables:
Optimised builds and minification
Stricter error handling
Disabled development-only logging
Production-level security defaults
For production deployments you should terminate TLS at the reverse proxy. If you use the nginx service from the Compose file above, place your certificate and key in the certs/ directory and update nginx.conf accordingly.
Example nginx.conf:
events {
worker_connections 1024;
}
http {
upstream orchestrator {
server orchestrator:4000;
}
upstream frontend {
server frontend:3000;
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# API routes
location /api {
proxy_pass http://orchestrator;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Frontend routes
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
}
}
}
Health Checks
The Orchestrator exposes a /health endpoint that returns the current service status. The Docker Compose file already configures a health check against it.
$ curl http://localhost:4000/health
Expected response:
{
"status": "ok",
"timestamp": "2026-02-25T12:00:00.000Z",
"services": {
"database": "healthy",
"redis": "healthy",
"stellar": "connected"
}
}
Docker reports health status directly:
A healthy deployment shows (healthy) next to each service. If a service is (unhealthy), inspect its logs:
$ docker compose logs orchestrator --tail 50
Point an uptime monitor (e.g. UptimeRobot, Betterstack) at /health.
Set up alerts for non-200 responses or response times above .
Monitor disk usage — logs, database, and Docker images can accumulate over time.
Track Redis memory usage — BullMQ jobs can grow if not cleaned up.
Background Jobs
The Orchestrator uses BullMQ for background job processing:
Queue Purpose blockchain-monitorMonitor Stellar for incoming deposits transaction-submitSubmit signed Stellar transactions webhook-deliverySend webhooks to registered endpoints balance-syncReconcile on-chain and off-chain balances
Monitor job queues:
# View Redis keys
docker compose exec redis redis-cli keys "bull:*"
# Check queue length
docker compose exec redis redis-cli llen "bull:webhook-delivery:wait"
Updating
$ docker compose up -d --remove-orphans
This pulls the newest images, recreates only the containers that changed, and removes any services no longer defined in the Compose file.
The recommended update procedure:
before any update.
Pull the latest images.
Run migrations.
Restart the stack.
# 1. Backup database
docker compose exec postgres pg_dump -U offerhub offerhub_db > backup-$(date +%Y%m%d).sql
# 2. Pull latest images
docker compose pull
# 3. Restart with new images
docker compose up -d --remove-orphans
# 4. Run any new migrations
docker compose exec orchestrator npx prisma migrate deploy
# 5. Verify health
curl http://localhost:4000/health
If something goes wrong, roll back to a specific image tag:
services:
orchestrator:
image: offerhub/orchestrator:1.2.0 # pin to a known-good version
Then restart:
Security Checklist
Before going to production, verify:
Troubleshooting
# Check logs
docker compose logs orchestrator --tail 100
# Common issues:
# - DATABASE_URL invalid
# - WALLET_ENCRYPTION_KEY wrong length
# - Redis not reachable
# Test PostgreSQL connection
docker compose exec orchestrator npx prisma db pull
# Check if migrations are applied
docker compose exec orchestrator npx prisma migrate status
# Check webhook queue
docker compose exec redis redis-cli lrange "bull:webhook-delivery:failed" 0 10
# Verify webhook endpoint is reachable
curl -X POST https://your-webhook-url/test
Next Steps
Configuration — Full environment variable reference
Quick Start — Make your first API call
API Reference — Explore all endpoints
Webhooks — Set up event notifications