Skip to main content

Installation

Quick Start​

Download the binary for your platform from GitHub Releases and run:

./waxum --token mysecrettoken

That's it. No database configured — Waxum drops a waxum.db SQLite file next to the binary and stores everything (sessions, webhooks, DLQ) there. Zero config.

Scale-out or need Postgres / MySQL? Pass a DATABASE_URL:

./waxum --token mysecrettoken --db postgres://user:pass@localhost:5432/waxum
./waxum --token mysecrettoken --db mysql://user:pass@localhost:3306/waxum

Schema migrations run automatically at startup on every backend.

Docker Compose​

git clone https://github.com/imtaqin/waxum.git
cd waxum

Create a .env file:

# Database — choose one:
DATABASE_URL=mysql://user:password@host:3306/wars
# DATABASE_URL=postgres://user:password@localhost:5432/wagateway

# Authentication
SUPERADMIN_TOKEN=your-secret-token

# Optional
JWT_SECRET=your-jwt-signing-secret
WHATSAPP_STORAGE_PATH=/app/whatsapp_sessions
RUST_LOG=waxum=info,tower_http=info

Then run:

docker compose up -d

This starts NATS (message queue) and the Waxum API. To also run a bundled PostgreSQL:

docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d

To run without NATS, remove or comment out the NATS_URL line in .env.

What is in the image​

Since v0.13.5 the release ships a distroless image (gcr.io/distroless/cc-debian13 plus the waxum binary, about 65 MB). There is no shell, package manager, curl or gosu inside it, which removes the CVEs those carried. What they did is built into the binary:

  • Non-root. The container starts as root, chowns the mounted data directories (WHATSAPP_STORAGE_PATH and the SQLite directory), then drops to the uid:gid in WAXUM_RUN_AS (1000:1000) before serving. Volumes written by an older root-running image keep working. Running the container with --user 1000:1000 works too; nothing is chowned then.
  • Healthcheck. The image declares HEALTHCHECK CMD ["/app/waxum", "--healthcheck"], which requests /health on 127.0.0.1:$PORT and exits 0 only on a 200.
  • Stop. docker stop ends the process immediately on SIGTERM.

If your own compose file or orchestrator defines a healthcheck with curl or wget, switch it, because those binaries no longer exist in the image:

healthcheck:
test: ["CMD", "/app/waxum", "--healthcheck"]
interval: 30s
timeout: 5s
retries: 3

docker exec ... sh is gone for the same reason. Use docker logs and the /status diagnostics instead.

Build from Source​

Linux (Ubuntu/Debian)​

# Install dependencies
sudo apt-get update
sudo apt-get install -y \
build-essential \
pkg-config \
libssl-dev \
libsqlite3-dev

# Install Rust nightly
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustup default nightly

# Clone and build
git clone https://github.com/imtaqin/waxum.git
cd waxum
cargo build --release

# Run
./target/release/waxum --token mysecrettoken --db mysql://user:pass@localhost/wars

Windows​

The prebuilt release binary is compiled with x86_64-pc-windows-gnu (MinGW runtime is statically linked) so it runs on a fresh Windows install without needing the Microsoft Visual C++ Redistributable (VCRUNTIME140.dll). Just download the .zip, extract, and run.

To build from source:

# 1. Install Rust from https://rustup.rs
# 2. Open a new terminal after installation

# Install nightly toolchain + the GNU target
rustup default nightly
rustup target add x86_64-pc-windows-gnu

# MinGW for the C toolchain
choco install mingw -y

# Clone and build
git clone https://github.com/imtaqin/waxum.git
cd waxum
cargo build --release --target x86_64-pc-windows-gnu

# Run
.\target\x86_64-pc-windows-gnu\release\waxum.exe --token mysecrettoken --db mysql://user:pass@localhost/wars

If you build with the default MSVC target instead (cargo build --release without --target), the resulting binary depends on VCRUNTIME140.dll and will fail with a "DLL not found" error on machines without the VC++ Redistributable installed.

macOS​

# Install Xcode command line tools
xcode-select --install

# Install Rust nightly
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustup default nightly

# Clone and build
git clone https://github.com/imtaqin/waxum.git
cd waxum
cargo build --release

# Run
./target/release/waxum --token mysecrettoken --db postgres://user:pass@localhost/wagateway

CLI Arguments​

