Single container
The all-in-one image runs the whole stack in one container: Postgres, Redis, Evolution API, the web app, and the Telegram bridge share one image and one volume, and every secret is generated on first boot.
Run it
docker run -d --name automata --restart unless-stopped --stop-timeout 30 \
-p 3000:3000 -v automata_data:/data \
-e NUXT_PUBLIC_APP_URL=https://automata.example.com \
ghcr.io/ju-li/automata-mcp-aio:1
Open the app on port 3000 and sign up. The first account creates an organization and becomes its administrator. First boot creates the database and runs Evolution's and the app's migrations; docker logs -f automata shows each service starting.
NUXT_PUBLIC_APP_URL is the address people will open, and every connector URL is built from it. Left out, it defaults to http://localhost:3000, which is fine for trying it and wrong for anything Claude has to reach.
--stop-timeout 30 gives the services time to stop in order: the web app, Evolution, and the bridge first, Postgres last. Docker's default of 10 seconds can cut that short.
What is inside
| Process | Version | Listens on (inside the container) |
|---|---|---|
| Web app | This release | 3000, or $PORT. The only port to publish |
| Postgres | 16 | 127.0.0.1:5432 |
| Redis | 8.2.9 | 127.0.0.1:6379 |
| Evolution API | v2.3.7 | 18080 |
| Telegram bridge | This release | 18095 |
The image is built on Evolution's own image, so Evolution runs exactly as it ships. s6-overlay starts the rest in order, restarts what crashes, and stops the container if a start-up step fails. The image is large, about 2 GB, most of it Evolution's.
PORT may not be one of the internal ports above; the container refuses to start and says so.
Everything lives in /data
| Path | Holds |
|---|---|
/data/postgres | The database: every user, connection, stored credential, and synced message |
/data/redis | Evolution's cache |
/data/evolution | Evolution's session files |
/data/secrets | The generated secrets, one file each |
On first boot the container generates POSTGRES_PASSWORD, WEBHOOK_SECRET, EVOLUTION_API_KEY, TELEGRAM_BRIDGE_ADMIN_KEY, and TELEGRAM_SESSION_ENCRYPTION_KEY into /data/secrets. An environment variable with one of those names overrides the stored file, which is left as it was. Set them only if you manage secrets elsewhere, keep them URL-safe (openssl rand -hex 32), and never change TELEGRAM_SESSION_ENCRYPTION_KEY after the first boot.
Losing TELEGRAM_SESSION_ENCRYPTION_KEY unlinks every Telegram account. Losing the volume loses everything, and every WhatsApp number needs its QR code scanned again. docker volume rm automata_data is the single-container equivalent of docker compose down -v. docker rm on its own is safe.
Options
Everything is optional. The names are the ones .env.selfhost.example uses, so a filled-in copy works as docker run --env-file .env …; blank values are ignored.
| Variable | For |
|---|---|
NUXT_PUBLIC_APP_URL | The public address, as above |
TELEGRAM_API_ID, TELEGRAM_API_HASH | Telegram, from my.telegram.org. Both or neither. Without them the app tells an administrator what to set. See Telegram |
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_TLS, MAIL_FROM, MAIL_FROM_NAME | Connection alerts, invitation emails, and email verification |
NUXT_ALLOW_PRIVATE_TARGETS | true lets connections reach private and loopback addresses. See below |
SIGNUP, SIGNUP_CODE | Who can create an account. See Who can sign up |
EMAIL_VERIFICATION | auto, required, or off. See Email verification |
PORT | The web app's port inside the container. Defaults to 3000 |
Every backend runs on the container's loopback. The app still refuses its own Postgres, Evolution, and bridge by address and port, but not Redis on 127.0.0.1:6379, so turn on NUXT_ALLOW_PRIVATE_TARGETS only where everyone with an account is trusted.
Updating
docker pull ghcr.io/ju-li/automata-mcp-aio:1
docker rm -f automata
docker run -d --name automata … # the same command as before
Removing the container and running it again with the same volume is safe. Migrations run at boot and only go forward, so moving to a new major is a deliberate edit of the tag. See Releases.
When to use Compose instead
Choose Docker Compose when you want to restart, upgrade, or back up one service without the others, put Postgres on its own disk, or leave out WhatsApp or Telegram entirely. The single container always runs all of them.
View source