Skip to main content
Start with the Runloop Quickstart to use the examples below.

Overview

Named shells are persistent, stateful shell sessions that maintain environment variables and the current working directory (CWD) across commands. This behavior should feel familiar: it’s like using a single shell locally—when you change directories or set environment variables, those changes persist for subsequent commands. Commands executed through the same named shell instance execute sequentially (with automatic queuing), ensuring that environment changes and directory changes from one command are preserved for the next command.

Creating a Named Shell

Use devbox.shell() to create a named shell instance. You can provide a custom name, or omit it to let Runloop generate a unique shell name for you.
# Create a named shell with a custom name
named_shell = devbox.shell("my-session")

# Create a named shell with an auto-generated unique name
uuid_shell = devbox.shell()

Maintaining Working Directory

Named shells preserve the current working directory across commands. This eliminates the need to repeatedly use cd commands.
shell = devbox.shell("my-session")

# Change directory once
await shell.exec("cd ~/my-project")

# All subsequent commands run in ~/my-project
result = await shell.exec("pwd")
print(await result.stdout())  # /home/user/my-project

result = await shell.exec("ls -la")
# Lists files in ~/my-project

result = await shell.exec("git status")
# Shows git status for ~/my-project

Maintaining Environment Variables

Named shells also preserve environment variables across commands, making it easy to set up your environment once and reuse it.
shell = devbox.shell("my-session")

# Set environment variables
await shell.exec("export NODE_ENV=production")
await shell.exec("export API_KEY=secret123")

# Environment variables are preserved
result = await shell.exec("echo $NODE_ENV")
print(await result.stdout())  # production

result = await shell.exec("echo $API_KEY")
print(await result.stdout())  # secret123

# Use in subsequent commands
await shell.exec("npm start")  # Runs with NODE_ENV=production

Sequential Execution

Commands in a named shell execute sequentially - only one command runs at a time. This ensures that state changes from one command are fully applied before the next command starts.
shell = devbox.shell("my-session")

# These commands execute one after another
await shell.exec("cd /app")
await shell.exec("export MY_VAR=value")
result = await shell.exec("echo $MY_VAR")  # Will output 'value'
result = await shell.exec("pwd")  # Will output '/app'

Reusing Named Shells

If you use the same shell name again, you’ll reattach to the existing named shell and reuse its state. This is useful when you need to resume work in a shell session.
# First session
project_shell = devbox.shell("my-session")
await project_shell.exec("cd ~/project")
await project_shell.exec("export PROJECT_DIR=~/project")

# Later, reattach to the same shell
resumed_shell = devbox.shell("my-session")
result = await resumed_shell.exec("pwd")  # Still in ~/project
result = await resumed_shell.exec("echo $PROJECT_DIR")  # Still set
If two different SDK clients use the same shell name on the same devbox, they share a single underlying named shell. This can be powerful, but be careful: commands from different processes will see the same working directory and environment.

Streaming Output

Named shells support streaming output just like regular command execution. You can stream stdout and stderr in real-time.
shell = devbox.shell("my-session")

result = await shell.exec(
  "npm install",
  stdout=lambda line: print(f"[STDOUT] {line}"),
  stderr=lambda line: print(f"[STDERR] {line}")
)

When to Use Named Shells

Use named shells when you need to:
  • Maintain working directory: Avoid repeatedly using cd commands
  • Preserve environment: Set environment variables once and reuse them
  • Run workflows in sequence: Execute ordered commands that build on previous state
  • Resume sessions: Reattach to a previous shell session with preserved state
For simple, isolated commands that don’t depend on previous state, you can use devbox.cmd.exec() directly without a named shell.
For more information about command execution options, see the Execute Commands documentation.