---
id: app/ai/cypress-agent-sandboxes
title: Run cypress in an AI agent sandbox
description: >-
  Run cypress inside an AI agent sandbox such as Claude Code on the web, Cursor
  Cloud, or locally so an AI agent can execute tests and inspect the results.
section: app
source_path: docs/app/ai/cypress-agent-sandboxes.mdx
version: 6d7c79b18749f1752f6d5d398d2c5d4812a5fa5f
updated_at: '2026-11-11T18:00:49.121Z'
---
# Run `cypress tap` in an AI agent sandbox

**An agent can start Cypress, run a spec, and read the result without anyone at the keyboard.**

Cloud coding agents such as Claude Code on the web and Cursor Cloud run in a sandbox: an isolated container or virtual machine with no screen and no person to click through the Cypress app. [`cypress tap`](/llm/markdown/app/tooling/cypress-tap.md) reads from a running Cypress app in open mode, so the sandbox has to run that app too. This page covers what the sandbox needs and how an agent starts a session on its own.

The behavior described on this page relies on changes made in Cypress v16.3.0. Earlier versions of Cypress can be run inside sandboxes but may require additional setup and configuration.

## How it fits together

Three things run in the same sandbox:

1.  **The agent**, which runs shell commands.
2.  **A Cypress app in open mode** (`cypress open`), with a browser it launches for the selected testing type.
3.  **`cypress tap` commands**, which the agent runs to start specs and read results.

`cypress tap` finds the Cypress session through files on the same machine and talks to it over local connections. It cannot reach a Cypress app running on another machine, so the agent and Cypress have to share one sandbox.

## Requirements

