Remote environments
Run your Letta Code agent across multiple environments accessible from any device
Server mode makes your Letta Code agent always-on and accessible from anywhere. Run letta server on your machine and then interact with your agent from chat.letta.com, another computer, or your phone — while the agent continues to execute tools (file edits, shell commands, etc.) on your local machine.
This decouples where you interact with the agent from where the agent acts.
Quick start
Section titled “Quick start”letta serverOn first run, you’ll be prompted to name your environment (defaults to your hostname). The CLI opens a persistent connection and waits for instructions.
You can also specify the name directly:
letta server --env-name "work-laptop"How it works
Section titled “How it works”Architecture
Section titled “Architecture”flowchart LR
subgraph Clients["Any client"]
Chat["chat.letta.com"]
SDK["SDK / API"]
Terminal["Another terminal"]
end
subgraph Local["Your machine"]
CLI["letta server"]
Tools["Tools (Bash, Read, Write, Edit, ...)"]
end
Clients -->|"Send message"| CLI
CLI --> Tools
CLI -->|"Stream responses"| Clients
- You start
letta serveron your machine — the agent runs locally and registers itself so other clients can reach it - From any client (chat.letta.com, SDK, another terminal), you send a message to the agent
- The agent executes tools on your machine (Bash, Read, Write, Edit, etc.)
- Responses stream back to whatever client you’re using
Connection lifecycle
Section titled “Connection lifecycle”The connection is designed to be resilient:
- Heartbeat: Ping every 30 seconds to keep the connection alive
- Auto-reconnect: On unexpected disconnect, exponential backoff retry (1s → 30s max, 5 minutes total)
- Auto re-registration: If the connection expires, the CLI automatically re-registers and reconnects
- State recovery: On reconnect, the agent syncs its full state (permission mode, active runs, pending approvals, queue state)
- Restart recovery: After a
letta serverrestart, each conversation restores its last working directory and permission mode
Interactive approvals
Section titled “Interactive approvals”Server mode supports the same approval workflow you get in interactive mode. Permission mode is tracked per conversation, so switching one conversation to plan or acceptEdits does not change the others. When the agent wants to use a tool that requires permission, the approval request is shown in whatever client you’re connected with:
Agent wants to call Write on /etc/config → Approval request shown in [chat.letta.com](https://chat.letta.com) → You approve or deny → Agent continues executionYou can also modify tool arguments before approving - edits flow back through the same channel.
Client lifecycle events
Section titled “Client lifecycle events”For custom server-mode clients, Letta Code now emits explicit lifecycle events around approvals and local tool execution:
approval_requestedapproval_receivedtool_execution_startedtool_execution_finished
These events are useful for UI state, timers, and telemetry in custom clients. The canonical approval flow still uses the existing control request and response messages.
Remote command parity
Section titled “Remote command parity”Server-mode clients can inspect device_status.supported_commands to see which slash commands the connected Letta Code version can execute remotely.
Current supported remote slash commands include /clear, /remember, /init, and /doctor.
Custom clients can also use newer websocket commands to list models, update the current model, enable or disable skills, and add agents. Device status now includes richer git context such as the current branch and recent local branches.
Multi-environment execution
Section titled “Multi-environment execution”You can run letta server on multiple machines simultaneously with the same agent. Each environment gets a unique connection:
letta server --env-name "laptop"letta server --env-name "staging-server"letta server --env-name "devcontainer"This enables workflows where your agent reads code on your laptop, runs tests on a staging server, and deploys to production — all in one conversation.
Message queuing
Section titled “Message queuing”When multiple messages arrive while the agent is busy processing a turn:
- Messages are queued and processed sequentially
- Consecutive user messages are coalesced (merged into a single turn)
- Queue state is visible in real-time from chat.letta.com
Scheduling
Section titled “Scheduling”Letta Code scheduling runs through the remote listener. Use letta cron to register one-time or recurring prompts for an agent and conversation, then keep letta server running so the connected environment can fire them.
If the agent is already busy when a scheduled task fires, the prompt waits in the queue and runs after the current turn completes.
Recurring tasks currently expire after 3 days, so this works best for lightweight automation tied to a live environment rather than long-lived hosted scheduling.
This is different from both the server-side API schedules feature and shell cron jobs that call letta -p directly. For setup and command examples, see Scheduling.
Session logging
Section titled “Session logging”Every server session writes a persistent log to ~/.letta/logs/remote/:
[14:17:03.221] → send (protocol) {"type":"ping"}[14:17:03.445] ← recv (control) {"type":"message","content":"..."}[14:17:05.112] → send (protocol) {"type":"run_started","run_id":"..."}- Logs are always written (not gated behind
--debug) - The last 10 log files are kept; older ones are automatically pruned
- Useful for debugging connection issues or auditing operations
What this enables
Section titled “What this enables”Work from any device: Start letta server on your dev machine, then interact with your agent from chat.letta.com on your tablet, phone, or a different computer. The agent works on your machine while you direct it from anywhere.
Always-on agents: The server runs indefinitely. Even if you close chat.letta.com, the agent keeps running and can resume immediately when you reconnect.
Collaborative workflows: A teammate can interact with your agent on chat.letta.com and approve/deny file writes without needing terminal access to your machine.
Cross-environment orchestration: Run the agent across multiple machines. It can read code on your laptop, run tests on a staging server, and deploy from a third — all in one conversation.
Cloud deployment
Section titled “Cloud deployment”You can run letta server on a cloud VM so your agent is always-on. Since letta server only makes an outbound WebSocket connection to Letta Cloud, there are no inbound ports to open, no reverse proxy to configure, and no domain name needed.
DigitalOcean (~2 minutes, $4/month)
Section titled “DigitalOcean (~2 minutes, $4/month)”# Create a dropletdoctl compute droplet create letta-remote \ --size s-1vcpu-512mb-10gb \ --image ubuntu-24-04-x64 \ --region sfo3 \ --ssh-keys $(doctl compute ssh-key list --format ID --no-header | head -1)Or create one from the DigitalOcean dashboard — pick Ubuntu 24.04, the $4/mo plan, and your SSH key.
SSH in and install:
ssh root@<droplet-ip>
# Install Node.js 20curl -fsSL https://deb.nodesource.com/setup_20.x | bash -apt-get install -y nodejs
# Install Letta Codenpm install -g @letta-ai/letta-code
# Start the serverexport LETTA_API_KEY="your-api-key-here"letta server --env-name "cloud"To keep it running across reboots, create a systemd service:
cat > /etc/systemd/system/letta-server.service << 'EOF'[Unit]Description=Letta Code Remote ServerAfter=network-online.targetWants=network-online.target
[Service]Type=simpleExecStart=/usr/bin/letta server --env-name "cloud"Environment=LETTA_API_KEY=your-api-key-hereRestart=alwaysRestartSec=5
[Install]WantedBy=multi-user.targetEOF
systemctl daemon-reloadsystemctl enable --now letta-serverFly.io (~5 minutes, ~$3/month)
Section titled “Fly.io (~5 minutes, ~$3/month)”Create a Dockerfile:
FROM node:20-slim
RUN npm install -g @letta-ai/letta-code
ENV LETTA_API_KEY=""
CMD ["letta", "server", "--env-name", "fly"]Deploy:
mkdir letta-remote && cd letta-remote
# Save the Dockerfile above, then:fly launch --name letta-remote --no-deployfly secrets set LETTA_API_KEY="your-api-key-here"fly deploy
# Keep exactly 1 machine running (not scaled to zero)fly scale count 1Railway (~5 minutes, ~$5/month)
Section titled “Railway (~5 minutes, ~$5/month)”- Push the same Dockerfile to a GitHub repo
- Connect the repo in Railway
- Add
LETTA_API_KEYas an environment variable - Deploy
Or use the Railway CLI:
railway initrailway variables set LETTA_API_KEY="your-api-key-here"railway upModal (usage-based)
Section titled “Modal (usage-based)”Modal Sandboxes are non-preemptible containers that can run a long-lived process with an entrypoint command. Create launch.py:
import modal
app = modal.App.lookup("letta-remote", create_if_missing=True)
image = ( modal.Image.debian_slim(python_version="3.12") .apt_install("curl") .run_commands( "curl -fsSL https://deb.nodesource.com/setup_20.x | bash -", "apt-get install -y nodejs", "npm install -g @letta-ai/letta-code", ))
with modal.enable_output(): sb = modal.Sandbox.create( "letta", "server", "--env-name", "modal", app=app, image=image, name="letta-server", secrets=[modal.Secret.from_name("letta-secrets")], timeout=24 * 60 * 60, # 24 hours (max) )
print(f"Sandbox running: {sb.object_id}")sb.detach()Set your API key and launch:
modal secret create letta-secrets LETTA_API_KEY="your-api-key-here"python launch.pyThe named sandbox (letta-server) ensures only one instance runs at a time. Sandboxes have a 24-hour max timeout, so you’ll need to re-run the script daily or set up a cron to recreate it.
Daytona (usage-based)
Section titled “Daytona (usage-based)”Daytona sandboxes are persistent cloud environments with SSH access. Install the CLI and log in:
brew install daytonaio/cli/daytonadaytona login --api-key=YOUR_DAYTONA_API_KEYThe recommended approach is building from a Dockerfile so Letta Code is pre-installed. Create a Dockerfile:
FROM oven/bun:slim
RUN apt-get update && \ apt-get install -y python3 make g++ && \ bun install -g @letta-ai/letta-code && \ apt-get purge -y python3 make g++ && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/*
ENV LETTA_API_KEY=""ENV ENV_NAME="cloud"
CMD ["sh", "-c", "/usr/local/bin/letta server --env-name \"$ENV_NAME\""]Create the sandbox:
daytona create --name letta-remote \ --dockerfile Dockerfile \ --env LETTA_API_KEY="your-letta-api-key" \ --auto-stop 0Daytona overrides the Dockerfile CMD with its own init process, so start letta server manually:
daytona exec letta-remote -- letta server --env-name "daytona" --debugOr SSH in for an interactive session:
daytona ssh letta-remoteletta server --env-name "daytona"Daytona sandboxes persist across sessions. Use daytona ssh letta-remote to reconnect at any time.
Which platform to choose
Section titled “Which platform to choose”| DigitalOcean | Fly.io | Railway | Modal | Daytona | |
|---|---|---|---|---|---|
| Setup | SSH + 3 commands | Dockerfile + CLI | Git push or CLI | Python file + CLI | CLI + SSH |
| Cost | $4/mo flat | ~$3/mo usage | ~$5/mo usage | Usage-based | Usage-based |
| Persistence | systemd | Built-in | Built-in | Named sandbox | Built-in |
| Best for | Simplicity, full control | Infra-as-code workflows | Quick deploys from GitHub | Python-native infra | Dev-oriented sandboxes |
For most users, DigitalOcean is the fastest path: a $4 VM where you SSH in and run three commands. All platforms include ready-to-use configs in the letta-code-server-deployment repo.
Configuration
Section titled “Configuration”Saved settings
Section titled “Saved settings”| Setting | Location | Description |
|---|---|---|
deviceId | ~/.letta/settings.json | Stable UUID, generated once |
listenerEnvName | .letta/settings.local.json (per-project) | Saved environment name |
| Remote server state | ~/.letta/remote-settings.json | Per-conversation working directories and permission modes restored after restarts |
Session logs
Section titled “Session logs”| Path | Description |
|---|---|
~/.letta/logs/remote/{timestamp}.log | Per-session transport log |