Cloud Agents
environment.json for Cursor Cloud Agents: Full Reference
.cursor/environment.json defines a Cloud Agent environment in code. Its install command now runs while Cursor prepares a Build; start and terminals run when an agent begins. Agents use the config at their starting commit, and new environments build it automatically before activating the latest successful prepared machine.

On this page
What is .cursor/environment.json?
It's the config-as-code option for cloud agent environments. Instead of saving an environment in the dashboard, you commit a JSON file at .cursor/environment.json and every agent launched against the repo builds its machine from it. Cursor resolves configuration by first match: this file, then a personal saved environment, then a team saved environment. When the file exists, it wins.
Agents use the configuration at the commit they start from, which is the behavior that makes the file worth committing in the first place. Push a config change to a branch, start an agent from that branch, and you have tested the new environment without touching what the rest of the team runs. The setup guide covers how this fits into the two setup paths; this page is the field-by-field reference.
- Location
.cursor/environment.jsonin the repository root- Precedence
- Beats personal and team saved environments
- Versioning
- Read at the commit the agent starts from
- Syntax
- JSON with comments allowed; trailing commas rejected; unknown fields rejected
Cursor's docs give the personal saved environment a specific job: one person tries a configuration out before the team inherits it, and it applies only when the repo has no .cursor/environment.json. Committing the file closes that route for everyone who launches an agent against the repo.
Two people sharing a repo can commit it on day one. On a repo a dozen people launch agents against, I would get the environment stable as a saved environment first and commit the file after a week of nobody editing it.
This is covered hands-on in Agent Mode Foundations — 6 short Units, free to read.
Rather do it than read about it? Run 11 interactive Cursor walkthroughs in a simulated editor. Free, no account needed.
What fields does environment.json support?
The published schema defines exactly ten top-level fields, split between the base image (how the machine is built) and the runtime (what runs on it). Fields not in this table don't exist; the schema rejects unknown properties, so a typo like startup fails validation rather than being silently ignored.
- Field
name- Type
- string
- What it does
- Names the environment.
- Field
user- Type
- string
- What it does
- The user the environment runs as.
- Field
install- Type
- string
- What it does
- The update script. Runs to completion during each Build after Cursor clones the repositories; use it for dependencies, generated code, compiled artifacts and disk caches.
- Field
start- Type
- string
- What it does
- Runs when the environment starts; for processes that stay alive during the run, like
sudo service docker start.
- Field
terminals- Type
- array
- What it does
- Named terminals started for the run, each
{ name, command, description }. They run in atmuxsession shared by you and the agent.
- Field
ports- Type
- array
- What it does
- Ports to expose from the container, each
{ name, port }. The docs compare it to devcontainer port forwarding.
- Field
repositoryDependencies- Type
- array
- What it does
- Extra repos, as
github.com/org/repo, that must be included in the GitHub access token generated for the environment.
- Field
snapshot- Type
- string
- What it does
- A snapshot ID to use as the base environment. Copy it from the environment's page in the dashboard.
- Field
agentCanUpdateSnapshot- Type
- boolean
- What it does
- Whether the agent may update the snapshot.
- Field
build- Type
- object
- What it does
- Docker build options:
dockerfile(required withinbuild) andcontext, both paths relative to the.cursorfolder.
| Field | Type | What it does |
|---|---|---|
name | string | Names the environment. |
user | string | The user the environment runs as. |
install | string | The update script. Runs to completion during each Build after Cursor clones the repositories; use it for dependencies, generated code, compiled artifacts and disk caches. |
start | string | Runs when the environment starts; for processes that stay alive during the run, like sudo service docker start. |
terminals | array | Named terminals started for the run, each { name, command, description }. They run in a tmux session shared by you and the agent. |
ports | array | Ports to expose from the container, each { name, port }. The docs compare it to devcontainer port forwarding. |
repositoryDependencies | array | Extra repos, as github.com/org/repo, that must be included in the GitHub access token generated for the environment. |
snapshot | string | A snapshot ID to use as the base environment. Copy it from the environment's page in the dashboard. |
agentCanUpdateSnapshot | boolean | Whether the agent may update the snapshot. |
build | object | Docker build options: dockerfile (required within build) and context, both paths relative to the .cursor folder. |
Verified against Cursor's published schema and Cloud Agent Builds docs, August 2026. `snapshot` and `build` are alternatives for the base image.
Most repos never use most of that table. Both samples Cursor publishes are two fields long, a base image plus an install, and I suspect that is the honest shape of most real configs. The rest arrive one problem at a time. ports shows up the day something outside the container has to reach the dev server, start the day a run needs the Docker daemon up.
Two of these deserve a closer look because their descriptions carry non-obvious detail. The terminals entries take a description field that is displayed to the agent, so it doubles as a prompt: a terminal described as 'Next.js dev server on port 3000' tells the agent what's already running and where. And repositoryDependencies is about access, not cloning: the schema describes it as repos that need to be in the environment's GitHub token, which matters when your build pulls a private sibling repo.
Nothing at the top level is required, incidentally, so an empty {} validates: you get Cursor's default base image and no install step.
How do Builds run install, start and terminals?
Builds split durable preparation from live services. Cursor prepares and activates disk state ahead of an agent run, then starts services and shared terminals when the agent begins. That separation decides where each command belongs.
- 1Prepare the Build. Cursor starts from the configured base, clones every repository at its default branch, and runs
installto completion. - 2Snapshot and activate. A successful Build records environment state and repository commits, then becomes the active prepared machine. A failed Build does not replace it.
- 3Start the agent. Cursor starts the agent from the active Build and runs
startfor services that must be fresh. - 4Open shared terminals. Each configured terminal launches in the workspace's shared
tmuxsession.
The install command must be complete and idempotent because Cursor can run it repeatedly on prepared disk state. A successful Build keeps installed packages, generated files and warmed disk caches. Processes, shell exports and in-memory caches stop at the snapshot, so Docker, databases and tunnels belong in start rather than install.
Idempotent carries more weight than it looks like it should. A later Build can run against prepared disk state, so a script written as though the machine were always new behaves differently the second time through. Keep destructive database setup out of install, and inspect the Build logs and exact commit before changing a config that appears stale.
Put reusable dependency and artifact preparation in install. Put services that must be running in start, and leave task-specific build or test choices in a 'Cursor Cloud specific instructions' section of AGENTS.md.
What does a complete environment.json look like?
Here's a Dockerfile-based config exercising most of the schema: a custom image, an idempotent install, Docker started for the run, a dev server the agent can see, and its port exposed.
{
"name": "web-app",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"install": "pnpm install",
"start": "sudo service docker start",
"terminals": [
{
"name": "dev",
"command": "pnpm dev",
"description": "Next.js dev server on port 3000"
}
],
"ports": [{ "name": "web", "port": 3000 }]
}You would write that version for the reasons the docs give for a Dockerfile: system-level packages, a pinned compiler version, a debugger, a different base OS. Plenty of repos need none of them, and guided setup gets those repos to a working environment in under ten minutes.
The snapshot-based variant is shorter, because the base image is a saved VM instead of a build. This is what you typically commit after agent-driven setup, so the whole team inherits the environment.
{
"snapshot": "snapshot-20260212-00000000-0000-0000-0000-000000000000",
"install": "npm install",
"agentCanUpdateSnapshot": true
}The schema says agentCanUpdateSnapshot controls whether the agent may update the snapshot and stops there, so before setting that last line true on a repo other people run agents against, watch what one setup run does with the permission.
What are the gotchas?
Most environment.json failures come from a handful of documented sharp edges rather than from the fields themselves. These are the ones worth knowing before you debug a broken setup run.
- Paths split between two roots.
build.dockerfileandbuild.contextare relative to.cursor; theinstallcommand runs from the project root. Mixing those up is the classic first failure. - Don't `COPY` the project in your Dockerfile. Cursor manages the workspace and checks out the correct commit itself; a
COPY . .bakes a stale tree into the image. - No secrets field exists. Credentials go in the dashboard's Secrets tab and arrive as environment variables. If your agent can't read one, that's its own fix path.
- Snapshots can fall back. If a snapshot expired, failed or isn't accessible to you, Cursor swaps in the default base image, keeps the rest of the config, still runs
install, and shows 'Environment ready (with warnings)'. Roll back deliberately from Version history in the dashboard settings if you need the old version. - Failed Builds stay inactive. Agents keep using the latest successful active Build while you inspect the failed Build's events, logs and commit SHAs.
- You never get direct machine access. With Dockerfile setup, the image definition is your only lever; there is no SSH into the VM to patch things by hand.
- Layer caching works for you. Changing a Dockerfile rebuilds only the changed layers, so order slow steps first and volatile steps last.
A base snapshot can still expire or become inaccessible. Builds add another protection after that base is resolved: only a successful prepared Build becomes active, and a failure leaves agents on the last successful one. Before the first successful Build, agents use the standard environment startup flow instead.
The Builds tab is now the first place to diagnose that state. It shows the Build status, logs, repository commit, and which Build each agent run used.
If a run behaves like a config you no longer have, check the commit it started from before you check the file. The fix you push afterwards applies to the next agent you start.
Frequently asked questions
Does install run every time a Cloud Agent starts?
With Builds enabled, no. Cursor runs install while preparing each Build in the background. A successful Build becomes the active prepared machine. When an agent starts, Cursor runs start and terminals for live services and app processes instead.
Does environment.json override my saved dashboard environment?
Yes. Cursor resolves configuration by first match: .cursor/environment.json in the repo, then a personal saved environment, then a team saved environment. Committing the file makes it the source of truth for that repo.
Where do I find a snapshot ID for environment.json?
On the environment's page in the Cloud Agents dashboard. After agent-driven setup you can save a snapshot of the machine, and the resulting ID is what the snapshot field references.
How do I test a new environment.json without breaking the team?
Commit the change to a branch and start a cloud agent from that branch. Agents read the config at the commit they start from, so the main branch and every saved environment stay untouched while you verify the new setup.
Can environment.json hold API keys or database credentials?
No. The schema has no secrets field, and the file is committed to your repo in plaintext. Use the Secrets tab in the Cloud Agents dashboard, which encrypts values and exposes them to the agent as environment variables.
Sources & last verified
- Cursor - environment.json schema
- Cursor Docs - Cloud Agent Setup
- Cursor Docs - Cloud Agent Settings
- Cursor Docs - Cloud Agent Builds
Cursor ships frequently. Last updated August 14, 2026.