# Start & ready commands (/docs/template/start-ready-command)

<!-- agent-signals: reading_time_min: 5 · est_tokens: 2683 · updated: 2026-07-30 -->
Related: [Quickstart](/docs/template/quickstart.md), [How it works](/docs/template/how-it-works.md), [User and workdir](/docs/template/user-and-workdir.md), [Caching](/docs/template/caching.md), [Base image](/docs/template/base-image.md), [Private registries](/docs/template/private-registries.md)

## Start command [#start-command]

The start command specifies a process that runs at the **end of the template build** — not when a sandbox is created.
During the build, E2B executes the start command, waits for the [ready command](#ready-command) to confirm the process is up, and then takes a [snapshot](/docs/template/how-it-works) of the entire sandbox including the running process.

When you later create a sandbox from that template, the snapshotted process is **already running** — there is no startup wait.
This is how you get servers, seeded databases, or any long-running process available instantly when spawning sandboxes with the SDK.

<Note title="The start command does not run on sandbox creation">
  The start command runs **once during template build** and is captured in a snapshot. It does not re-execute each time you create a sandbox. If you need to run a command every time a sandbox is created, use `sandbox.commands.run()` after creating the sandbox instead.

  This also means that [environment variables passed to `Sandbox.create()`](/docs/sandbox/environment-variables#1-global-environment-variables) are **not available** to the start command process — it already ran during the build. If your start command needs environment variables, set them in the template definition using `setEnvs()` / `set_envs()`.
</Note>

You can see the full build process [here](/docs/template/how-it-works).

## Ready command [#ready-command]

The ready command determines when the sandbox is ready before a [snapshot](/docs/template/how-it-works) is created.
It is executed in an infinite loop until it returns a successful **exit code 0**.
This lets you control how long the build waits for the [start command](#start-command) or any other system state to be ready.

## `setStartCmd` / `set_start_cmd` [#setstartcmd--set_start_cmd]

Use `setStartCmd` / `set_start_cmd` when you want to run a process during the template build **and** wait for it to be ready. This method accepts **two arguments**: the start command and the ready command.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import { Template, waitForPort, waitForURL, waitForTimeout } from 'e2b'

      // Start a Python HTTP server and wait for it to listen on port 8000
      const template = Template()
        .fromUbuntuImage("22.04")
        .aptInstall(["curl", "python3"])
        .setStartCmd('python3 -m http.server 8000', waitForPort(8000))
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      from e2b import Template, wait_for_port, wait_for_url, wait_for_timeout

      # Start a Python HTTP server and wait for it to listen on port 8000
      template = (
          Template()
          .from_ubuntu_image("22.04")
          .apt_install(["curl", "python3"])
          .set_start_cmd("python3 -m http.server 8000", wait_for_port(8000))
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

You can also pass a custom shell command as the ready command instead of using a helper:

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // Start command with a custom ready command
      template.setStartCmd('npm start', 'curl -s http://localhost:3000/health')
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # Start command with a custom ready command
      template.set_start_cmd("npm start", "curl -s http://localhost:3000/health")
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

More examples:

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import { Template, waitForURL, waitForPort } from 'e2b'

      // Next.js app — wait for the dev server URL
      template.setStartCmd('npx next --turbo', waitForURL('http://localhost:3000'))

      // Python HTTP server — wait for port 8000
      template.setStartCmd('python -m http.server 8000', waitForPort(8000))

      // VNC desktop — wait for the VNC port
      template.setStartCmd('/start_command.sh', waitForPort(6080))
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      from e2b import Template, wait_for_url, wait_for_port

      # Next.js app — wait for the dev server URL
      template.set_start_cmd("npx next --turbo", wait_for_url("http://localhost:3000"))

      # Python HTTP server — wait for port 8000
      template.set_start_cmd("python -m http.server 8000", wait_for_port(8000))

      # VNC desktop — wait for the VNC port
      template.set_start_cmd("/start_command.sh", wait_for_port(6080))
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## `setReadyCmd` / `set_ready_cmd` [#setreadycmd--set_ready_cmd]

Use `setReadyCmd` / `set_ready_cmd` when you **don't need a start command** but still want to control when the sandbox snapshot is taken. This method accepts only **one argument**: the ready command.

This is useful when your template's build steps (e.g., `runCmd` / `run_cmd`) already start a background process or when you just need extra time for the system to settle before snapshotting.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import { Template, waitForTimeout, waitForPort, waitForFile } from 'e2b'

      // Wait 10 seconds before taking the snapshot
      const template = Template()
        .fromUbuntuImage("22.04")
        .runCmd("apt-get install -y nginx && service nginx start")
        .setReadyCmd(waitForPort(80))
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      from e2b import Template, wait_for_timeout, wait_for_port, wait_for_file

      # Wait for nginx to start listening before taking the snapshot
      template = (
          Template()
          .from_ubuntu_image("22.04")
          .run_cmd("apt-get install -y nginx && service nginx start")
          .set_ready_cmd(wait_for_port(80))
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

More examples:

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // Wait for a file to be created by a background process
      template.setReadyCmd(waitForFile('/tmp/ready'))

      // Wait a fixed duration for the system to stabilize
      template.setReadyCmd(waitForTimeout(10_000))

      // Custom readiness check
      template.setReadyCmd('curl -s http://localhost:8080/health')
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # Wait for a file to be created by a background process
      template.set_ready_cmd(wait_for_file("/tmp/ready"))

      # Wait a fixed duration for the system to stabilize
      template.set_ready_cmd(wait_for_timeout(10_000))

      # Custom readiness check
      template.set_ready_cmd("curl -s http://localhost:8080/health")
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Ready command helpers [#ready-command-helpers]

The SDK provides helper functions for common ready command patterns. These can be used with both `setStartCmd` / `set_start_cmd` and `setReadyCmd` / `set_ready_cmd`.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      import {
        waitForPort,
        waitForURL,
        waitForProcess,
        waitForFile,
        waitForTimeout,
      } from 'e2b'

      // Wait for a port to be available
      waitForPort(3000)

      // Wait for a URL to return a specific status code (defaults to 200)
      waitForURL('http://localhost:3000/health')
      waitForURL('http://localhost:3000/health', 200)

      // Wait for a process to be running
      waitForProcess('node')

      // Wait for a file to exist
      waitForFile('/tmp/ready')

      // Wait for a specified duration
      waitForTimeout(10_000) // 10 seconds
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      from e2b import wait_for_port, wait_for_url, wait_for_process, wait_for_file, wait_for_timeout

      # Wait for a port to be available
      wait_for_port(3000)

      # Wait for a URL to return a specific status code (defaults to 200)
      wait_for_url("http://localhost:3000/health")
      wait_for_url("http://localhost:3000/health", 200)

      # Wait for a process to be running
      wait_for_process("node")

      # Wait for a file to exist
      wait_for_file("/tmp/ready")

      # Wait for a specified duration
      wait_for_timeout(10_000)  # 10 seconds
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>