Usage: waxum [OPTIONS]

Options:
-t, --token <TOKEN> Set superadmin token
-d, --db <URL> Set database URL (postgres/mysql)
-p, --port <PORT> Set server port (default: 3451)
-h, --help Show help

Examples:

# MySQL with custom port
./waxum --token mytoken --db mysql://user:pass@localhost:3306/wars --port 8080

# PostgreSQL
./waxum -t mytoken -d postgres://user:pass@localhost:5432/wagateway

CLI arguments override .env values.

Database​

Waxum runs against three backends. Pick one, or pick none — the default is fine.

SQLite (default, zero-config)​

Leave DATABASE_URL empty and Waxum drops a local SQLite file next to the binary at ./waxum.db. Schema is auto-migrated on boot. Good for development and single-node production.

Override the path if you want:

./waxum --token mytoken --db sqlite://./data/waxum.db
# or
SQLITE_PATH=./data/waxum.db ./waxum --token mytoken

PostgreSQL​

For scale-out, HA, and shared metadata across multiple Waxum nodes:

sudo -u postgres createdb waxum

./waxum --token mytoken --db postgres://postgres:password@localhost:5432/waxum

MySQL​

Same story, MySQL flavour:

mysql -u root -p -e "CREATE DATABASE waxum;"

./waxum --token mytoken --db mysql://root:password@localhost:3306/waxum

Every backend runs the same idempotent CREATE TABLE IF NOT EXISTS + ALTER TABLE ADD COLUMN IF NOT EXISTS sequence at startup — no manual migrations, no docker compose up bootstrapping, no schema drift.

:::info Legacy Postgres env vars If DATABASE_URL is unset and you set POSTGRES_* or MYSQL_* env vars, Waxum picks those up as fallback. They still work; prefer the DATABASE_URL form for new deployments.

POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your-password
POSTGRES_DB=waxum

:::

Environment Variables​

Database​

VariableDefaultDescription
DATABASE_URL(none — falls back to sqlite://./waxum.db)Database connection URL (postgres://, mysql://, or sqlite://)

Authentication​

VariableDefaultDescription
SUPERADMIN_TOKEN(auto-generated JWT)API access token (any string)
JWT_SECRET(random)JWT signing secret (for auto-generated tokens)

Server​

VariableDefaultDescription
PORT3451Server port
WHATSAPP_STORAGE_PATH./whatsapp_sessionsWhatsApp session storage path (SQLite files)
RUST_LOGinfoLog level (debug, info, warn, error)
STALE_SOCKET_SECS150Rebuild a logged-in session whose socket has received nothing for this many seconds (a half-open connection, e.g. after the host's IP changed). 0 disables the check.
LINK_PREVIEW_PAGE_TIMEOUT_MS5000How long waxum waits for a page when building a link preview, in milliseconds (1000 to 30000). An invalid value is ignored with a warning.
WAXUM_RUN_AS(unset; 1000:1000 in the Docker image)uid:gid to drop to when started as root. Unset means waxum keeps the user it was started as.

NATS JetStream (Optional)​

VariableDefaultDescription
NATS_URL(none)NATS server URL — required to enable NATS
NATS_EVENTS_STREAMWA_EVENTSStream name for incoming events
NATS_SEND_STREAMWA_SENDStream name for outbound commands
NATS_EVENTS_MAX_AGE_DAYS7Max age for event messages (days)
NATS_SEND_MAX_AGE_DAYS1Max age for outbound commands (days)
NATS_TOKEN(none)Authentication token
NATS_CREDS_FILE(none)Path to credentials file

Sample .env Files​

DATABASE_URL=mysql://user:password@localhost:3306/wars
SUPERADMIN_TOKEN=mysecrettoken
RUST_LOG=waxum=info,tower_http=info

PostgreSQL with NATS​

DATABASE_URL=postgres://user:password@localhost:5432/wagateway
SUPERADMIN_TOKEN=mysecrettoken
JWT_SECRET=your-jwt-signing-secret
WHATSAPP_STORAGE_PATH=./whatsapp_sessions
NATS_URL=nats://localhost:4222
RUST_LOG=waxum=info,tower_http=info

Verify Installation​

# Health check
curl http://localhost:3451/health
# Should return: OK

# Authenticated request
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:3451/api/v1/sessions

# Open Swagger UI in browser
open http://localhost:3451/swagger-ui