223 lines
7.8 KiB
Markdown
223 lines
7.8 KiB
Markdown
# Railway Deployment
|
|
|
|
Deploy LettaBot to [Railway](https://railway.app) for always-on hosting. For other platforms (Fly.io, Docker, Render), see [Cloud Deployment](./cloud-deploy.md).
|
|
|
|
## One-Click Deploy
|
|
|
|
1. Fork this repository
|
|
2. Connect to Railway
|
|
3. Set environment variables (see below)
|
|
4. 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.
|
|
|
|
```bash
|
|
# 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](https://app.letta.com)) |
|
|
|
|
#### 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_POLICY` | `fraction` | Recent-user skip policy (`fixed`, `fraction`, `off`) |
|
|
| `HEARTBEAT_SKIP_RECENT_FRACTION` | `0.5` | Fraction of interval used when policy is `fraction` |
|
|
| `HEARTBEAT_SKIP_RECENT_USER_MIN` | `5` | Skip window in minutes when policy is `fixed` (`0` disables) |
|
|
| `HEARTBEAT_INTERRUPT_ON_USER_MESSAGE` | `true` | Cancel in-flight heartbeat when a user message arrives on the same key |
|
|
| `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:
|
|
1. Checks for `LETTA_AGENT_ID` env var - uses if set
|
|
2. Otherwise, searches Letta API for an agent named `LETTA_AGENT_NAME` (or legacy `AGENT_NAME`, default: "LettaBot")
|
|
3. If found, uses the existing agent (preserves memory!)
|
|
4. 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 build` to compile TypeScript
|
|
- Runs `npm start` to start the server
|
|
- Sets the `PORT` environment variable
|
|
- Binds API server to `0.0.0.0` by default on Railway (unless `API_HOST` is set)
|
|
- Monitors `/health` endpoint
|
|
|
|
## 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_ID` manually after first run
|
|
- **Cron jobs** - Scheduled tasks survive restarts
|
|
- **Skills** - Agent-scoped (`.letta/agents/.../skills`) and working-dir (`WORKING_DIR/.skills`) 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:
|
|
|
|
1. In your Railway project, click **+ New** and select **Volume**
|
|
2. Connect the volume to your LettaBot service
|
|
3. Set the mount path to `/data`
|
|
|
|
LettaBot automatically detects `RAILWAY_VOLUME_MOUNT_PATH` and uses it for persistent data.
|
|
|
|
By default (when `WORKING_DIR` is unset), LettaBot uses `$RAILWAY_VOLUME_MOUNT_PATH/data` as the working directory, so `WORKING_DIR/.skills` is persisted across redeploys. Agent-scoped skills are also stored under `$RAILWAY_VOLUME_MOUNT_PATH/.letta/agents/.../skills`.
|
|
|
|
## 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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 |
|
|
| WhatsApp | 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 legacy `AGENT_NAME`) matches your intended agent
|
|
- Or set `LETTA_AGENT_ID` explicitly 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_HOST` incorrectly set to localhost (`127.0.0.1`)
|
|
|
|
At startup, LettaBot prints a `[Railway] Preflight check` block with:
|
|
- `OK` lines for detected config
|
|
- `WARN` lines for risky settings (for example missing volume)
|
|
- `FAIL` lines for blocking issues (for example missing `LETTA_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:
|
|
1. Verify a volume is attached to your service
|
|
2. Check that the mount path is set (e.g., `/data`)
|
|
3. Look for `[Storage] Railway volume detected` in startup logs
|
|
4. If not using a volume, set `LETTA_AGENT_ID` explicitly
|
|
|
|
## Deploy Button
|
|
|
|
[](https://railway.com/deploy/lettabot?utm_medium=integration&utm_source=template&utm_campaign=generic)
|
|
|
|
Or add to your README:
|
|
|
|
```markdown
|
|
[](https://railway.com/deploy/lettabot?utm_medium=integration&utm_source=template&utm_campaign=generic)
|
|
```
|