Railway deployment
A complete deployment uses up to five services. Provision only the connector infrastructure you need.
Railway's template provisions the service matrix below automatically, including volumes. You still need to fill in the environment variables described under Environment wiring.
Service matrix
| Service | Source | Target port | Persistent volume |
|---|---|---|---|
| Postgres | Railway template | Managed | Managed |
| Redis (WhatsApp only) | Railway template | Managed | Managed |
| Evolution (WhatsApp only) | evoapicloud/evolution-api:v2.3.7 | 8080 | /evolution/instances |
| Telegram bridge (Telegram only) | ghcr.io/ju-li/automata-mcp-telegram-bridge:1 | 8095 | None |
| Web | ghcr.io/ju-li/automata-mcp-web:1 | Railway assigned | None |
Postgres is always required because it holds the app's own tables. A deployment serving only PostgreSQL connections needs just Postgres and web.
Published images
The web app and Telegram bridge are published to GHCR on every release, tagged X.Y.Z, X.Y, X, latest, and sha-<commit> for linux/amd64. See Releases for upgrade guidance.
Point services at :1, not :latest. The web service migrates its schema at boot and migrations only move forward, so a major release should never reach a deployment by accident on its next restart.
To run unreleased code, build from the repository instead. Keep the root directory at / and set the Dockerfile path to apps/web/Dockerfile or /apps/telegram-bridge/Dockerfile. A root directory of /apps/telegram-bridge fails with "/pnpm-workspace.yaml": not found.
Volumes and ports
Attach /evolution/instances to Evolution in its service settings. Railway rejects Dockerfile VOLUME instructions, so no image declares one and nothing warns you if it is missing. The Telegram bridge needs no volume; its sessions and chats live in Postgres.
Railway injects PORT into every service, so pin the internal ports explicitly:
evolution: SERVER_PORT=8080
telegram-bridge: TELEGRAM_BRIDGE_PORT=8095
web: Railway-assigned public PORT
Without it, every deploy unpairs all WhatsApp accounts. Users, connections, and tokens live in Postgres, so back that up too: its backups hold every stored credential.
Environment wiring
Use Railway references such as ${{Service.VAR}} for shared secrets and database URLs, so rotated values propagate.
NUXT_DATABASE_URL=${{Postgres.DATABASE_URL}}
NUXT_PUBLIC_APP_URL=https://<web-domain>
# WhatsApp only
NUXT_EVOLUTION_URL=http://evolution.railway.internal:8080
NUXT_EVOLUTION_ADMIN_KEY=${{evolution.AUTHENTICATION_API_KEY}}
NUXT_EVOLUTION_DATABASE_URL=${{Postgres.DATABASE_URL}}
NUXT_WEBHOOK_URL=https://<web-domain>/api/webhook/evolution
NUXT_WEBHOOK_SECRET=<openssl rand -hex 32>
# Telegram only
NUXT_TELEGRAM_URL=http://<telegram-bridge-service>.railway.internal:8095
NUXT_TELEGRAM_ADMIN_KEY=${{telegram-bridge.TELEGRAM_BRIDGE_ADMIN_KEY}}
NUXT_TELEGRAM_DATABASE_URL=${{Postgres.DATABASE_URL}}
Evolution and bridge variables
- Set Evolution
WEBHOOK_GLOBAL_ENABLED=false. Automata registers webhooks per connection, and a global webhook would deliver every event twice. - Keep the
DATABASE_SAVE_DATA_*flags on, includingDATABASE_SAVE_DATA_HISTORIC, or message tools return nothing. TELEGRAM_API_IDandTELEGRAM_API_HASHmay stay empty until you need Telegram. Setting both later redeploys the bridge and enables it.NUXT_TELEGRAM_WEBHOOK_URLis not needed; the bridge posts toNUXT_PUBLIC_APP_URL+/api/webhook/telegram.
Web mail and network settings
- Configure
NUXT_SMTP_*andNUXT_MAIL_FROMon web to deliver alerts and invitations. - Leave
NUXT_ALLOW_PRIVATE_TARGETSunset. - Sign-up is invite-only unless you say otherwise: the first account gets through and everyone after joins by invitation. Set
NUXT_SIGNUP_CODEto let holders of that code create their own organization (the first account then needs it too), orNUXT_SIGNUP=opento let anyone who finds the domain sign up. See Who can sign up. - Once SMTP is configured, new signups and email changes are verified by email. See Email verification.
Keep the Telegram bridge and web app at one replica, and leave RAILWAY_DEPLOYMENT_OVERLAP_SECONDS unset on the bridge. Multiple bridge processes compete for sessions; multiple web replicas run duplicate alert sweeps.
First-run checks
- Confirm web logs include
[app-db] applied migrations. No manual schema step is needed. - Open the web domain and create an account. The first account becomes the administrator; invite everyone else from the Team page.
- Create one connection and call a read-only status tool.
- Confirm connector URLs use the correct public origin.
Coming from PocketBase
Earlier versions stored users and connections in PocketBase. Deploy commit 2e1872e first; it imports every user, password, connection, and token into Postgres on first boot. Once that import is done, delete the PocketBase service and its volume.