Skip to main content
You don’t need to write this by hand. The easiest way to set up your environment is to start a Devin session and ask it to configure the repo. Devin will analyze the project, install dependencies, and propose a configuration — you click Approve on the suggestion cards in your timeline. This guide is for when you want to understand what Devin generated, customize it, or write a configuration from scratch.

How Devin’s environment works

Devin runs on its own VM. Each session boots from a machine image — a saved snapshot with your tools, repos, and dependencies pre-installed. You control what goes into that image by editing your environment configuration in Settings > Devin’s Environment.

Writing your configuration

Your environment.yaml has three sections:
SectionPurposeWhen it runsExecuted?
initializeInstall tools and compile dependenciesBuild only — results are saved in the machine imageYes
maintenanceKeep dependencies up to date, write credential configsBuild + start of every sessionYes
knowledgeShort notes tied to the environment setup (e.g., lint/test commands)Loaded at session startNo — reference notes only

initialize — one-time setup

Use initialize for anything slow or that only needs to happen once: installing package managers, global CLI tools, compiling native dependencies, or adding system packages. Results are saved in the machine image and do not run again at session start.
initialize: |
  curl -LsSf https://astral.sh/uv/install.sh | sh
  npm install -g pnpm
The multi-line string is executed as a single bash script with set -e, so any failing line stops the build. Use \ for line continuation, or switch to the expanded form for named steps that show up individually in build logs.
The secrets file is removed before the machine image is saved. If a command in initialize writes a secret value into a config file (e.g., an auth token into .npmrc), that value may persist in the image — which is a security risk. Put credential-writing steps in maintenance instead, where secrets are freshly loaded each session.

Expanded form

When you need named steps, use the list form. Named steps make build logs easier to debug — when something fails, you’ll see which step broke instead of just a line number.
initialize:
  - name: Install uv
    run: curl -LsSf https://astral.sh/uv/install.sh | sh
  - name: Install system packages
    run: |
      sudo apt-get update -qq
      sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq libpq-dev ffmpeg
You can mix named and unnamed steps. Steps without run are skipped.

Setting environment variables

To set environment variables for subsequent steps, write KEY=VALUE lines to the $ENVRC file (like GitHub Actions’ $GITHUB_ENV). All variables written to $ENVRC are automatically exported for subsequent steps and the Devin session.
initialize:
  - name: Configure environment
    run: |
      echo "NODE_ENV=production" >> $ENVRC
      echo "CI=true" >> $ENVRC

maintenance — every session

Use maintenance to keep dependencies up to date and to write credential config files. It runs during the build (after initialize) and at the start of every session, after pulling the latest code. Because it runs on every session start, commands should be fast and incremental.
maintenance: |
  npm install
  pip install -r requirements.txt
Use npm install, not npm ci. npm ci deletes node_modules and reinstalls everything from scratch on every run. npm install does an incremental update, which is much faster when dependencies haven’t changed.

Credential configuration

Any step that writes secrets into config files (.npmrc, settings.xml, pip.conf) belongs in maintenance, not initialize. This keeps credentials fresh at the start of every session.
maintenance:
  - name: Install dependencies
    run: npm install
  - name: Configure private registry
    run: npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN

knowledge — environment-specific notes (optional)

Use knowledge for small pieces of setup information that Devin needs to actually use the environment you just built — things like the lint command, the test command, or how to start the dev server.
This is not the same as the main Knowledge feature. For most things you want Devin to remember — architecture, conventions, gotchas, team workflows — use the standalone Knowledge feature. It has richer triggers, is easier to edit, and is the right place for general project context.Only use the knowledge section in environment.yaml for short notes that are directly tied to the environment setup in this file (e.g., “the lint command is npm run lint”). This section is optional and most environments don’t need it.
Entries are reference notes — they are not executed. Keep them short:
knowledge:
  - name: lint
    contents: |
      Run `npm run lint` to check for errors.
      Run `npm run lint:fix` to auto-fix.
  - name: test
    contents: |
      Run `npm test` for the full suite.
      Run `npm test -- --watch` during development.
  - name: startup
    contents: |
      Run `npm run dev` to start the dev server on port 3000.

Configuration levels

Devin supports three configuration tiers. Each tier’s commands are strictly additive — they run in sequence during builds, and lower levels cannot override or modify what higher levels configure.
LevelWhere to configureWhat to put hereExamples
Account-wide (Enterprise)Enterprise Settings > EnvironmentInfrastructure that all orgs needCA certificates, corporate proxy, VPN, DNS
Org-wideSettings > Environment > Organization-wide setupTools and setup shared across all reposLanguage runtimes (pnpm, uv), Docker auth, shared CLI tools
Repo-specificSettings > Environment > [repo]Project-specific setup and knowledgenpm install, lint/test/build commands, architecture notes
How to decide what goes where:
  • If every org in your enterprise needs it → account-wide
  • If every repo in your org needs it → org-wide
  • If only one repo needs it → repo-specific
