Skip to content

Deploying EchoFox with Docker ​

The fastest, most portable way to run EchoFox in production. One container, two persistent volumes, optional Prometheus + Grafana.

TL;DR ​

bash
docker run -d \
  --name echofox \
  --restart unless-stopped \
  -p 127.0.0.1:3000:3000 \
  -v echofox-session:/app/src/@session \
  -v echofox-store:/app/src/store/runtime \
  -e TZ=Asia/Kolkata \
  ghcr.io/cosm1cbug/echofox:latest

docker logs -f echofox   # scan the QR

After pairing once, the bot survives restarts/rebuilds as long as you keep the echofox-session volume.


Image variants ​

TagWhat it isWhen to use
ghcr.io/cosm1cbug/echofox:latestLatest tagged releaseProduction. Recommended.
ghcr.io/cosm1cbug/echofox:v0.x.ySpecific versionPin for reproducible deploys.
ghcr.io/cosm1cbug/echofox:mainLatest main branchPre-release testing. May break.
cosm1cbug/echofox:* (Docker Hub)Mirror of GHCRIf GHCR is blocked on your network.

Multi-arch: every tag ships linux/amd64 and linux/arm64 (works on Raspberry Pi 4/5, Apple Silicon, AWS Graviton, etc.).


Required volumes ​

There are exactly two directories you must persist:

Mount pointWhat's insideWhat happens if you lose it
/app/src/@sessionWhatsApp pairing credentials (Baileys auth files)Bot logs out; you must re-scan QR.
/app/src/store/runtimeSQLite DBs: message store, analytics, user directoryBot loses message-retry cache, stats, user history. Non-fatal.

Use named volumes (recommended) or bind mounts:

bash
# Named volumes (managed by Docker)
-v echofox-session:/app/src/@session
-v echofox-store:/app/src/store/runtime

# Bind mounts (live in your filesystem)
-v $HOME/echofox/session:/app/src/@session
-v $HOME/echofox/store:/app/src/store/runtime

Configuration ​

You have two options. Pick one (or combine).

Option A — Mount your own config.js ​

bash
docker run -d \
  --name echofox \
  -v $(pwd)/src/config.js:/app/src/config.js:ro \
  -v echofox-session:/app/src/@session \
  -v echofox-store:/app/src/store/runtime \
  ghcr.io/cosm1cbug/echofox:latest

The container reads it just like a bare-metal install. :ro (read-only) is recommended — the bot never needs to write the file.

Option B — Environment variables ​

Every config field can be overridden via ECHOFOX_<SECTION>_<CAMELCASEKEY>:

bash
docker run -d \
  --name echofox \
  -e ECHOFOX_BOT_PREFIX='.' \
  -e ECHOFOX_BOT_ADMINPREFIX='$' \
  -e ECHOFOX_APIS_OMDB_APIKEY=xxxxx \
  -e ECHOFOX_APIS_VIRUSTOTAL_APIKEY=yyyyy \
  -v echofox-session:/app/src/@session \
  -v echofox-store:/app/src/store/runtime \
  ghcr.io/cosm1cbug/echofox:latest

If both are present, environment variables win (override config.js).

Mapping cheat-sheet ​

config.js pathEnv variable
bot.prefixECHOFOX_BOT_PREFIX
bot.adminPrefixECHOFOX_BOT_ADMINPREFIX
bot.publicECHOFOX_BOT_PUBLIC
features.readMessagesECHOFOX_FEATURES_READMESSAGES
apis.omdb.apiKeyECHOFOX_APIS_OMDB_APIKEY
apis.virustotal.apiKeyECHOFOX_APIS_VIRUSTOTAL_APIKEY
runtime.logLevelECHOFOX_RUNTIME_LOGLEVEL
runtime.portECHOFOX_RUNTIME_PORT
channels.botLogsECHOFOX_CHANNELS_BOTLOGS

admins[] and channels.* are simple strings; nested arrays like admins=["jid1","jid2"] are not yet supported via env vars — use Option A (mount config.js) when you need them.


Health checks ​

The container's HEALTHCHECK probes /healthz every 30 s. Inspect:

bash
docker inspect --format='{{json .State.Health}}' echofox | jq

Manually:

bash
docker exec echofox curl -fsS http://127.0.0.1:3000/healthz
# → {"status":"ok","uptime":1234,"pid":1}

Logs ​

bash
docker logs -f --tail=200 echofox

Logs are JSON in production (single line per event) — pipe through jq or ship to Loki/CloudWatch/Datadog. To get a single-line summary:

bash
docker logs --tail=200 echofox | jq -r '"\(.time) \(.level | tostring) \(.msg)"'

First-run / pairing ​

The bot prints the QR code to stdout, so:

bash
docker logs -f echofox
# Wait for: "scan QR to log in:"
# Then the QR appears in the terminal.

Scan from WhatsApp → Settings → Linked devices → Link a device. After successful pairing, you'll see INFO ✅ connected.

If you'll be far from the terminal, run detached without -d the first time so the QR stays on screen, scan it, then Ctrl+C and start it normally with -d. The session is preserved in the volume.


Updates ​

bash
docker pull ghcr.io/cosm1cbug/echofox:latest
docker stop echofox && docker rm echofox
# re-run with the same docker run command (volumes preserve the session)

Or with Compose: docker compose pull && docker compose up -d.

We use semver — minor/patch are safe to auto-update; major versions may have config migration steps (see CHANGELOG.md).


Troubleshooting ​

"unable to verify the first certificate" ​

Your network is intercepting HTTPS to WhatsApp (corporate proxy, Zscaler, some hotel WiFi, certain ISPs). Try a different network or VPN. For diagnostics see the troubleshooting section in docs/deploy/troubleshooting.md.

"loggedOut" or "401" after months of working ​

Your number got banned, OR you logged the linked device out from your phone. Wipe the session and re-pair:

bash
docker stop echofox
docker volume rm echofox-session
docker volume create echofox-session
docker start echofox
docker logs -f echofox   # scan new QR

Container restarts in a loop ​

bash
docker logs --tail=80 echofox

Common culprits:

  • Config validation failed → look for ❌ EchoFox configuration is invalid
  • Port 3000 busy on the host → change with -p 127.0.0.1:8080:3000
  • Missing --init flag → use init: true in compose or --init in docker run

"I want fewer logs" ​

-e LOG_LEVEL=warn (options: trace, debug, info, warn, error, fatal).


Building locally ​

bash
git clone https://github.com/Cosm1cBug/EchoFox.git
cd EchoFox
docker build -t echofox:local .
docker run -d --name echofox -p 3000:3000 \
  -v echofox-session:/app/src/@session \
  -v echofox-store:/app/src/store/runtime \
  echofox:local

For multi-arch builds, see docs/deploy/multi-arch.md.

Released under the AGPL-3.0 License.