Repository navigation
OpenWA Production Setup
This guide covers deploying the OpenWA Gateway (WhatsApp Web API) in production. Choose Docker Compose (recommended) or Bare Metal.
Docker handles Chromium isolation, security hardening, volume persistence, and resource limits automatically.
# Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
# Verify
docker --version
docker compose versioncd /opt
git clone https://github.com/open-wa/wa-automate-nodejs.git openwa 2>/dev/null || echo "Upload your OpenWA-main folder"
cd /opt/openwaCopy your project files to the server (via scp, SFTP, or git):
scp -r "C:\Users\lenovo\Downloads\whatsapp MIT\OpenWA-main"/* user@your-server:/opt/openwa/OpenWA uses BullMQ (backed by Redis) for webhook delivery queuing. Without Redis, OpenWA falls back to direct HTTP delivery, which loses messages during reconnects and can't retry failed webhooks.
⚠️ Critical: ERPNext Shared Server? If ERPNext is on the same server, you MUST NOT modify/etc/redis/redis.conf— it will break ERPNext's background jobs. See Redis Isolation for ERPNext below.
Option A: Redis via Docker Compose (simplest)
The full Docker Compose profile includes Redis. Just use it:
docker compose --profile full up -d --buildThis starts Redis on port 6379 (internal, not exposed to host).
Option B: Redis on host machine (bare metal or separate)
# Install Redis
sudo apt-get install -y redis-server
# Start & enable
sudo systemctl start redis-server
sudo systemctl enable redis-server
# Verify it's running
redis-cli ping
# Should return: PONGSecure Redis (required for production):
⚠️ ERPNext Shared Server? Skip this step and follow Redis Isolation for ERPNext instead.
# Set a password and memory limit
sudo tee -a /etc/redis/redis.conf << 'EOF'
requirepass your-redis-password-here
maxmemory 256mb
maxmemory-policy allkeys-lru
bind 127.0.0.1
EOF
sudo systemctl restart redis-server
# Test with password
redis-cli -a your-redis-password-here ping
# Should return: PONGcp .env.example .env
nano .envKey values to set:
NODE_ENV=production
# Port the app binds to when run directly (bare metal / npm run start:prod).
# In bundled Docker Compose the container always listens on 2785;
# the API_PORT below is only the HOST-published port.
PORT=2785
# Host network interface for bare-metal / systemd. 0.0.0.0 binds to all
# network interfaces, allowing external browser access without relying on
# VS Code SSH port forwarding tunnels. Without this, Node.js defaults to
# 127.0.0.1 — only accessible from inside the server.
HOST=0.0.0.0
# Docker Compose only: host-side port mapped to the container's 2785
# (no effect on bare-metal run).
API_PORT=2785
LOG_LEVEL=info
DOMAIN=localhost
BASE_URL=http://your-server-ip:2785
CORS_ORIGINS=*
API_MASTER_KEY=your-strong-secret-key-here
ENGINE_TYPE=whatsapp-web.js
AUTO_START_SESSIONS=false
PUPPETEER_HEADLESS=true
PUPPETEER_ARGS=--no-sandbox,--disable-setuid-sandbox,--disable-dev-shm-usage,--disable-gpu
# Redis (required for webhook queues and caching)
REDIS_ENABLED=true # master switch — must be true for queue/cache
REDIS_BUILTIN=false # true = OpenWA auto-provisions Redis container
REDIS_HOST=localhost # use "redis" for Docker Compose
REDIS_PORT=6385 # use 6379 if OpenWA is alone; 6385 if shared with ERPNext
REDIS_PASSWORD=your-redis-password-here
REDIS_CONNECT_TIMEOUT_MS=5000
QUEUE_ENABLED=true # enable BullMQ webhook/ingress queues
# CACHE_ENABLED=true # optional — Redis-backed caching (pure optimization)
# =============================================================================
# WEBHOOK (SSRF — allow Frappe to reach OpenWA)
# =============================================================================
WEBHOOK_TIMEOUT=10000
WEBHOOK_MAX_RETRIES=3
WEBHOOK_RETRY_DELAY=5000
# Keep global SSRF protection ON; whitelist your Frappe site name + IP:
SSRF_ALLOWED_HOSTS=your-site-name,your-server-ip,localhost,127.0.0.1,minio
# your-site-name = your Frappe/ERPNext site name (e.g., my-site)
# your-server-ip = the IP used to connect from VM to host (e.g., 192.168.1.15)
# minio = MinIO service hostname (if used for file storage)
# =============================================================================
# MEDIA (disable to prevent memory floods on reconnect)
# =============================================================================
MEDIA_DOWNLOAD_ENABLED=false
STORE_EPHEMERAL_MESSAGES=false
# Set to false if accessing dashboard over plain HTTP (no SSL/Nginx proxy)
CSP_UPGRADE_INSECURE_REQUESTS=falseNote: If using Docker Compose with the
fullprofile, setREDIS_HOST=redis(the container name) instead oflocalhost.REDIS_ENABLED=trueis the master switch — without it, queue and cache are disabled regardless of other settings.
Note: If you access the dashboard over plain HTTP (e.g.,
http://SERVER_IP:2785) without an SSL certificate or Nginx reverse proxy, browsers will block dashboard scripts due to Content Security Policy (CSP) settings — resulting in a blank white page. To fix this, addCSP_UPGRADE_INSECURE_REQUESTS=falseto your.envand restart OpenWA:echo 'CSP_UPGRADE_INSECURE_REQUESTS=false' >> ~/OpenWA/.env sudo systemctl restart openwa
SQLite (simplest):
docker compose up -d --buildPostgreSQL (production recommended):
First update .env with database settings:
DATABASE_TYPE=postgres
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_NAME=openwa
DATABASE_USERNAME=openwa
DATABASE_PASSWORD=Change-Me-Strong-Password-123!
DATABASE_SYNCHRONIZE=falseThen start with the full profile (includes Redis):
docker compose --profile full up -d --buildThis spins up:
-
openwa-api(port 2785) — the main API + dashboard -
postgres(internal, port 5432) — PostgreSQL database -
docker-proxy— for container orchestration - Redis (internal, port 6379) — webhook queue backend
- Optionally MinIO with
fullprofile
# Check logs
docker compose logs -f openwa-api
# Test health
curl http://localhost:2785/api/health/ready
# Verify Redis connection
docker compose exec redis redis-cli ping # Docker
redis-cli ping # Bare metalAccess dashboard at: http://your-server-ip:2785
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verify
node -v # should be v22.xsudo apt-get update
sudo apt-get install -y \
chromium \
fonts-liberation \
libappindicator3-1 \
libasound2 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libcups2 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libx11-xcb1 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
xdg-utils \
python3 \
make \
g++ \
build-essentialOpenWA uses BullMQ (backed by Redis) for webhook delivery queuing. Without Redis, direct HTTP delivery loses messages during reconnects and can't retry failed webhooks.
⚠️ Critical: ERPNext Shared Server? If ERPNext is on the same server, you MUST NOT modify/etc/redis/redis.conf— it will break ERPNext's background jobs. See Redis Isolation for ERPNext below.
# Install Redis
sudo apt-get install -y redis-server
# Start & enable on boot
sudo systemctl start redis-server
sudo systemctl enable redis-server
# Verify it's running
redis-cli ping
# Should return: PONGSecure Redis (required for production):
⚠️ ERPNext Shared Server? Skip this step and follow Redis Isolation for ERPNext instead.
# Set a password and memory limit
sudo tee -a /etc/redis/redis.conf << 'EOF'
requirepass your-redis-password-here
maxmemory 256mb
maxmemory-policy allkeys-lru
bind 127.0.0.1
EOF
sudo systemctl restart redis-server
# Test with password
redis-cli -a your-redis-password-here ping
# Should return: PONGcd /opt/openwa
# Install dependencies
npm ci
# Create env file
cp .env.example .env
nano .envSQLite — set these in .env:
NODE_ENV=production
PORT=2785
HOST=0.0.0.0 # bind to all interfaces (required for external browser access)
PUPPETEER_HEADLESS=true
PUPPETEER_ARGS=--no-sandbox,--disable-setuid-sandbox,--disable-dev-shm-usage,--disable-gpu
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
SESSION_DATA_PATH=./data/sessions
DATABASE_TYPE=sqlite
ENGINE_TYPE=whatsapp-web.js
API_MASTER_KEY=your-strong-secret-key-here
# Redis (required for webhook queues and caching)
REDIS_ENABLED=true # master switch — must be true for queue/cache
REDIS_HOST=localhost
REDIS_PORT=6385 # use 6379 if OpenWA is alone; 6385 if shared with ERPNext
REDIS_PASSWORD=your-redis-password-here
REDIS_CONNECT_TIMEOUT_MS=5000
QUEUE_ENABLED=true # enable BullMQ webhook/ingress queues
# Webhook SSRF — whitelist your Frappe site name + IP
WEBHOOK_TIMEOUT=10000
WEBHOOK_MAX_RETRIES=3
WEBHOOK_RETRY_DELAY=5000
SSRF_ALLOWED_HOSTS=your-site-name,your-server-ip,localhost,127.0.0.1,minioPostgreSQL — first install and configure PostgreSQL, then set .env:
# Install PostgreSQL
sudo apt-get install -y postgresql
# Start & enable
sudo systemctl start postgresql
sudo systemctl enable postgresql
# Create user, database, and schema
sudo -u postgres psql << 'SQL'
CREATE USER openwa WITH PASSWORD 'Change-Me-Strong-Password-123!';
CREATE DATABASE openwa OWNER openwa;
CREATE SCHEMA IF NOT EXISTS openwa;
GRANT ALL PRIVILEGES ON DATABASE openwa TO openwa;
\q
SQLThen set .env:
NODE_ENV=production
PORT=2785
HOST=0.0.0.0 # bind to all interfaces (required for external browser access)
PUPPETEER_HEADLESS=true
PUPPETEER_ARGS=--no-sandbox,--disable-setuid-sandbox,--disable-dev-shm-usage,--disable-gpu
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
SESSION_DATA_PATH=./data/sessions
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=openwa
DATABASE_USERNAME=openwa
DATABASE_PASSWORD=Change-Me-Strong-Password-123!
DATABASE_SYNCHRONIZE=false
ENGINE_TYPE=whatsapp-web.js
API_MASTER_KEY=your-strong-secret-key-here
# Redis (required for webhook queues and caching)
REDIS_ENABLED=true # master switch — must be true for queue/cache
REDIS_HOST=localhost
REDIS_PORT=6385 # use 6379 if OpenWA is alone; 6385 if shared with ERPNext
REDIS_PASSWORD=your-redis-password-here
REDIS_CONNECT_TIMEOUT_MS=5000
QUEUE_ENABLED=true # enable BullMQ webhook/ingress queues
# Webhook SSRF — whitelist your Frappe site name + IP
WEBHOOK_TIMEOUT=10000
WEBHOOK_MAX_RETRIES=3
WEBHOOK_RETRY_DELAY=5000
SSRF_ALLOWED_HOSTS=your-site-name,your-server-ip,localhost,127.0.0.1,minio
# your-site-name = your Frappe/ERPNext site name (e.g., my-site)
# your-server-ip = the IP used to connect from VM to host (e.g., 192.168.1.15)
# minio = MinIO service hostname (if used for file storage)
# Set to false if accessing dashboard over plain HTTP (no SSL/Nginx proxy)
CSP_UPGRADE_INSECURE_REQUESTS=falseImportant: Replace
Change-Me-Strong-Password-123!with a real strong password in all places (.envand theCREATE USERcommand). Also set a real Redis password.
# Build everything (API + dashboard)
npm run prodsudo tee /etc/systemd/system/openwa.service << 'EOF'
[Unit]
Description=OpenWA WhatsApp API
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/openwa
ExecStart=/usr/bin/node dist/main
Restart=always
RestartSec=5
Environment=NODE_ENV=production
Environment=PUPPETEER_ARGS="--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage"
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable openwa
sudo systemctl start openwa
# Check status
sudo systemctl status openwa
journalctl -u openwa -fCritical: The
PUPPETEER_ARGSline is required. Without it, Chrome's security sandbox is blocked by systemd and the WhatsApp engine silently freezes until OpenWA times out with"Engine initialization timed out". See Engine Initialization Timed Out below.
If Puppeteer can't find Chrome, change User=root to your username or set PUPPETEER_EXECUTABLE_PATH:
# Option A: Run as your user (finds Chrome in ~/.cache/puppeteer)
sudo sed -i 's/User=root/User=your-username/' /etc/systemd/system/openwa.service
# Option B: Point to system Chromium
# Add to .env: PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromiumsudo apt-get install -y nginx certbot python3-certbot-nginx
sudo tee /etc/nginx/sites-available/openwa << 'EOF'
server {
listen 80;
server_name api.yourdomain.com;
client_max_body_size 25m;
location / {
proxy_pass http://127.0.0.1:2785;
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;
proxy_read_timeout 300s;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/openwa /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginxFor HTTPS:
sudo certbot --nginx -d api.yourdomain.comCopy this as your starting point:
# =============================================================================
# CORE
# =============================================================================
NODE_ENV=production
# Port the app binds to when run directly (bare metal / npm run start:prod).
# In bundled Docker Compose the container always listens on 2785;
# the API_PORT below is only the HOST-published port.
PORT=2785
# Host network interface for bare-metal / systemd. 0.0.0.0 binds to all
# network interfaces, allowing external browser access without relying on
# VS Code SSH port forwarding tunnels. Without this, Node.js defaults to
# 127.0.0.1 — only accessible from inside the server.
HOST=0.0.0.0
# Docker Compose only: host-side port mapped to the container's 2785
# (no effect on bare-metal run).
API_PORT=2785
LOG_LEVEL=info
DOMAIN=localhost
CORS_ORIGINS=*
AUTO_START_SESSIONS=true
# =============================================================================
# ENGINE
# =============================================================================
ENGINE_TYPE=whatsapp-web.js
SESSION_DATA_PATH=./data/sessions
PUPPETEER_HEADLESS=true
PUPPETEER_ARGS=--no-sandbox,--disable-setuid-sandbox,--disable-dev-shm-usage,--disable-gpu
# PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium # uncomment if Chrome not found
# =============================================================================
# DATABASE (SQLite or PostgreSQL)
# =============================================================================
DATABASE_TYPE=sqlite
DATABASE_SYNCHRONIZE=false
# --- PostgreSQL (uncomment and set password) ---
# DATABASE_TYPE=postgres
# DATABASE_HOST=localhost # use "postgres" for Docker Compose
# DATABASE_PORT=5432
# DATABASE_NAME=openwa
# DATABASE_USERNAME=openwa
# DATABASE_PASSWORD=Change-Me-Strong-Password-123!
# DATABASE_SYNCHRONIZE=false
# =============================================================================
# SECURITY
# =============================================================================
API_MASTER_KEY=your-strong-secret-key-here
# =============================================================================
# WEBHOOK (SSRF — allow Frappe to reach OpenWA)
# =============================================================================
WEBHOOK_TIMEOUT=10000
WEBHOOK_MAX_RETRIES=3
WEBHOOK_RETRY_DELAY=5000
# Keep global SSRF protection ON; whitelist your Frappe site name + IP:
SSRF_ALLOWED_HOSTS=your-site-name,your-server-ip,localhost,127.0.0.1,minio
# your-site-name = your Frappe/ERPNext site name (e.g., my-site)
# your-server-ip = the IP used to connect from VM to host (e.g., 192.168.1.15)
# minio = MinIO service hostname (if used for file storage)
# =============================================================================
# REDIS (required for BullMQ webhook queues and caching)
# =============================================================================
REDIS_ENABLED=true # master switch — must be true for queue/cache
# REDIS_BUILTIN=false # true = OpenWA auto-provisions Redis container
REDIS_HOST=localhost # use "redis" for Docker Compose
REDIS_PORT=6385 # use 6379 if OpenWA is alone; 6385 if shared with ERPNext
REDIS_PASSWORD=your-redis-password-here
# REDIS_USERNAME= # uncomment if using ACL auth
REDIS_CONNECT_TIMEOUT_MS=5000
QUEUE_ENABLED=true # enable BullMQ webhook/ingress queues
# CACHE_ENABLED=true # optional — Redis-backed caching (pure optimization)
# REDIS_CACHE_DB=1 # Redis DB for cache (separate from queue on DB 0)
# WEBHOOK_WORKER_CONCURRENCY=10 # parallel webhook deliveries
# INGRESS_WORKER_CONCURRENCY=10 # parallel inbound event processing
# =============================================================================
# MEDIA (disable to prevent memory floods on reconnect)
# =============================================================================
MEDIA_DOWNLOAD_ENABLED=false
STORE_EPHEMERAL_MESSAGES=false
# Set to false if accessing dashboard over plain HTTP (no SSL/Nginx proxy)
# Without this, browsers block dashboard scripts causing a blank white page
CSP_UPGRADE_INSECURE_REQUESTS=false| Step | Docker | Bare Metal |
|---|---|---|
| Prereqs | Docker + Compose | Node.js 22 + Chromium |
| Config |
.env file |
.env file |
| Build | docker compose up -d --build |
npm run prod |
| Access | http://server:2785 |
http://server:2785 |
| Service | restart: unless-stopped |
systemd unit |
| TLS | Add nginx/caddy reverse proxy | nginx + certbot |
⚠️ When to apply: ERPNext and OpenWA on the same server (e.g., single VPS with Frappe Bench + OpenWA). If they're on separate servers, you can safely use the standard Redis setup.
ERPNext's Frappe framework requires Redis for:
-
Queues: 3 separate Redis instances for background job processing (
redis_queue) -
Cache: Session cache, page cache, DocType cache (
redis_cache) -
Realtime: WebSocket connections for live updates (
redis_socketio)
OpenWA uses Redis for BullMQ webhook queues. If both share the same Redis instance or if you modify the global /etc/redis/redis.conf, you risk:
- Breaking ERPNext's background jobs (enqueuing, retries, scheduling)
- ERPNext losing session/cache data
- WebSocket connections dropping
| Instance | Port | Purpose |
|---|---|---|
| Default | 6379 |
ERPNext queues (redis_queue) |
| Cache | 6380 |
ERPNext cache (redis_cache) |
| Socketio | 6381 |
ERPNext WebSocket (redis_socketio) |
| OpenWA | 6385 |
OpenWA BullMQ webhook queues |
-
Find OpenWA's Redis password (you set this during ERPNext setup):
grep -r "redis_queue" /opt/frappe-bench/sites/*/site_config.json # Look for "redis_queue": "redis://:your-password@localhost:6379"
-
Create dedicated OpenWA Redis config:
sudo mkdir -p /etc/redis sudo tee /etc/redis/redis-openwa.conf << 'EOF' # Configuration for OpenWA BullMQ port 6385 bind 127.0.0.1 daemonize no supervised systemd pidfile /run/redis/redis-server-openwa.pid logfile /var/log/redis/redis-server-openwa.log dir /var/lib/redis dbfilename dump-openwa.rdb # Authentication and sizing requirepass your-openwa-redis-password maxmemory 256mb maxmemory-policy allkeys-lru # Persistence appendonly yes appendfsync everysec EOF
-
Create systemd service:
sudo tee /etc/systemd/system/redis-openwa.service << 'EOF' [Unit] Description=Advanced key-value store After=network.target Documentation=http://redis.io/documentation, man:redis-server(1) [Service] Type=notify ExecStart=/usr/bin/redis-server /etc/redis/redis-openwa.conf --supervised systemd --daemonize no PIDFile=/run/redis/redis-server.pid TimeoutStopSec=0 Restart=always User=redis Group=redis RuntimeDirectory=redis RuntimeDirectoryMode=2755 UMask=007 PrivateTmp=true LimitNOFILE=65535 PrivateDevices=true ProtectHome=true ProtectSystem=strict ReadWritePaths=-/var/lib/redis ReadWritePaths=-/var/log/redis ReadWritePaths=-/var/run/redis CapabilityBoundingSet= LockPersonality=true MemoryDenyWriteExecute=true NoNewPrivileges=true PrivateUsers=true ProtectClock=true ProtectControlGroups=true ProtectHostname=true ProtectKernelLogs=true ProtectKernelModules=true ProtectKernelTunables=true ProtectProc=invisible RemoveIPC=true RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX RestrictNamespaces=true RestrictRealtime=true RestrictSUIDSGID=true SystemCallArchitectures=native SystemCallFilter=@system-service SystemCallFilter=~ @privileged @resources ReadWriteDirectories=-/etc/redis NoExecPaths=/ ExecPaths=/usr/bin/redis-server /usr/lib /lib [Install] WantedBy=multi-user.target Alias=redis-openwa.service EOF
-
Start OpenWA's Redis:
sudo systemctl daemon-reload sudo systemctl enable redis-openwa sudo systemctl start redis-openwa # Verify it's running redis-cli -p 6385 -a your-openwa-redis-password ping # Should return: PONG
-
Configure OpenWA's
.env:REDIS_HOST=localhost REDIS_PORT=6385 REDIS_PASSWORD=your-openwa-redis-password
# OpenWA's Redis (port 6385)
redis-cli -p 6385 -a your-openwa-redis-password ping
# Should return: PONG
# ERPNext's Redis (port 6379) - should NOT be affected
redis-cli -p 6379 -a your-erpnext-redis-password ping
# Should return: PONG
# Verify ERPNext queues still work
cd /opt/frappe-bench && bench doctor-
DO NOT add
requirepassto/etc/redis/redis.conf— this breaks all ERPNext instances using that Redis -
DO NOT set
maxmemory-policy allkeys-lruglobally — this can evict ERPNext's cached data - DO NOT change ports 6379, 6380, or 6381 — these are used by ERPNext
Puppeteer looks for Chrome in the running user's cache directory:
- Root:
/root/.cache/puppeteer/ - Your user:
/home/your-username/.cache/puppeteer/
Fix (pick one):
- Change systemd
User=roottoUser=your-username - Set
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromiumin.env - Install Chrome for root:
sudo npx puppeteer browsers install chrome
Logs show "Engine initialization timed out for session <name>" — Chrome launched but froze in the background.
Root cause: systemd blocks Chrome's security sandbox by default. Without the right flags, Chrome silently hangs until OpenWA gives up.
Fix: Add PUPPETEER_ARGS to your systemd service file:
[Service]
...
Environment=PUPPETEER_ARGS="--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage"Then restart:
sudo systemctl daemon-reload
sudo systemctl restart openwa
journalctl -u openwa -fWhy these flags are needed:
-
--no-sandbox— Chrome's sandbox requires kernel capabilities (namespaces, seccomp) that systemd restricts. Without it, Chrome freezes silently. -
--disable-setuid-sandbox— Backup sandbox mechanism that also fails under systemd's user service model. -
--disable-dev-shm-usage— Chrome uses/dev/shmfor shared memory. Systemd services often get a tiny or restricted/dev/shm, causing crashes when rendering pages (like dynamic header images).
These flags are standard for any headless Chromium in a background service, Docker container, or CI pipeline.
Set MEDIA_DOWNLOAD_ENABLED=false in .env — prevents media flood crashes on reconnect. See Troubleshooting.
Add your Frappe site name and server IP to SSRF_ALLOWED_HOSTS in OpenWA's .env:
# In ~/OpenWA/.env
SSRF_ALLOWED_HOSTS=your-site-name,your-server-ip,localhost,127.0.0.1,minioWhy include the site name? OpenWA resolves webhook target hosts. When Frappe's webhook URL uses a site name (e.g.,
your-site-nameorerp.example.com), that hostname must be in the allow list — otherwise it's blocked as an internal address. If you use MinIO for file storage, includeminioas well.
Restart after changing:
sudo systemctl restart openwaBrowser shows ERR_CONNECTION_REFUSED when accessing http://SERVER_IP:2785, but it works from inside the server.
Root cause: Without HOST=0.0.0.0 in .env, Node.js defaults to binding to 127.0.0.1 — only accessible from inside the server. If you were using VS Code with Remote SSH, VS Code automatically created an SSH port tunnel that forwarded 127.0.0.1:2785 to your local browser. Closing VS Code collapsed that tunnel.
Fix: Add HOST=0.0.0.0 to your .env and restart:
echo 'HOST=0.0.0.0' >> ~/OpenWA/.env
sudo systemctl restart openwaWhy VS Code masked this: VS Code's Remote SSH extension silently forwards ports. When you open a browser from VS Code, it tunnels
127.0.0.1:2785from the server to your local machine. The moment you close VS Code, that tunnel collapses and direct browser access fails.
# Find what's using port 2785
sudo lsof -i :2785
# Kill it or change PORT in .envOpenWA logs show Redis connection error or webhooks fall back to direct delivery.
# Check if Redis is running
redis-cli ping
# If no response:
sudo systemctl status redis-server
# Check Redis config
redis-cli CONFIG GET requirepass
# Should show your password
# Check if port is listening
ss -tlnp | grep 6379Common fixes:
- Redis not installed:
sudo apt-get install -y redis-server - Redis not started:
sudo systemctl start redis-server - Wrong password in
.env— must match/etc/redis/redis.conf - Using Docker but
REDIS_HOST=localhost— change toREDIS_HOST=redis - Redis bound to wrong interface — check
bindinredis.conf
Docker Compose check:
docker compose exec redis redis-cli -a your-password pingIf you skipped Redis setup, OpenWA falls back to direct HTTP delivery. This means:
- No retry on failure (webhooks are fire-and-forget)
- Messages lost during reconnect cycles
- No parallel delivery (one slow receiver blocks all others)
Fix: Install Redis and set the REDIS_* env vars in .env, then restart OpenWA.
If ERPNext's background jobs stop working after setting up OpenWA Redis:
Symptoms:
-
bench doctorshows queue issues - Email alerts stop sending
- Scheduled reports fail
- DocType changes don't propagate
Root Cause: You modified /etc/redis/redis.conf (added requirepass or changed settings) which affects ALL Redis instances on that port.
Fix:
-
Check if ERPNext Redis is affected:
# Test ERPNext's Redis (usually port 6379) redis-cli -p 6379 ping # If it asks for password or fails, it's broken
-
Restore ERPNext's Redis config:
# Backup current config sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.bak # Restore original (without requirepass) sudo sed -i '/^requirepass/d' /etc/redis/redis.conf sudo sed -i '/^maxmemory/d' /etc/redis/redis.conf sudo sed -i '/^maxmemory-policy/d' /etc/redis/redis.conf sudo systemctl restart redis-server
-
Verify ERPNext works:
cd /opt/frappe-bench && bench doctor
-
Move OpenWA to isolated Redis (if not done already): Follow Setup for Shared Servers above to create a dedicated Redis instance for OpenWA on port
6385.
OpenWA falls back to direct HTTP delivery when Redis is unavailable. This is a warning, not an error. Webhooks will work but:
- No retry on failure (fire-and-forget)
- Messages lost during reconnect cycles
- No parallel delivery
Fix: Ensure OpenWA's Redis is running:
sudo systemctl status redis-openwa
redis-cli -p 6385 -a your-openwa-redis-password ping