Enterprise users: For detailed guidance on enterprise-level configuration — permissions, build cascading, multi-org management, and enterprise migration — see the Enterprise Environment Setup page.

How it works

The machine image

Your organization has one machine image — a VM snapshot with your tools, repos, and dependencies pre-installed. All configured repos are cloned and set up in that single image. Every session boots from a fresh copy.

How builds work

A build creates a new machine image in this order:
1. Enterprise config (runs in ~):
   a. initialize
   b. maintenance
2. Org-wide config (runs in ~):
   a. initialize
   b. maintenance
3. Clone all repositories (up to 10 concurrent)
4. For each configured repo, in the order shown in Settings
   (runs in ~/repos/<repo-name>):
   a. initialize
   b. maintenance
5. Health check, then image is saved
Layers are additive: repo-specific commands can use tools installed by the org-wide or enterprise config. Lower levels cannot override what a higher level set up. Builds typically take 5–15 minutes. Individual commands time out after 1 hour; the whole build times out after 2 hours.

How sessions work

Each session boots a fresh copy of the machine image. When the session ends, all changes are discarded. At session start:
  1. Enterprise and org-wide maintenance runs (in ~).
  2. The latest code is pulled for the relevant repo(s).
  3. That repo’s maintenance runs again to catch dependency changes since the last build.
  4. That repo’s knowledge entries are loaded into Devin’s context.
Knowledge is per-repo. If you have 5 repos configured, Devin only sees the knowledge entries for the one it’s working on.

Build statuses

StatusMeaning
SuccessAll steps completed. Machine image is ready.
PartialSome repos failed but the core build succeeded. Sessions will work but some repos may not be fully set up.
FailedCore build failed (e.g., clone failure, enterprise/org setup failed).
CancelledSuperseded by a newer build.
SkippedNo configuration changes detected.
Build failing? See Troubleshooting & FAQ for a step-by-step debugging guide.

Repository states

In the Environment Settings UI, repositories appear in three states:
StateMeaning
ConfiguredHas YAML configuration with initialize/maintenance/knowledge. Fully set up in the machine image.
IncludedCloned into the machine image but has no custom configuration. Devin can access the code.
AvailableAccessible to the org but not added to the environment. Not cloned.
Included vs. configured: An “included” repo is cloned so Devin can access the code, but has no custom setup commands. A “configured” repo has explicit initialize/maintenance/knowledge instructions.

What triggers a rebuild?

A new build is triggered when:

Secrets

Reference secrets with $VARIABLE_NAME syntax. Add them in Settings > Secrets.
maintenance:
  - name: Configure private registry
    run: npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN
Secrets are available as environment variables during builds and sessions. The secrets file is removed before the machine image is saved — but if a command expanded a secret value into a config file during initialize, that expanded value may persist in the image. Always write credentials in maintenance instead, where they’re freshly loaded each session. For more on secrets management, see Secrets & Site Cookies.

Repo patterns

Multiple repositories

When you configure multiple repos, each one gets its own YAML in Settings. During a build, all of them are set up in the same image — cloned into separate directories, dependencies installed independently. During a session, only the active repo’s maintenance and knowledge are relevant. What if two repos conflict? Each repo’s commands run in its own directory, so dependency conflicts are rare. If two repos install different versions of a global tool or modify shared files (like ~/.bashrc), the last one to run wins. Put shared tool installs in the org-wide config to avoid this.

Monorepos

For a monorepo, you write a single YAML that covers all the sub-projects. Use subshells to run commands in subdirectories without changing the working directory for subsequent steps:
initialize:
  - name: Install pnpm
    run: npm install -g pnpm
  - name: Install uv
    run: curl -LsSf https://astral.sh/uv/install.sh | sh

maintenance:
  - name: Frontend deps
    run: (cd packages/frontend && pnpm install)
  - name: Backend deps
    run: (cd packages/backend && uv sync)
  - name: Shared library
    run: (cd packages/shared && pnpm install)

knowledge:
  - name: structure
    contents: |
      Monorepo with three packages:
      - `packages/frontend` — React app (TypeScript, pnpm)
      - `packages/backend` — Python API (FastAPI, uv)
      - `packages/shared` — Shared TypeScript utilities
The parentheses (cd ... && ...) are important — they run the command in a subshell so the working directory resets for the next step. When to use multi-repo vs. monorepo config: If the code lives in one Git repository, use one YAML with subshells. If it’s spread across multiple Git repositories, configure each repo separately in Settings.

Next steps

YAML Reference

Field reference tables, execution details, pre-installed tools, and glossary.

Configuration Examples

Copy-paste examples for Node.js, Python, Java, Go, monorepos, enterprise patterns, and more.

Troubleshooting & FAQ

Debug build failures, migrate from interactive setup, and find answers to common questions.

Enterprise Environment Setup

Permissions, build cascading, multi-org management, and enterprise migration.