Skip to content
Letta Code Letta Code Letta Docs
Sign up

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.

letta server

On 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"
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
  1. You start letta server on your machine — the agent runs locally and registers itself so other clients can reach it
  2. From any client (chat.letta.com, SDK, another terminal), you send a message to the agent
  3. The agent executes tools on your machine (Bash, Read, Write, Edit, etc.)
  4. Responses stream back to whatever client you’re using

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 server restart, each conversation restores its last working directory and permission mode

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 execution

You can also modify tool arguments before approving - edits flow back through the same channel.

For custom server-mode clients, Letta Code now emits explicit lifecycle events around approvals and local tool execution:

  • approval_requested
  • approval_received
  • tool_execution_started
  • tool_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.

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.

You can run letta server on multiple machines simultaneously with the same agent. Each environment gets a unique connection:

On your laptop
letta server --env-name "laptop"
On a staging server
letta server --env-name "staging-server"
In a dev container
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.

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

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.

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

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.

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.

# Create a droplet
doctl 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 20
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
# Install Letta Code
npm install -g @letta-ai/letta-code
# Start the server
export 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 Server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/letta server --env-name "cloud"
Environment=LETTA_API_KEY=your-api-key-here
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now letta-server

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-deploy
fly secrets set LETTA_API_KEY="your-api-key-here"
fly deploy
# Keep exactly 1 machine running (not scaled to zero)
fly scale count 1
  1. Push the same Dockerfile to a GitHub repo
  2. Connect the repo in Railway
  3. Add LETTA_API_KEY as an environment variable
  4. Deploy

Or use the Railway CLI:

railway init
railway variables set LETTA_API_KEY="your-api-key-here"
railway up

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.py

The 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 sandboxes are persistent cloud environments with SSH access. Install the CLI and log in:

brew install daytonaio/cli/daytona
daytona login --api-key=YOUR_DAYTONA_API_KEY

The 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 0

Daytona overrides the Dockerfile CMD with its own init process, so start letta server manually:

daytona exec letta-remote -- letta server --env-name "daytona" --debug

Or SSH in for an interactive session:

daytona ssh letta-remote
letta server --env-name "daytona"

Daytona sandboxes persist across sessions. Use daytona ssh letta-remote to reconnect at any time.

DigitalOceanFly.ioRailwayModalDaytona
SetupSSH + 3 commandsDockerfile + CLIGit push or CLIPython file + CLICLI + SSH
Cost$4/mo flat~$3/mo usage~$5/mo usageUsage-basedUsage-based
PersistencesystemdBuilt-inBuilt-inNamed sandboxBuilt-in
Best forSimplicity, full controlInfra-as-code workflowsQuick deploys from GitHubPython-native infraDev-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.

SettingLocationDescription
deviceId~/.letta/settings.jsonStable UUID, generated once
listenerEnvName.letta/settings.local.json (per-project)Saved environment name
Remote server state~/.letta/remote-settings.jsonPer-conversation working directories and permission modes restored after restarts
PathDescription
~/.letta/logs/remote/{timestamp}.logPer-session transport log