*   Cypress version 16.3.0 or greater
*   A version of Node.js supported by Cypress. See [System requirements](/llm/markdown/app/get-started/install-cypress.md#System-requirements).
*   On Linux, the `xvfb` package. A sandbox has no screen; Cypress starts a virtual one for the app and the browser using [Xvfb](https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml). If you already have a display you want Cypress to use, set the `DISPLAY` environment variable and Cypress uses it. If `DISPLAY` points to a display that is not running, Cypress starts its own.
*   Network access to the hosts in the table below.
*   A supported browser. See [Choose a browser](#Choose-a-browser).

### Network access

Many agent sandboxes block network traffic by default. Allow these hosts so the Cypress binary downloads and the app can reach its services:

| Host | Used for |
| --- | --- |
| `registry.npmjs.org` | Installing the `cypress` npm package |
| `download.cypress.io` | Finding and downloading the Cypress binary |
| `cdn.cypress.io` | Downloading the Cypress binary |
| `api.cypress.io` | Requests the Cypress App makes to Cypress Cloud |

In addition to the hosts above, you will need to ensure the sandbox is allowed to access any hosts used by your tests; this includes your UI, API, auth layers, and any other network resources your tests rely on. If your test suite relies on dependencies downloaded from repositories other than NPMJS you will need to configure those as well.

If your application is network-restricted (only accessible from your corporate VPN for example) then you will need to coordinate with your network security team to establish a method for a remote sandbox to access it. This may require use of a proxy or other advanced configuration.

## Detecting an AI agent

Cypress recognizes common coding agents from the environment variables they set. When an agent is detected, certain UI behaviors are bypassed or automated to facilitate running headlessly. If your agent is not recognized, set `CYPRESS_AGENT_MODE=1` on the same command line that starts Cypress. Note that some agents start a new shell for each command, so it may be necessary to change how the environment variable is set depending on how your agent works. See [Environment variables](#Environment-variables).

## Changes made in a sandbox

When an agent runs inside a sandbox there is the possibility it could create, delete, or edit files depending on the task it has been given. These changes are performed _inside the sandbox_, so you will need to take action to ensure they are not lost. This could involve copying the changes back to your local machine or committing them to a remote `git` branch. The process for identifying and handling file changes depends on your choice of agent and how you have configured your sandbox - refer to your agent's documentation for more information.

## Running Cypress Tests Non-interactively

Tests can be run for basic pass/fail measurements inside a sandbox using [`cypress run`](/llm/markdown/app/references/command-line.md#cypress-run).

## Running Cypress Tests Interactively

An agent can run tests one at a time, monitor the execution, and query the application under test throughout using `cypress tap`. Doing this in a sandbox requires running a backgrounded instance of Cypress.

### Prepare the project

When launched in a sandbox, Cypress will use the established `cypress.config.js` file from your project.

If a Cypress configuration has not been created yet Cypress will automatically create one when running in `--e2e` mode when launched by an agent. Component testing mode (`--component`) requires that configuration be created before launching inside a sandbox to avoid potential mistakes with library auto-detection.

### Start Cypress in the background

Run `cypress open` with a browser and a testing type, and leave it running. Use `nohup` so Cypress keeps running after the command that starts it returns:

Command

```
nohup npx cypress open --e2e --browser chromium --project . > cypress-open.log 2>&1 &
```

With the above command, logging output will be written to `cypress-open.log`. This includes error conditions, configuration issues, and the location of files that are created automatically. Instruct your agent to refer to this file in the event of unexpected behavior.

When an AI agent has been detected, Cypress will automatically bypass the "welcome" portions of the UI when `--project`, `--browser` (or set `defaultBrowser` in the config), and a testing type parameter are supplied.

`cypress open --detached` is an alternative that waits until Cypress reports ready before returning, instead of backgrounding right away like the `nohup` form above. It doesn't keep writing to a log the way the `nohup` form's redirect does: once Cypress is ready, `--detached` stops relaying output to the command that started it, so a redirect on it only captures what printed before that point, not anything that happens afterward. For the ongoing log this page's troubleshooting steps assume, start Cypress with the `nohup` form above; reach for `--detached` only when you don't need that log and just want the command to block until Cypress is ready.

Command

```
npx cypress open --e2e --browser chromium --project . --detached
```

### Wait for the browser

Once the "open" mode instance of Cypress has been launched it can be controlled and queried using `cypress tap`. On initial launch it may take some time for the browser to launch; poll `status` until it reports a successful connection to the browser. If the loop completes unsuccessfully read `cypress-open.log` for the reason.

Command

```
deadline=$((SECONDS + 90))

until out=$(npx cypress tap status --json) && echo "$out" | grep -Eq '"browserAttached": *true'; do
  echo "$out" | grep -Eq '"status": *"error"' && { echo "$out"; exit 1; }
  [ "$SECONDS" -ge "$deadline" ] && { echo "no browser after 90 s; see cypress-open.log"; exit 1; }
  sleep 1
done
```

### Run specs and read the results

From here the agent uses the same commands as on a local machine:

Command

```
npx cypress tap run cypress/e2e/checkout.cy.ts
npx cypress tap status --json
npx cypress tap reporter --json
```

See [Getting Started](/llm/markdown/app/tooling/cypress-tap.md#Getting-Started) for the full loop and [Polling for a verdict](/llm/markdown/app/tooling/cypress-tap.md#polling) for how to tell that a run finished. The [`cypress-tap` skill](/llm/markdown/app/tooling/ai-skills.md#cypress-tap) teaches an agent this loop.

If `status` stays at `spec not selected` after you run a spec, run the spec again.

## Choose a browser

`cypress tap` works with Chromium-based browsers: Chrome, Chromium, Edge, and Electron.

*   **Chromium or Chrome.** Pass the browser by name if it's on the `PATH`, or point straight to its executable:
    
    Command
    
    ```
    npx cypress open --e2e --browser /usr/bin/chromium --project .
    ```
    
    Either way, Cypress runs `<path> --version` and checks the answer against what it expects for that browser, such as `Chromium 123.0.6312.4`. Passed by name, it only checks the pattern for that one name; passed by path, it checks every pattern it knows and uses whichever one matches. So if a build isn't recognized under the name you gave it, its full path is worth trying before you give up on it — but if it isn't recognized either way, its `--version` output doesn't match anything Cypress knows, and renaming or moving it won't change that. When Cypress can't use the browser you asked for, it writes the reason, along with the raw `<path> --version` output, to `cypress-open.log`, and `cypress tap status` stays at `browser not selected`.
    
*   **Electron.** Electron ships with Cypress, so it works without installing a browser. Cypress prints a deprecation warning because Electron as a test browser is [deprecated](/llm/markdown/app/references/migration-guide.md#Migrating-away-from-the-Electron-browser).
    

Sandboxes often come with a Chromium already installed by a browser-automation tool. Find it with `ls /opt/pw-browsers/chromium-*/chrome-linux/chrome` (the location varies by sandbox), then run `<path> --version` yourself to check that it introduces itself as Chromium before pointing Cypress at it. From there, pass the path straight to `--browser`, or put it on your `PATH` under a name Cypress knows, like `chromium`, and pass that name instead — a Chromium build like this one is recognized either way. If you try a name and Cypress doesn't recognize it, fall back to the full path, since it's checked against more patterns.

Once a `cypress.config.*` file exists, setting [`defaultBrowser`](/llm/markdown/app/references/configuration.md#Browser) in it works the same way as passing `--browser`, so you can set the browser once in the project instead of on every command that starts Cypress. On the very first launch of an unconfigured project, there is no config file yet to read `defaultBrowser` from, so pass `--browser` instead.

## Configure your sandbox

*   Claude Code on the web
*   Cursor Cloud
*   Other sandboxes

1.  In the environment's network settings, allow the hosts in [Network access](#Network-access).
    
2.  In the environment's setup script, install your dependencies, which also downloads the Cypress binary:
    
    Setup script
    
    ```
    npm ci
    ```
    
    As an alternative that lives in your repository, add a "SessionStart" hook that installs when the session runs on the web:
    
    .claude/settings.json
    
    ```
    {
      "hooks": {
        "SessionStart": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh"
              }
            ]
          }
        ]
      }
    }
    ```
    
    .claude/hooks/session-start.sh
    
    ```
    #!/bin/bash
    set -euo pipefail
    [ "${CLAUDE_CODE_REMOTE:-}" = "true" ] || exit 0
    cd "$CLAUDE_PROJECT_DIR"
    npm install --no-audit --no-fund >&2
    npx cypress verify >&2 || { npx cypress install >&2; npx cypress verify >&2; }
    ```
    
    By default most agents will not persist the sandbox filesystem - this means Cypress and your dependencies will be re-downloaded each time which can have substantial time and bandwidth costs. Review your agent's documentation to determine if it is appropriate to cache the Cypress binary (see [Environment variables](#Environment-variables)) and other dependencies.
    
3.  Claude's sandbox environments already have `xvfb` and a Chromium build installed, but `DISPLAY` is left unset and that Chromium isn't on `PATH`. If you have a display of your own, set `DISPLAY` yourself; otherwise Cypress starts an Xvfb display for you. Symlink the bundled Chromium onto `PATH` as `chromium`, so you can refer to it by name instead of typing out the full path, then start Cypress with it. Tell the agent how to do this, or [add the recipe to your agent's instructions](#Add-the-recipe-to-your-agents-instructions).
    
    Command
    
    ```
    mkdir -p ~/bin
    ln -sf "$(ls /opt/pw-browsers/chromium-*/chrome-linux/chrome | head -1)" ~/bin/chromium
    PATH="$HOME/bin:$PATH" nohup npx cypress open --e2e --browser chromium --project . > cypress-open.log 2>&1 &
    ```
    

Cursor Cloud builds the environment from a Dockerfile and runs commands from `.cursor/environment.json`.

.cursor/Dockerfile

```
FROM cypress/browsers:<tag>
```

.cursor/environment.json

```
{
  "build": { "dockerfile": "Dockerfile" },
  "install": "npm ci"
}
```

Use a [`cypress/browsers` image](/llm/markdown/app/continuous-integration/overview.md#Cypress-Docker-Images) that matches your Node.js version in place of `<tag>`. This image comes with Chrome, Firefox, and Edge, but not Chromium, so pass `--browser chrome` when you [start Cypress](#Start-Cypress-in-the-background). Allow the hosts in [Network access](#Network-access) in the environment's network settings.

Any other sandbox that meets the [requirements](#Requirements) can work, including a local Docker container or an agent sandbox on your own machine. However, it may require custom configuration, dependency installation, or setup steps.

*   If the sandbox restricts where processes can write or which ports they can open, allow writes to the cache folder and connections on `127.0.0.1`.

## Add the recipe to your agent's instructions

Add the steps to the file your agent reads, or install the [`cypress-tap` skill](/llm/markdown/app/tooling/ai-skills.md#cypress-tap), which includes them:

AGENTS.md

```
## Run Cypress tests

Start Cypress once per session, in the background. Replace `<browser>` with
`chromium` if it is on the PATH, or with the path to a Chromium or Chrome
executable:

    nohup npx cypress open --e2e --browser <browser> --project . > cypress-open.log 2>&1 &

Wait until `npx cypress tap status --json` reports a status other than
`not connected` or `browser not selected`. Stop waiting if it reports `error`
or if nothing changes after about 90 seconds, and read `cypress-open.log`.

Then run a spec with `npx cypress tap run <spec>` and poll
`npx cypress tap status --json` until `status` is `passed` or `failed` and
`startedAt` has changed. Read failures with `npx cypress tap reporter --json`.

For component testing, write the configuration, support files, and bundler
config first, and check them with `npx cypress run --component --spec <spec>`.
```

## Environment variables

| Name | Description |
| --- | --- |
| `CYPRESS_AGENT_MODE` | Set to `1` or `true` to make Cypress recognize this run as an AI agent, or to `0` or `false` to turn that off. It only controls recognition: skipping the welcome screen still needs `--project`, a browser (or `defaultBrowser`), and a testing type. |
| `CYPRESS_CACHE_FOLDER` | [Changes the location of the Cypress binary cache](/llm/markdown/app/references/advanced-installation.md#Binary-cache) |
| `CYPRESS_INSTALL_BINARY` | [Sets which Cypress binary to install: a version, URL, or local file](/llm/markdown/app/references/advanced-installation.md#Install-binary) |

## Troubleshooting

*   **The binary download fails.** The sandbox blocks `download.cypress.io` or `cdn.cypress.io`. Allow them, or use a mirror. See [Network access](#Network-access).
    
*   **Cypress reports a missing system library.** The error names the library and an install command for Debian and Ubuntu. For a library it does not recognize, it gives an `apt-file search` command instead; `apt-file` is not installed by default, so install it first (`apt-get install apt-file && apt-file update`). Install the library and run the command again.
    
*   **Cypress reports that Xvfb is missing.** Install the `xvfb` package, or set `DISPLAY` to a display that is running.
    
*   **`status` stays at `not connected`.** Cypress is not running yet, or it has not started loading the project. Read `cypress-open.log`. A project with a configuration file but no support file may also stay at `not connected` instead of reporting an error; check that `cypress/support/e2e.js` or `cypress/support/e2e.ts` exists.
    
*   **`status` reports `error`.** The project failed to load: an invalid configuration file, or (for component testing) a dev server that failed to start. Cypress prints the same error to `cypress-open.log`, and `npx cypress tap status --json` includes it under `error`. After a configuration file fails to load, or `setupNodeEvents` throws, Cypress stops watching the configuration and will need to be restarted to pick up any fixes made to the configuration.
    
*   **`status` stays at `browser not selected`.** This can happen for two reasons. First, check that you passed `--project`, `--browser` (or set `defaultBrowser`), and a testing type to `cypress open`; if you already did, set `CYPRESS_AGENT_MODE=1`, since Cypress didn't recognize the agent. Second, if you pointed `--browser` at a path, Cypress may not have recognized the binary at that path; read the reason in `cypress-open.log` and see [Choose a browser](#Choose-a-browser).
    
*   **Cypress ignores `ELECTRON_RUN_AS_NODE`.** Some agent environments set this variable, which makes Electron run as plain Node.js with no window. `cypress open` ignores it and prints a warning.
    
*   **Component testing shows no session and no error.** Check that the configuration, support file, `component-index.html`, and bundler config (such as `vite.config.*`) exist, and that `setupNodeEvents` does not throw. Cypress does not scaffold component testing for an agent because it needs a framework and bundler to be chosen and their dependencies installed, so create these files first. To check the setup, run `npx cypress run --component --spec <spec>`.
    
*   **`status` reports `not connected` for a session that was running.** `cypress tap` running in a different PID namespace than Cypress, such as a separate container sharing the filesystem, can decide the session is gone. Run `tap` in the same container as Cypress.
    
*   **`cypress open` exits right away.** Run `npx cypress verify` to see why the Cypress binary cannot start.
    

## Limitations

*   The agent and Cypress have to run in the same sandbox.
*   An open Cypress session with a browser uses about 2 GB of memory in a small project, and more in a larger one. Size the sandbox accordingly.
*   Each new session in a sandbox that does not persist the filesystem starts with an empty Cypress binary cache. Downloading, extracting, and verifying the binary adds a delay to startup in each new sandbox instance. It is recommended to configure your sandbox to persist the binary cache directory for optimal performance.
*   `--detached` leaves the Xvfb that Cypress started running after Cypress exits. You can find its process ID with `pgrep Xvfb` and stop it when you're done — but only if it's the only one on the host. If another session is also running Xvfb, `pgrep Xvfb` turns up both with no way to tell which is yours, so skip `pkill Xvfb` too: it would stop every Xvfb on the host, including the other session's. Giving each session its own sandbox, torn down once the session ends, avoids the problem entirely.
*   `cypress tap` has several [documented limitations](/llm/markdown/app/tooling/cypress-tap.md#CLI-Limitations) to be aware of.

## See also

*   [`cypress tap`](/llm/markdown/app/tooling/cypress-tap.md): commands, polling, and examples
*   [Cypress AI Skills](/llm/markdown/app/tooling/ai-skills.md): teach your agent to drive `cypress tap`
*   [Advanced installation](/llm/markdown/app/references/advanced-installation.md): mirrors, proxies, and the binary cache
*   [Command line](/llm/markdown/app/references/command-line.md#cypress-open): the `cypress open` options
