Skip to content

OpenWA Production Setup

Manaa-Soft edited this page Jul 22, 2026 · 15 revisions

OpenWA Production Setup

This guide covers deploying the OpenWA Gateway (WhatsApp Web API) in production. Choose Docker Compose (recommended) or Bare Metal.


Option 1: Docker Compose (Recommended)

Docker handles Chromium isolation, security hardening, volume persistence, and resource limits automatically.

Step 1: Install Docker & Docker Compose

# Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker

# Verify
docker --version
docker compose version

Step 2: Clone & Configure

cd /opt
git clone https://github.com/open-wa/wa-automate-nodejs.git openwa 2>/dev/null || echo "Upload your OpenWA-main folder"
cd /opt/openwa

Copy 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/

Step 3: Install Redis (Required for Webhook Queues)

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 --build

This 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: PONG

Secure 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: PONG

Step 4: Create .env File

cp .env.example .env
nano .env

Key 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=false

Note: If using Docker Compose with the full profile, set REDIS_HOST=redis (the container name) instead of localhost. REDIS_ENABLED=true is 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, add CSP_UPGRADE_INSECURE_REQUESTS=false to your .env and restart OpenWA:

echo 'CSP_UPGRADE_INSECURE_REQUESTS=false' >> ~/OpenWA/.env
sudo systemctl restart openwa

Step 5: Build and Start

SQLite (simplest):

docker compose up -d --build

PostgreSQL (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=false

Then start with the full profile (includes Redis):

docker compose --profile full up -d --build

This 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 full profile

Step 6: Verify It's Running

# 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 metal

Access dashboard at: http://your-server-ip:2785


Option 2: Bare Metal (Without Docker)

Step 1: Install Node.js 22

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs

# Verify
node -v  # should be v22.x

Step 2: Install System Dependencies

sudo 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-essential

Step 3: Install & Configure Redis

OpenWA 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: PONG

Secure 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: PONG

Step 4: Setup App

cd /opt/openwa

# Install dependencies
npm ci

# Create env file
cp .env.example .env
nano .env

SQLite — 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,minio

PostgreSQL — 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
SQL

Then 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=false

Important: Replace Change-Me-Strong-Password-123! with a real strong password in all places (.env and the CREATE USER command). Also set a real Redis password.

Step 5: Build & Run

# Build everything (API + dashboard)
npm run prod

Step 6: Run as a Service (systemd)

sudo 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 -f

Critical: The PUPPETEER_ARGS line 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/chromium

Step 7: Setup Nginx Reverse Proxy (for Domain/HTTPS)

sudo 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 nginx

For HTTPS:

sudo certbot --nginx -d api.yourdomain.com

Production .env Template

Copy 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

Quick Comparison

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

Redis Isolation for ERPNext Shared Server

⚠️ 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

ERPNext Redis Port Allocation

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

Setup for Shared Servers

  1. 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"
  2. 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
  3. 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
  4. 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
  5. Configure OpenWA's .env:

    REDIS_HOST=localhost
    REDIS_PORT=6385
    REDIS_PASSWORD=your-openwa-redis-password

Verification

# 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

⚠️ What NOT to Do

  • DO NOT add requirepass to /etc/redis/redis.conf — this breaks all ERPNext instances using that Redis
  • DO NOT set maxmemory-policy allkeys-lru globally — this can evict ERPNext's cached data
  • DO NOT change ports 6379, 6380, or 6381 — these are used by ERPNext

Troubleshooting

"Could not find Chrome" Error

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):

  1. Change systemd User=root to User=your-username
  2. Set PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium in .env
  3. Install Chrome for root: sudo npx puppeteer browsers install chrome

Engine Initialization Timed Out

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 -f

Why 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/shm for 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.

Session Disconnects After 1-2 Minutes

Set MEDIA_DOWNLOAD_ENABLED=false in .env — prevents media flood crashes on reconnect. See Troubleshooting.

OpenWA Not Reachable from Frappe

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,minio

Why include the site name? OpenWA resolves webhook target hosts. When Frappe's webhook URL uses a site name (e.g., your-site-name or erp.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, include minio as well.

Restart after changing:

sudo systemctl restart openwa

Dashboard Unreachable from Browser (ERR_CONNECTION_REFUSED)

Browser 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 openwa

Why 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:2785 from the server to your local machine. The moment you close VS Code, that tunnel collapses and direct browser access fails.

Port Already in Use

# Find what's using port 2785
sudo lsof -i :2785
# Kill it or change PORT in .env

Redis Connection Refused

OpenWA 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 6379

Common fixes:

  1. Redis not installed: sudo apt-get install -y redis-server
  2. Redis not started: sudo systemctl start redis-server
  3. Wrong password in .env — must match /etc/redis/redis.conf
  4. Using Docker but REDIS_HOST=localhost — change to REDIS_HOST=redis
  5. Redis bound to wrong interface — check bind in redis.conf

Docker Compose check:

docker compose exec redis redis-cli -a your-password ping

Webhooks Delayed or Lost Without Redis

If 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.

ERPNext Background Jobs Broken After Redis Config

If ERPNext's background jobs stop working after setting up OpenWA Redis:

Symptoms:

  • bench doctor shows 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:

  1. 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
  2. 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
  3. Verify ERPNext works:

    cd /opt/frappe-bench && bench doctor
  4. 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 Shows "Redis connection error" But Webhooks Still Work

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

Clone this wiki locally