Container Lifecycle and Sessions - mensfeld/code-on-incus GitHub Wiki

Understanding how containers and sessions work in COI.

How It Works Internally

  1. Containers are always launched as non-ephemeral (persistent in Incus terms)

    • This allows saving session data even if the container is stopped from within (e.g., sudo shutdown 0)
    • Session data can be pulled from stopped containers, but not from deleted ones
  2. Inside the container: tmuxbash<ai-tool>

    • When the AI tool exits, you're dropped to bash
    • From bash you can: type exit, press Ctrl+b d to detach, or run sudo shutdown 0
  3. On cleanup (when you exit/detach):

    • Session data (tool config directory) is always saved to ~/.coi/sessions-<tool>/
    • If --persistent was NOT set: container is deleted after saving
    • If --persistent was set: container is kept for reuse
    • Cleanup is protected by sync.Once to prevent race conditions between signal handlers and deferred cleanup
  4. Docker/Compose support: Session containers automatically get Docker support flags (security.nesting, security.syscalls.intercept.mknod/setxattr) applied before the container starts, so Docker and Docker Compose work out of the box inside sessions.

What Gets Preserved

Mode Workspace Files AI Tool Session Container State
Default (ephemeral) Always saved Always saved Deleted
--persistent Always saved Always saved Kept

Session vs Container Persistence

--resume Flag

Restores the AI tool conversation in a fresh container:

  • Use when you want to continue a conversation but don't need installed packages
  • Container is recreated, only tool session data is restored
  • Workspace-scoped: Only finds sessions from the current workspace directory (security feature)

--persistent Flag

Keeps the entire container with all modifications:

  • Use when you've installed tools, built artifacts, or modified the environment
  • coi attach reconnects to the same container with everything intact

coi persist

Converts a running ephemeral session to persistent mode:

# Convert the current session to persistent (keeps the container on exit)
coi persist

This is useful when you started an ephemeral session but later decide you want to keep the container (e.g., after installing tools or setting up the environment).

SSH Agent Forwarding

Forward your host's SSH agent into the container so git-over-SSH works without copying private keys:

# ~/.coi/config.toml
[ssh]
forward_agent = true

The host's SSH_AUTH_SOCK is bridged into the container at /tmp/ssh-agent.sock via an Incus proxy device. COI automatically sets SSH_AUTH_SOCK inside the container and includes retry logic to handle an Incus race condition where proxy devices on freshly-launched containers may not create the listen socket immediately.

Key points:

  • Disabled by default (opt-in for security)
  • Gracefully skips if no SSH agent is running on the host
  • Works with both ephemeral and persistent containers
  • Device is replaced on persistent container re-entry

Environment Variable Forwarding

Selectively forward host environment variables into the container by name:

# ~/.coi/config.toml
[defaults]
forward_env = ["ANTHROPIC_API_KEY", "GITHUB_TOKEN", "AWS_ACCESS_KEY_ID"]

# Static environment variables (always set)
[defaults.environment]
RUST_BACKTRACE = "1"
NODE_ENV = "development"

Key points:

  • Values are read from the host at session start — never stored in config files
  • Missing variables produce a warning but don't fail the session
  • Config values from different levels are merged (deduplicated)
  • Profile-level environment vars are also applied

Stopping Containers

From Inside the Container

  • exit in bash → exits bash but keeps container running (use for temporary shell exit)
  • Ctrl+b d → detaches from tmux, container stays running
  • sudo shutdown 0 or sudo poweroff → stops container, session is saved, then container is deleted (or kept if --persistent)

From Outside (Host)

  • coi shutdown <name> → graceful stop with session save, then delete (60s timeout by default)
  • coi shutdown --timeout=30 <name> → graceful stop with 30s timeout
  • coi shutdown --all → graceful stop all containers (with confirmation)
  • coi shutdown --all --force → graceful stop all without confirmation
  • coi kill <name> → force stop and delete immediately
  • coi kill --all → force stop and delete all containers (with confirmation)
  • coi kill --all --force → force stop all without confirmation
  • coi unfreeze → unfreeze a paused container (e.g., after security monitor auto-pause)

Example Workflows

Quick Task (Default Mode)

coi shell                    # Start session with default AI tool
# ... work with AI assistant ...
sudo poweroff                # Shutdown container → session saved, container deleted
coi shell --resume           # Continue conversation in fresh container

Note: exit in bash keeps the container running - use sudo poweroff or sudo shutdown 0 to properly end the session. Both require sudo but no password.

Long-Running Project (--persistent)

coi shell --persistent       # Start persistent session
# ... install tools, build things ...
# Press Ctrl+b d to detach
coi attach                   # Reconnect to same container with all tools
sudo poweroff                # When done, shutdown and save
coi shell --persistent --resume  # Resume with all installed tools intact

Parallel Sessions (Multi-Slot)

# Terminal 1: Start first session (auto-allocates slot 1)
coi shell
# ... working on feature A ...
# Press Ctrl+b d to detach (container stays running)

# Terminal 2: Start second session (auto-allocates slot 2)
coi shell
# ... working on feature B in parallel ...

# Both sessions share the same workspace but have isolated:
# - Home directories (~/slot1_file won't appear in slot 2)
# - Installed packages
# - Running processes
# - AI tool conversation history

# List both running sessions
coi list
#   coi-abc12345-1 (ephemeral)
#   coi-abc12345-2 (ephemeral)

# When done, shutdown all sessions
coi shutdown --all

Container Aliases

Assign human-friendly names to your containers for easy management from any directory:

# .coi/config.toml
[container]
alias = "myproject"
coi shell myproject              # Launch session using alias (from any directory)
coi attach myproject             # Attach to running aliased container
coi kill myproject --force       # Kill by alias
coi attach myproject-2           # Attach to slot 2 by alias

Aliases are registered in ~/.coi/aliases.json on first use and stored as user.coi.alias metadata on each container. coi list shows aliases next to container names.

⚠️ **GitHub.com Fallback** ⚠️