podspawnpodspawn

AI Agent Environments

Setting up disposable SSH environments for Claude Code, Cursor, Codex, and other AI coding agents

podspawn is built for AI agents -- disposable containers, native SSH, zero client install.

This guide covers ephemeral, destroy-on-disconnect workflows for AI agents. For persistent human development environments, see Session Lifecycle.

AI coding agents need disposable environments they can SSH into, run code, and throw away. They can't install desktop apps, navigate web UIs, or open IDEs. They can SSH.

Podspawn is built for this use case. An agent SSHes in, gets a container with the repo, dependencies, and services running, does its work, exits, and the container self-destructs. No cleanup, no zombie processes, no billing surprises.

Why SSH-first matters for agents

Every competitor in this space requires some combination of: a CLI tool installed on the agent's machine, a web UI interaction, an API call to a proprietary service, or a specific IDE integration. None of that works for headless agents.

SSH is the universal interface. Every agent framework supports it. Every CI system supports it. Every programming language has SSH libraries. Podspawn turns that into a container platform.

# Agent receives a task
ssh agent-run-4829@backend.pod

# Agent is in a container with the repo, deps, and services
# Write code, run tests, commit, push
exit

# Container and all companion services self-destruct

Setting up an agent environment

Configure destroy-on-disconnect mode

For agent workflows, you want zero grace period. The container should die the instant the SSH connection closes.

Set the session mode in /etc/podspawn/config.yaml:

session:
  mode: "destroy-on-disconnect"
  grace_period: "0s"
  max_lifetime: "2h"     # safety net for hung agents

With destroy-on-disconnect:

  • Container is destroyed immediately when the last SSH connection closes
  • No grace period, no reconnect window
  • Companion services (databases, caches) are destroyed too
  • max_lifetime acts as a safety net for agents that hang or forget to exit

Register agent users

Register a dedicated user for each agent (or agent run):

# Static agent user with a persistent key
sudo podspawn add-user claude-agent --key-file /path/to/agent-key.pub

# Or generate per-run users programmatically
sudo podspawn add-user "agent-run-${RUN_ID}" --key "ssh-ed25519 AAAA..."

For ephemeral per-run users, clean up the key file after the run:

# After the agent finishes
rm /etc/podspawn/keys/agent-run-${RUN_ID}

Create a Podfile for agents

A Podfile defines exactly what the agent needs. Commit it to your repo and every agent run gets the same environment.

# podfile.yaml
base: ubuntu:24.04

packages:
  - nodejs@22
  - python@3.12
  - git
  - curl

shell: /bin/bash

repos:
  - url: github.com/company/backend
    path: /workspace/backend
    branch: main

env:
  CI: "true"
  DATABASE_URL: "postgres://postgres:devpass@postgres:5432/dev"

services:
  - name: postgres
    image: postgres:16
    ports: [5432]
    env:
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: dev

on_create: |
  cd /workspace/backend && npm install

on_start: |
  echo "Environment ready"

When the agent SSHes in, it gets:

  • The repo cloned at /workspace/backend with dependencies installed
  • A postgres instance running and reachable at postgres:5432
  • Environment variables pre-configured

Connect the agent

See the agent-specific setup below, then set resource limits and monitoring.

Agent-specific setup

Claude Code connects via SSH and needs a shell, git, and the project's toolchain.

Generate a key pair for the agent:

ssh-keygen -t ed25519 -f /path/to/claude-agent-key -N ""

Register the user:

sudo podspawn add-user claude-agent --key-file /path/to/claude-agent-key.pub

Configure the agent with the SSH connection:

Host: yourserver.com
User: claude-agent
Key: /path/to/claude-agent-key

Claude Code SSHes in and gets a container with the project environment.

Recommended Podfile additions for Claude Code:

packages:
  - git
  - curl
  - ripgrep          # Claude Code uses rg for code search
  - tree             # directory structure inspection

env:
  EDITOR: "cat"      # prevents interactive editor prompts

Cursor uses VS Code Remote SSH under the hood. It connects via SSH, syncs files via SFTP, and runs commands via exec channels. All of this works with podspawn.

Register the user:

sudo podspawn add-user cursor-agent --key-file /path/to/cursor-key.pub

In Cursor's SSH config, point to the podspawn server:

Host dev-env
    HostName yourserver.com
    User cursor-agent
    IdentityFile /path/to/cursor-key

Connect via Remote SSH. Cursor installs its server-side component automatically.

With destroy-on-disconnect, Cursor's server component will be re-installed on each connection since the container is fresh. If this is too slow for your workflow, use grace-period mode with a short window (e.g., 300s) to keep the container alive between reconnects.

OpenAI's Codex agent operates similarly. It needs SSH access and a shell.

Register the user and generate keys (same as Claude Code above).

Point the Codex agent configuration at the SSH endpoint.

The agent SSHes in, gets a container, does its work, and exits.

Per-run isolation

For maximum isolation between agent runs, use per-run usernames. Each run gets a unique user, a fresh container, and an ephemeral key pair. No state leaks between runs.

#!/bin/bash
# orchestrator script
RUN_ID=$(uuidgen | head -c 8)
USERNAME="agent-${RUN_ID}"

# Generate ephemeral key pair
ssh-keygen -t ed25519 -f "/tmp/${USERNAME}-key" -N "" -q

# Register the user
sudo podspawn add-user "${USERNAME}" --key-file "/tmp/${USERNAME}-key.pub"

# Run the agent
ssh -i "/tmp/${USERNAME}-key" "${USERNAME}@yourserver.com" << 'SCRIPT'
cd /workspace
npm test
SCRIPT

# Cleanup
rm -f "/tmp/${USERNAME}-key" "/tmp/${USERNAME}-key.pub"
sudo rm -f "/etc/podspawn/keys/${USERNAME}"

Each run gets a completely fresh container. No state leaks between runs. The key and user are cleaned up after.

Resource limits for agents

Agents can be resource-hungry. Set limits to prevent any single agent from starving others:

# /etc/podspawn/config.yaml
defaults:
  cpus: 2.0
  memory: "4g"

resources:
  max_containers: 50
  max_per_user: 3

security:
  pids_limit: 512

For specific agent users that need more resources, use per-user overrides:

# /etc/podspawn/users/claude-agent.yaml
cpus: 4.0
memory: "8g"

Monitoring agent usage

Use podspawn list and podspawn status to monitor active agent sessions:

# See what's running
podspawn list

# System-level metrics
podspawn status

# Prometheus metrics for alerting
podspawn status --prometheus

Set max_lifetime as a safety net. If an agent hangs, the container will be destroyed when the lifetime expires:

session:
  max_lifetime: "2h"

The cleanup daemon enforces this:

podspawn cleanup --daemon --interval 30s

How is this guide?

On this page