7.2 KiB
Railway Deployment
Deploy LettaBot to Railway for always-on hosting. For other platforms (Fly.io, Docker, Render), see Cloud Deployment.
One-Click Deploy
- Fork this repository
- Connect to Railway
- Set environment variables (see below)
- Deploy!
No local setup required. LettaBot automatically finds or creates your agent by name.
Configuration
Option A: Full YAML Config (Recommended)
Use LETTABOT_CONFIG_YAML to pass your entire lettabot.yaml as a single base64-encoded environment variable. This gives you access to the full config schema (multi-agent, conversation routing, group policies, etc.) without managing dozens of individual env vars.
# Encode your local config
base64 < lettabot.yaml | tr -d '\n'
# Or use the CLI helper
lettabot config encode
Set the output as LETTABOT_CONFIG_YAML in your Railway service variables. That's it -- no other env vars needed (everything is in the YAML).
Option B: Individual Environment Variables
For simple setups (one channel, basic config), you can use individual env vars instead.
Required
| Variable | Description |
|---|---|
LETTA_API_KEY |
Your Letta API key (get one here) |
Channel Configuration (at least one required)
Telegram:
TELEGRAM_BOT_TOKEN=your-bot-token
TELEGRAM_DM_POLICY=pairing
Discord:
DISCORD_BOT_TOKEN=your-bot-token
DISCORD_DM_POLICY=pairing
Slack:
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
Optional
| Variable | Default | Description |
|---|---|---|
LETTA_AGENT_NAME |
LettaBot |
Agent name (used to find/create agent) |
AGENT_NAME |
- | Legacy alias for LETTA_AGENT_NAME |
LETTA_AGENT_ID |
- | Override auto-discovery with specific agent ID |
CRON_ENABLED |
false |
Enable cron jobs |
HEARTBEAT_ENABLED |
false |
Enable heartbeat service |
HEARTBEAT_INTERVAL_MIN |
30 |
Heartbeat interval (minutes). Also enables heartbeat when set |
HEARTBEAT_SKIP_RECENT_USER_MIN |
5 |
Skip automatic heartbeats for N minutes after user messages (0 disables) |
HEARTBEAT_TARGET |
- | Target chat (e.g., telegram:123456) |
OPENAI_API_KEY |
- | For voice message transcription |
API_HOST |
0.0.0.0 on Railway |
Optional override for API bind address |
LOG_LEVEL |
info |
Log verbosity (fatal/error/warn/info/debug/trace) |
LOG_FORMAT |
- | Set to json for structured JSON output (recommended for Railway) |
How It Works
Agent Discovery
On startup, LettaBot:
- Checks for
LETTA_AGENT_IDenv var - uses if set - Otherwise, searches Letta API for an agent named
LETTA_AGENT_NAME(or legacyAGENT_NAME, default: "LettaBot") - If found, uses the existing agent (preserves memory!)
- If not found, creates a new agent on first message
This means your agent persists across deploys without any manual ID copying.
Build & Deploy
Railway automatically:
- Detects Node.js and installs dependencies
- Runs
npm run buildto compile TypeScript - Runs
npm startto start the server - Sets the
PORTenvironment variable - Binds API server to
0.0.0.0by default on Railway (unlessAPI_HOSTis set) - Monitors
/healthendpoint
Persistent Storage
The Railway template includes a persistent volume mounted at /data. This is set up automatically when you deploy using the button above.
What Gets Persisted
- Agent ID - No need to set
LETTA_AGENT_IDmanually after first run - Cron jobs - Scheduled tasks survive restarts
- Skills - Downloaded skills persist
- Attachments - Downloaded media files
Volume Size
- Free plan: 0.5 GB (sufficient for most use cases)
- Hobby plan: 5 GB
- Pro plan: 50 GB
Manual Deployment (Without Template)
If you deploy manually from a fork instead of using the template, you'll need to add a volume yourself:
- In your Railway project, click + New and select Volume
- Connect the volume to your LettaBot service
- Set the mount path to
/data
LettaBot automatically detects RAILWAY_VOLUME_MOUNT_PATH and uses it for persistent data.
Remote Pairing Approval
When using pairing DM policy on a cloud deployment, you need a way to approve new users without CLI access.
Web Portal
Navigate to https://your-app/portal to access the admin portal. It provides a UI for managing pairing requests across all channels (Telegram, Discord, Slack).
You'll need your LETTABOT_API_KEY to log in. The key is auto-generated on first boot and printed in logs. Set it as an environment variable for stable access across deploys:
# Railway
# Set LETTABOT_API_KEY in service variables
# Fly.io
fly secrets set LETTABOT_API_KEY=your-key -a your-app
API
You can also approve pairings via the HTTP API:
# List pending pairing requests for a channel
curl -H "X-Api-Key: $LETTABOT_API_KEY" \
https://your-app/api/v1/pairing/telegram
# Approve a pairing code
curl -X POST \
-H "X-Api-Key: $LETTABOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"code": "ABCD1234"}' \
https://your-app/api/v1/pairing/telegram/approve
Alternatively, use allowlist DM policy and pre-configure allowed users in environment variables to skip pairing entirely.
Channel Limitations
| Channel | Railway Support | Notes |
|---|---|---|
| Telegram | Yes | Full support |
| Discord | Yes | Full support |
| Slack | Yes | Full support |
| No | Requires local QR pairing | |
| Signal | No | Requires local device registration |
Troubleshooting
"No channels configured"
Set at least one channel token (TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, or SLACK tokens).
Agent not found / wrong agent
- Check
LETTA_AGENT_NAME(or legacyAGENT_NAME) matches your intended agent - Or set
LETTA_AGENT_IDexplicitly to use a specific agent - Multiple agents with the same name? The most recently created one is used
Health check failing
Check Railway logs for startup errors. Common issues:
- Missing
LETTA_API_KEY - Invalid channel tokens
API_HOSTincorrectly set to localhost (127.0.0.1)
At startup, LettaBot prints a [Railway] Preflight check block with:
OKlines for detected configWARNlines for risky settings (for example missing volume)FAILlines for blocking issues (for example missingLETTA_API_KEY)
Reading logs
Set LOG_FORMAT=json for structured output compatible with Railway's log search and filtering. Use LOG_LEVEL=debug to enable verbose channel-level debug output when diagnosing issues.
Data not persisting
If data is lost between restarts:
- Verify a volume is attached to your service
- Check that the mount path is set (e.g.,
/data) - Look for
[Storage] Railway volume detectedin startup logs - If not using a volume, set
LETTA_AGENT_IDexplicitly
Deploy Button
Or add to your README:
[](https://railway.com/deploy/lettabot?utm_medium=integration&utm_source=template&utm_campaign=generic)