Name in the product: this mode is a Simple sandbox. The environment page’s header calls it that, and when you create the environment it is the Single service option. This page says Simple sandbox from here on.
When to use this mode
- Your app is one repository that runs on its own: a static site, a front end, or an API that only talks to hosted services.
- You can install, build and serve it with your package manager’s scripts.
- You want the agent to test the exact code in the pull request, not a separate preview deployment.
Set it up
From your project, click Add environment and choose Test every pull request.1
Repo
Pick the Repository and Branch (the default to test against, typically
main; on a pull request qlane uses its head commit automatically). If the app is not at the repository root, set the Root directory. See When the app is not at the repository root.2
Runtime
Under How does this app run?, choose Single service.
3
Login
Add a test account only if signing up isn’t enough for your app; see Accounts and data. Otherwise skip this step.
4
Build
Fill in the four fields. qlane reads your
package.json and suggests values; check them against the rules below, especially serve the production build.5
Review
Check the summary and create the environment.
Accounts and data
Most apps need neither a test account nor seed data. Every run gives the agent an email inbox of its own, so it can sign up to your app the way a new user does, open the confirmation email, and sign in. Before testing a pull request, Qlane also tries to set up what each planned case needs, such as an account, a record or a screen in the right state, mostly through your app’s own pages. That is Set up preconditions before testing under Project settings → PR testing. It is on by default; check that it is on for your project. Add a test account when signing up isn’t enough:- Your app has no self sign-up: accounts are invite-only, created by an admin, or signed in through single sign-on.
- Signing up needs something an inbox can’t give, such as a phone number, a CAPTCHA or an address on your own company’s domain, or a confirmation email your app can’t send from the sandbox.
- Cases need a role or a plan that a new sign-up doesn’t get.
The four fields
Install and build must run to completion and exit
0. A non-zero exit fails the environment and reports that step’s own output, so a failure names the step it came from.
The start command must keep running. It is launched in the background as a long-running server. If it exits with an error, the step fails at once and shows its output. If it exits cleanly, nothing is left listening, and the step fails when the 30-second window ends.
There is no ready-check setting. Your app has 30 seconds to answer. Once the start command is launched, a request to the preview URL must reach your app within a fixed 30-second window. Build time does not count against it, because the build is a separate step that finishes first. The window measures how long your server takes to begin serving, not how long your project takes to compile.
PORT is set in the start command’s environment to the value of the Port field. Some frameworks read it and others ignore it, so treat it as a convenience rather than a guarantee. If your framework takes a port flag or reads a port from its config file, make sure that value and the Port field agree.
Serve the production build, not the dev server
This is the most consequential choice on the screen, and the default answer is the production build:- Build command: your production build.
- Start command: the command that serves what the build produced.
Listen on 0.0.0.0, not 127.0.0.1
Most dev and preview servers listen on localhost only. That default is invisible on a laptop and fatal in a sandbox: a server bound to loopback inside the sandbox can’t be reached from outside it. There is no error message. The 30-second wait expires while the process looks perfectly healthy.
Pass whatever your framework uses to listen on every interface: commonly a --host 0.0.0.0 flag, sometimes a HOST=0.0.0.0 variable. A Composed sandbox has the same requirement.
What you can type in the command fields
Each command is run throughsh -c. What limits you is the check that runs before it. Because the shell is real, that check is what stands between a command and everything a shell would otherwise let it do. Two restrictions apply before anything runs:
-
The first word must be one of:
npm,npx,yarn,pnpm,bun,node,next,turbo,tsc. -
These characters are rejected anywhere in the field, as are line breaks and carriage returns:
; | & ` $ ( ) { } \ < >
npm run build && npm run postbuild is refused, so is cd app && npm start, and so is anything using $(...).
The remedy is better than the thing it replaces: put the chain in a package.json script and call the script. The restriction applies to what you type in the field, never to what the script does, so an && inside a package script runs normally.
package.json
npm run build and npm run serve, and both pass.
When the app is not at the repository root
Set the environment’s Root directory rather than trying tocd into the subdirectory, which the character rules forbid anyway. Commands then run inside that directory, which also lets the package manager find the workspace’s own manifest and use the package manager it declares.
A worked example
A static site built by a bundler, whosepackage.json has a build script and a preview server:
Three things in it matter:
npm cirather thannpm install. It installs exactly what the lockfile pins, which is reproducible and usually faster. It fails outright if the lockfile has drifted frompackage.json; if the first run errors there, switch tonpm install.- The bare
--. With npm, it passes--hostthrough to the underlying command instead of npm consuming it. - The port is the preview server’s, not the dev server’s. They are often different numbers in the same project, and Port must name the one the start command binds.
scripts block in package.json. The script that serves built output is usually called preview, serve or start, but check what it does rather than trusting the name: a start script that runs the dev server is common, and it is the wrong choice.
Variables
- Your app’s own public address isn’t provided.
QLANE_SANDBOX_PUBLIC_URLis set only in a Composed sandbox. A variable on a Simple sandbox that references${QLANE_SANDBOX_PUBLIC_URL}stops the run rather than expanding to nothing. - Names starting with
QLANE_are reserved. Saving a variable of your own with that prefix is refused.
Turn on pull-request testing
A new environment starts with only Mention triggers on: comment@qlane-ai test on a pull request to start a run. To test every pull request automatically, open Project settings → PR testing, find the environment under Per environment, and turn on Automatic runs. See Triggers for the details, and Docker Compose for Watch paths, which work the same way here.
What qlane does on each pull request
- Provisions a fresh, isolated sandbox.
- Clones your repository at the pull request’s head commit.
- Runs your install command, then your build command if you set one, then starts your app.
- Waits up to 30 seconds for the app to answer on the configured port.
- Tries to set up the accounts and data the planned cases need, unless setup is off.
- Connects the agent and runs your active test cases plus any drafts generated from the pull request’s diff.
- Posts a check and, if anything broke, a structured review.
What the agent can do in this mode
In addition to a real browser, the agent gets:- Shell access inside the sandbox (
bash,readFile). It can inspect logs, check files it wrote, and re-run commands. - Code awareness. It reads the repository to understand failures and write better reproductions.
- Deep testing. When stuck, the agent can add temporary instrumentation, re-run, and observe. The sandbox is torn down after the session, so changes to its files and processes don’t escape. Anything written to a hosted service your app talks to does.
Common gotchas
- Native dependencies. Anything that needs a system library should be in your package’s install hooks. The sandbox is Linux-based, so macOS-only build steps fail.
- A slow server start. The 30-second window covers start-up only, not the build. If your server needs longer to begin serving, move the work into the build step, or serve the prebuilt output with a lighter server.
- Required variables. Set the auth secret, API URLs and anything else the app needs under Runtime Variables. Without them the app won’t boot, and the run fails before the agent connects.
Checklist
- The start command serves the production build, not a dev server (why)
- The server listens on
0.0.0.0, not127.0.0.1(why) - Port is the port the start command actually binds (fields)
- Every field starts with an allowed command and contains no rejected characters (rules)
- Anything that needs chaining lives in a package script, not in a field (rules)
- The app is at the repository root, or Root directory is set (root directory)
- The server answers within 30 seconds of the start command launching (fields)
- The app needs no backing services. If it does, use a Composed sandbox
- A new sign-up can reach what your cases test, or you added a test account that can (accounts and data)

