How to Set Up Claude Code on Daytona

How to create a Daytona workspace, install Claude Code inside it, and run a fully cloud-based agent — laptop closed, session persistent, optionally accessible from your phone.

To set up Claude Code on Daytona, create a Daytona workspace, set auto_stop_interval = 0 before running any long task (Daytona's 15-minute inactivity timer kills the workspace even if Claude Code is actively running), install Claude Code with npm install -g @anthropic-ai/claude-code, authenticate once inside the workspace terminal, and keep the process alive with tmux. For remote access from outside your local network, install Tailscale using userspace networking mode — standard Tailscale fails on most Daytona container images due to a missing TUN kernel module. Add Grass to get mobile monitoring and approval gates on top of this stack without SSH.


TL;DR: The complete Daytona + Claude Code setup requires four things most guides skip: disabling auto-stop before any long task, tmux for process persistence, Tailscale in userspace mode for remote access, and Grass for mobile monitoring. Without all four, your workspace will silently stop mid-task the first time you walk away. This guide documents each step in order.


What is Daytona and why run Claude Code there?

Daytona (daytona.io) is a managed sandbox runtime — Kubernetes-backed container workspaces that provision in seconds, support devcontainer configs, and stay alive independently of your laptop. Daytona is Anthropic's endorsed self-hosted sandbox provider for Claude Managed Agents, alongside Cloudflare, Modal, and Vercel. Running Claude Code on a Daytona workspace means the agent process lives on a cloud VM, not on your laptop. Your laptop can close, sleep, or lose power and the agent keeps running.

The alternative — running Claude Code locally — ties every session to your laptop's uptime. Long-running tasks get interrupted by sleep, battery, and SSH disconnects. Daytona eliminates the laptop-tether at the workspace level. Grass eliminates it at the monitoring level.


Prerequisites

Before starting:

  • A Daytona account (daytona.io — free tier available)
  • A repository or project to work in (or you'll create a folder inside the workspace)
  • An Anthropic API key for Claude Code authentication
  • For remote access: a Tailscale account (tailscale.com — free tier covers single-user setups)
  • For mobile monitoring: the Grass app and CLI (npm install -g @grass-ai/ide)

Step 1: Create a Daytona Workspace

From the Daytona dashboard, click New Workspace and select a base image. For Claude Code, a standard Ubuntu 22.04 or Debian image works. If your project has a devcontainer.json, Daytona will use it automatically.

Via the Daytona CLI:

daytona create --name claude-workspace --image ubuntu:22.04
daytona ssh claude-workspace

You're now inside the workspace terminal. Everything from this point runs inside the Daytona container.


Step 2: Disable Auto-Stop Before Running Any Long Task

This is the most common failure mode for new users. Daytona workspaces have a 15-minute inactivity auto-stop timer. Critically, the timer measures user interaction — not process activity. As the Daytona documentation states: "The auto-stop triggers even if there are internal processes running in the sandbox." This means a Claude Code task running in tmux will silently stop the workspace after 15 minutes of no user interaction, with no warning.

Disable auto-stop for the workspace immediately after creating it:

# Via Daytona CLI (run from your local machine)
daytona workspace update --auto-stop 0 --workspace claude-workspace

Or set it via the Daytona dashboard: Workspace Settings → Auto-stop → Disabled (0).

If you're using a devcontainer.json, you can lock this in permanently by adding a lifecycle hook that sets the value at workspace creation time. But the safest habit is: before you fire off any long agent task, verify auto-stop is disabled.

The reason this matters operationally: a Claude Code task doing LLM inference doesn't touch the workspace's user-interaction layer. Time the model spends thinking, generating, or waiting on tool execution does not reset the inactivity timer. A 40-minute autonomous coding run will hit the 15-minute mark and stop — right in the middle of the task — with no log entry in the Daytona UI indicating why.


Step 3: Install Claude Code in the Workspace

# Node.js 18+ is required
node --version  # should be 18+; if not, install via nvm

# Install Claude Code globally
npm install -g @anthropic-ai/claude-code

# Verify install
claude --version

If you want Claude Code available on every workspace restart without reinstalling, add this to your devcontainer.json:

{
  "postCreateCommand": "npm install -g @anthropic-ai/claude-code @grass-ai/ide"
}

This runs once when the workspace is first created. npm global installs persist in the container filesystem, so you'll have both tools on every session without repeating the install step.


Step 4: Set Up tmux for Process Persistence

tmux (terminal multiplexer) is mandatory for keeping Claude Code alive through SSH disconnects. Without tmux, closing your SSH connection or browser tab kills the Claude Code process entirely. With tmux, the process runs as a detached session that persists until the workspace stops.

# Install tmux if not present
apt-get install -y tmux 2>/dev/null || yum install -y tmux 2>/dev/null

# Start a named tmux session with Grass running inside
tmux new-session -d -s grass 'cd ~/project && grass start --network tailscale'

# Or start a Claude Code session directly in tmux
tmux new-session -d -s claude 'cd ~/project && claude'

# Attach to an existing session
tmux attach -t grass

# Detach from a session (keep it running): Ctrl+B then D

Run separate tmux windows for the Grass server and the Claude Code session:

tmux new-session -d -s agent
tmux send-keys -t agent 'cd ~/project && grass start --network tailscale' Enter
tmux new-window -t agent
tmux send-keys -t agent:1 'cd ~/project && claude' Enter

The Claude Code process exits when its parent terminal closes unless protected by tmux. This applies even to tasks you kick off and walk away from — without tmux, an SSH timeout or tab close will kill the agent mid-run.

For more on tmux patterns with Claude Code, see How to Run Claude Code with tmux on a VPS.


Step 5: Authenticate Claude Code Inside the Workspace

Claude Code authentication requires a one-time interactive step inside the workspace terminal — it cannot be pre-configured in a devcontainer.json.

# Set your Anthropic API key in the workspace environment
export ANTHROPIC_API_KEY="sk-ant-..."

# Or store it as a Daytona workspace environment variable (run from your local machine)
# so it's available automatically on every session restart:
daytona env set ANTHROPIC_API_KEY="sk-ant-..." --workspace claude-workspace

# Start Claude Code to confirm authentication works
claude --version && claude -p "say hello"

Claude Code's BYOK (bring your own key) model means your Anthropic API key stays in the Daytona workspace environment — it never transits any third-party server. The key is yours; the workspace is isolated; the agent calls the Anthropic API directly.

For the full security walkthrough on API key handling in remote environments, see How to Store Your API Key Securely When Running Coding Agents on a VPS.


Step 6: Configure Tailscale for Remote Access

Tailscale is architecturally required (not optional) for the Daytona + Grass setup. Daytona workspace IPs are internal and not publicly routable — you cannot connect to the Grass server running inside the workspace from outside the Daytona network without either Tailscale or explicitly opening the Grass port (32100) to the public internet.

Known issue: standard Tailscale fails on most Daytona container images. Many Daytona base images don't include the TUN kernel module that standard Tailscale requires. Running sudo tailscale up will hang or fail with a kernel module error. The fix is userspace networking mode:

# Install Tailscale inside the workspace
curl -fsSL https://tailscale.com/install.sh | sh

# Start Tailscale with userspace networking (avoids the TUN kernel module issue)
sudo tailscale up --tun=userspace-networking --authkey=<your-tailscale-authkey>

# Verify your workspace is on the Tailnet
tailscale ip -4

Replace <your-tailscale-authkey> with an ephemeral or reusable auth key from your Tailscale admin console. Once the workspace appears on your Tailnet, you can connect to it from any device on the same Tailnet — laptop, phone, or automation — using the workspace's Tailscale IP.

To persist Tailscale across workspace restarts, add the startup command to your devcontainer.json:

{
  "postStartCommand": "sudo tailscale up --tun=userspace-networking --authkey=${TAILSCALE_AUTHKEY}"
}

For a detailed guide on the Tailscale setup, see How to Use Tailscale with Claude Code for Remote Access.


What Does the Complete Setup Look Like End-to-End?

After completing the above steps, your architecture is:

  1. Daytona workspace running on Daytona's infrastructure — not your laptop
  2. Auto-stop disabled — workspace stays alive regardless of inactivity
  3. tmux session keeping Claude Code and Grass alive through any SSH disconnect
  4. Tailscale connecting the workspace to your Tailnet so you can reach it from anywhere
  5. Claude Code authenticated with your API key, ready to receive tasks

To verify the full stack is working:

# Check Grass is reachable over Tailscale (run from your laptop or phone on the same Tailnet)
curl http://<workspace-tailscale-ip>:32100/health
# Expected: {"status":"ok","cwd":"/home/user/project",...}

Your laptop is not in this diagram at all. You can close it after confirming the Grass server is running, and the agent will keep working.


How Does Grass Add Mobile Monitoring to This Stack?

Grass is a machine built for AI coding agents — it runs on top of the Daytona workspace and adds the mobile control layer that Daytona alone does not provide. The Grass server (grass start --network tailscale) runs inside the workspace's tmux session. The Grass mobile app connects to it over Tailscale and gives you real-time streaming output, a diff viewer, and permission approval modals — without SSH or a terminal app.

The critical architectural difference: closing the Grass app does not kill the agent. Claude Code keeps running on the Daytona VM. Closing the app only drops the SSE (server-sent events) stream. When you reopen the app, it reconnects and replays all output since your last connection via sequence-numbered SSE frames. Permission requests queue server-side — they're waiting for you when you reconnect, and the agent is paused until you respond.

This solves the specific failure mode where a running agent hits a permission gate and silently waits: "Claude code is awesome, but the desk babysitting isn't. I often kick off a task, go make dinner, come back 30 minutes later and it's just... waiting for me to say 'yes, run grep.'" With Grass on this stack, the permission request shows up as a native modal on your phone the moment Claude Code triggers it.

Install Grass and connect to the workspace

Inside the Daytona workspace (if not already installed via devcontainer.json):

npm install -g @grass-ai/ide

Start Grass inside a tmux window:

tmux new-window -t agent
tmux send-keys -t agent:1 'cd ~/project && grass start --network tailscale' Enter

Grass auto-selects a port from 32100–32199. Startup output shows the QR code and the Tailscale URL:

Starting grass server...
  workspace: /home/user/project
  port: 32100 (auto-selected)
  available agents: claude-code

  Tailscale  http://100.x.x.x:32100

  [QR code]

On your phone, open the Grass app, tap Scan QR Code, and scan. You're now connected to the Claude Code agent running inside the Daytona workspace. One surface. Every agent. Always on.


What Happens If the Daytona Workspace Auto-Stops Mid-Task?

Session transcripts for Claude Code are stored at ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl inside the workspace. These files survive workspace stops — Daytona persists workspace filesystem state when a workspace is stopped (not deleted). Running claude --continue after restarting the workspace resumes from the last checkpoint in the transcript.

The auto-stop failure mode is recoverable, but it requires knowing about the transcript and the --continue flag. The better approach is preventing it: disable auto-stop before every long task, and verify with:

daytona workspace info claude-workspace | grep -i stop

Troubleshooting

Tailscale hangs or fails with a kernel module error
Use --tun=userspace-networking mode. Standard Tailscale requires a TUN kernel module that most Daytona container images don't include. sudo tailscale up --tun=userspace-networking --authkey=<key> bypasses this requirement.

Workspace stops mid-task after 15 minutes
Set auto-stop to 0: daytona workspace update --auto-stop 0. The timer counts user interaction, not process activity. A running Claude Code task does not reset it.

Claude Code exits when SSH session closes
Wrap all agent processes in a named tmux session. Without tmux, the Claude Code process receives SIGHUP on SSH disconnect and exits — even mid-task.

Grass app shows "unreachable" for the workspace
Confirm Tailscale is running inside the workspace (tailscale status) and that the workspace Tailscale IP is reachable from your device (ping <workspace-ip> from the same Tailnet). Also verify the Grass port isn't blocked.

npm install -g @anthropic-ai/claude-code fails
Node.js 18+ is required. Install via nvm: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash && nvm install 18 && nvm use 18.


Frequently Asked Questions

How do I set up Claude Code on Daytona?
Create a Daytona workspace, disable auto-stop (daytona workspace update --auto-stop 0), install Claude Code (npm install -g @anthropic-ai/claude-code), authenticate with your Anthropic API key, wrap the process in tmux for persistence, and install Tailscale with --tun=userspace-networking for remote access. Optionally add Grass (npm install -g @grass-ai/ide, grass start --network tailscale) for mobile monitoring without SSH.

What is Daytona's auto-stop behavior and how does it affect Claude Code sessions?
Daytona workspaces stop automatically after 15 minutes of inactivity by default. The timer measures user interaction, not process activity — a Claude Code task running in the background does not prevent the workspace from stopping. Daytona's documentation confirms: "The auto-stop triggers even if there are internal processes running in the sandbox." Set auto_stop_interval to 0 via daytona workspace update --auto-stop 0 before any long-running agent task.

Why does Tailscale fail inside a Daytona workspace?
Most Daytona container images don't include the TUN kernel module that standard Tailscale requires. Running sudo tailscale up hangs or fails with a cryptic kernel error. The fix is userspace networking mode: sudo tailscale up --tun=userspace-networking --authkey=<key>. This bypasses the kernel module requirement entirely and is the documented workaround for container environments.

Can I monitor Claude Code on Daytona from my phone without SSH?
Yes. Install Grass (npm install -g @grass-ai/ide) inside the workspace and run grass start --network tailscale inside a tmux session. The Grass mobile app connects over Tailscale, streams agent output in real time, shows diffs, and forwards permission requests as native modals — no terminal app or SSH required. Closing the app does not kill the agent; it keeps running on the Daytona VM and the app reconnects automatically.

What happens to my Claude Code session if the Daytona workspace auto-stops?
Claude Code stores session transcripts at ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl inside the workspace filesystem. Daytona persists filesystem state when a workspace stops (as opposed to being deleted), so transcripts survive. Running claude --continue after restarting the workspace resumes from the last session checkpoint.


What's Next

With Daytona + Claude Code + Grass running, you have a complete always-on agent stack: the workspace persists independently of your laptop, tmux keeps the processes alive, Tailscale routes your connection securely, and Grass provides mobile control over every agent action.

Immediate next steps:

  1. Add postCreateCommand to your devcontainer.json so both tools install automatically on every new workspace
  2. Set ANTHROPIC_API_KEY as a Daytona workspace environment variable so it's available without manual export on restart
  3. Verify auto-stop is set to 0 before kicking off your first long autonomous task

For parallel agent workflows once the single-agent setup is stable, see How to Manage Multiple Coding Agents from Your Phone.

Grass is free to start — 10 hours of always-on cloud VM time, no credit card required. Start at codeongrass.com.