Files
coder/docs/admin/integrations/devcontainers/integration.md
T
Nick Vigilante e458692cb8 refactor(docs): convert absolute coder/coder blob/tree/main links to relative (DOCS-351) (#26341)
Closes [DOCS-351](https://linear.app/codercom/issue/DOCS-351).

> [!WARNING]
> **DO NOT MERGE** until
[DOCS-349](https://linear.app/codercom/issue/DOCS-349)
([coder.com#877](https://github.com/coder/coder.com/pull/877)) has
shipped to production and baked for at least one Vercel cycle.
>
> Without DOCS-349, the relative links in this PR resolve to broken
docs-route URLs (`/docs/helm/coder/values.yaml` -> 404) instead of
GitHub URLs tagged with the displayed docs version. DOCS-349 fixes the
rewriter to classify these as GitHub blob/tree URLs with the page's
resolved ref.

## TL;DR

Converts 121 absolute
`https://github.com/coder/coder/(blob|tree)/main/<path>` links across 39
docs markdown files to relative paths. After this lands AND DOCS-349
deploys, every one of these links will follow the displayed docs version
(mainline tag on bare URLs, explicit tag on `/@vX.Y.Z/`, `main` on
`/@main/`) instead of always pointing to `main`.

## Why

Today a reader on `/docs/@v2.30.0/install/docker` follows a
`compose.yaml` link and arrives at `main`'s `compose.yaml`, which
doesn't necessarily match what the docs page describes. Helm values,
Terraform templates, and source-code references in particular drift
across versions. The fix is to let the coder.com rewriter substitute the
page's resolved ref into the URL; that only works on relative links.

## Example payoff (post-DOCS-349)

| URL | Today (absolute, always `main`) | After (relative + rewriter) |
|---|---|---|
| `/docs/install/docker` |
`https://github.com/coder/coder/blob/main/compose.yaml` |
`https://github.com/coder/coder/blob/v2.34.1/compose.yaml` (today's
mainline) |
| `/docs/@v2.30.0/install/docker` | same as above |
`https://github.com/coder/coder/blob/v2.30.0/compose.yaml` |
| `/docs/@main/install/docker` | same as above |
`https://github.com/coder/coder/blob/main/compose.yaml` |

## Scope

- **121 conversions** across **39 files**.
- Verb breakdown: `tree/main` (directories) and `blob/main` (files),
both flipped to relative paths.
- Line anchors (`#L23-L24`) and query strings preserved verbatim.
- Conversion is mechanical: relative path computed from the doc file's
directory to the target via `os.path.relpath`. Any path starting at the
same directory or below gets a `./` prefix; otherwise `../` chains.

## Rebased on main

The branch was rebased onto `main` after the DOCS-350 hotfix
([#26339](https://github.com/coder/coder/pull/26339)) merged. The hotfix
repointed 3 `docs-backend-contrib-guide` refs in `backend.md` to `main`,
which then needed the same `main` -> relative conversion this PR is
doing for the other 121 links. The conflict was resolved by reapplying
the mechanical conversion to `backend.md` after taking the hotfix's
content. Net result: those 3 links land here as relative, same as
everything else. New HEAD `3f501cb622`.

## Inline fix folded in: dead `nix` link

- `docs/about/contributing/CONTRIBUTING.md:7` -> `../../../nix`

The original absolute URL `https://github.com/coder/coder/tree/main/nix`
already returned 404 today. Repointed to `flake.nix` (modern Nix
entrypoint, what the prose "Nix environment" semantically refers to).
Closes [DOCS-357](https://linear.app/codercom/issue/DOCS-357) here since
the `check-docs` Linkspector job surfaced it during rebase; cheaper to
fix inline than in a separate single-line PR.

## Out of scope (filed separately)

- [DOCS-350](https://linear.app/codercom/issue/DOCS-350): 3 dead
`docs-backend-contrib-guide` branch refs in `backend.md`
([#26339](https://github.com/coder/coder/pull/26339), merged).
- [DOCS-352](https://linear.app/codercom/issue/DOCS-352): 10 SHA-pinned
`(blob|tree)/<sha>` links pending intent review.
- [DOCS-355](https://linear.app/codercom/issue/DOCS-355): code-server
analog (4 absolute `(blob|tree)/main` links in `coder/code-server`).
- [DOCS-356](https://linear.app/codercom/issue/DOCS-356): 2 upstream
content bugs in `coder/code-server/docs/CONTRIBUTING.md` (independent of
this PR).


## Not triggering `/coder-agents-review`

Docs-only edit; per `AGENTS.md` the bot review is reserved for
product/CI changes.

## Pre-mortem

| Concern | Mitigation |
|---|---|
| Merging before DOCS-349 deploys regresses ~120 currently-working links
into 404s on coder.com | Clear DO-NOT-MERGE banner; tracked as blocker
in Linear. |
| Relative path computed incorrectly (off-by-one `..`) | Verified all
114 newly-relative non-md/non-image paths resolve to existing files in
the repo (only exception is the pre-existing dead `nix` link above). |
| Line anchors stripped during conversion | Preserved by the
substitution regex; verified `#L<n>-L<m>` cases in `airgap.md` and
`speed-up-templates.md`. |
| Future code reorgs change file locations | Relative links will start
pointing to nothing. Same failure mode as absolute links pointing to
renamed files; can be caught with a future link-checker job. |

## Validation

```
$ grep -rE 'github\.com/coder/coder/(blob|tree)/main' docs --include="*.md" | wc -l
0
$ git diff --stat origin/main | tail -1
39 files changed, 118 insertions(+), 118 deletions(-)
```

114 newly-relative paths verified to resolve to existing repo files
(Python `os.path.exists` check on each computed target).

<details>
<summary>Decision log + planning context</summary>

**Why relative over `(blob|tree)/{{currentDocsVersion}}/...`
templating**: relative paths require zero markdown-system support and
zero upstream churn beyond this one PR. Templating would require a
preprocessor on `coder.com` side AND a convention upstream authors have
to remember; relative paths just work in a plain editor and
`github.com`'s own renderer too.

**Why `./` prefix on same-directory targets**: makes the conversion
grep-able later (`grep -E '\((\.\./|\./)'`).

**Why preserve `#L<n>-L<m>` anchors verbatim**: the anchor is meaningful
to the linked file's content, not to the URL form; keeping it as-is
preserves authorial intent. If the file later changes such that the line
range drifts, that's a different problem the SHA-pin audit
([DOCS-352](https://linear.app/codercom/issue/DOCS-352)) will surface.

</details>

---

*Generated by Coder Agents on @nickvigilante's behalf.*





## Drive-by external link fix folded in

`docs/about/contributing/CONTRIBUTING.md:296` cited
`https://reflectoring.io/meaningful-commit-messages/` which is returning
HTTP 503 (the host appears to be down site-wide right now). `check-docs`
Linkspector flagged it after the rebase. Replaced with
`https://cbea.ms/git-commit/` (Chris Beams' canonical "If applied, this
commit will..." article, confirmed 200), which is the original source of
the rule the prose recites anyway.
2026-06-22 11:39:12 -04:00

11 KiB

Configure a template for Dev Containers

This guide covers the Dev Containers Integration, which uses Docker. For environments without Docker, see Envbuilder as an alternative.

To enable Dev Containers in workspaces, configure your template with the Dev Containers modules and configurations outlined in this doc.

Dev Containers are currently not supported in Windows or macOS workspaces.

Configuration Modes

There are two approaches to configuring Dev Containers in Coder:

Manual Configuration

Use the coder_devcontainer Terraform resource to explicitly define which Dev Containers should be started in your workspace. This approach provides:

  • Predictable behavior and explicit control
  • Clear template configuration
  • Easier troubleshooting
  • Better for production environments

This is the recommended approach for most use cases.

Project Discovery

Alternatively, enable automatic discovery of Dev Containers in Git repositories. The agent scans for devcontainer.json files and surfaces them in the Coder UI. See Environment Variables for configuration options.

This approach is useful when developers frequently switch between repositories or work with many projects, as it reduces template maintenance overhead.

Install the Dev Containers CLI

Use the devcontainers-cli module to ensure the @devcontainers/cli is installed in your workspace:

module "devcontainers-cli" {
  count    = data.coder_workspace.me.start_count
  source   = "registry.coder.com/coder/devcontainers-cli/coder"
  agent_id = coder_agent.dev.id
}

Alternatively, install the devcontainer CLI manually in your base image.

Configure Automatic Dev Container Startup

The coder_devcontainer resource automatically starts a Dev Container in your workspace, ensuring it's ready when you access the workspace:

resource "coder_devcontainer" "my-repository" {
  count            = data.coder_workspace.me.start_count
  agent_id         = coder_agent.dev.id
  workspace_folder = "/home/coder/my-repository"
}

The workspace_folder attribute must point to a valid project folder containing a devcontainer.json file. Consider using the git-clone module to ensure your repository is cloned and ready for automatic startup.

For multi-repo workspaces, define multiple coder_devcontainer resources, each pointing to a different repository. Each one runs as a separate sub-agent with its own terminal and apps in the dashboard.

Enable Dev Containers Integration

Dev Containers integration is enabled by default in Coder 2.24.0 and later. You don't need to set any environment variables unless you want to change the default behavior.

If you need to explicitly disable Dev Containers, set the CODER_AGENT_DEVCONTAINERS_ENABLE environment variable to false:

resource "docker_container" "workspace" {
  count = data.coder_workspace.me.start_count
  image = "codercom/oss-dogfood:latest"
  env = [
    "CODER_AGENT_DEVCONTAINERS_ENABLE=false",  # Explicitly disable
    # ... Other environment variables.
  ]
  # ... Other container configuration.
}

See the Environment Variables section below for more details on available configuration options.

Environment Variables

The following environment variables control Dev Container behavior in your workspace. Both CODER_AGENT_DEVCONTAINERS_ENABLE and CODER_AGENT_DEVCONTAINERS_PROJECT_DISCOVERY_ENABLE are enabled by default, so you typically don't need to set them unless you want to explicitly disable the feature.

CODER_AGENT_DEVCONTAINERS_ENABLE

Default: trueAdded in: v2.24.0

Enables the Dev Containers integration in the Coder agent.

The Dev Containers feature is enabled by default. You can explicitly disable it by setting this to false.

CODER_AGENT_DEVCONTAINERS_PROJECT_DISCOVERY_ENABLE

Default: trueAdded in: v2.25.0

Enables automatic discovery of Dev Containers in Git repositories.

When enabled, the agent scans the configured working directory (set via the directory attribute in coder_agent, typically the user's home directory) for Git repositories. If the directory itself is a Git repository, it searches that project. Otherwise, it searches immediate subdirectories for Git repositories.

For each repository found, the agent looks for devcontainer.json files in the standard locations and surfaces discovered Dev Containers in the Coder UI. Discovery respects .gitignore patterns.

Set to false if you prefer explicit configuration via coder_devcontainer.

CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE

Default: falseAdded in: v2.25.0

Automatically starts Dev Containers discovered via project discovery.

When enabled, discovered Dev Containers will be automatically built and started during workspace initialization. This only applies to Dev Containers found via project discovery. Dev Containers defined with the coder_devcontainer resource always auto-start regardless of this setting.

Attach Resources to Dev Containers

You can attach coder_app, coder_script, and coder_env resources to a coder_devcontainer by referencing its subagent_id attribute as the agent_id:

resource "coder_devcontainer" "my-repository" {
  count            = data.coder_workspace.me.start_count
  agent_id         = coder_agent.dev.id
  workspace_folder = "/home/coder/my-repository"
}

resource "coder_app" "code-server" {
  count        = data.coder_workspace.me.start_count
  agent_id     = coder_devcontainer.my-repository[0].subagent_id
  # ...
}

resource "coder_script" "dev-setup" {
  count        = data.coder_workspace.me.start_count
  agent_id     = coder_devcontainer.my-repository[0].subagent_id
  # ...
}

resource "coder_env" "my-var" {
  count    = data.coder_workspace.me.start_count
  agent_id = coder_devcontainer.my-repository[0].subagent_id
  # ...
}

This also enables using Coder registry modules that depend on these resources inside dev containers, by passing the subagent_id as the module's agent_id.

Terraform-managed dev containers

When a coder_devcontainer has any coder_app, coder_script, or coder_env resource attached, it becomes a terraform-managed dev container. This changes how Coder handles the sub-agent:

  • The sub-agent is pre-defined during Terraform provisioning rather than created dynamically.
  • On dev container configuration changes, Coder updates the sub-agent in-place instead of deleting and recreating it.

Interaction with devcontainer.json customizations

Terraform-defined resources and devcontainer.json customizations work together with some limitations. The displayApps settings from devcontainer.json are applied to terraform-managed dev containers, so you can control built-in app visibility (e.g., hide VS Code Insiders) via devcontainer.json even when using Terraform resources.

However, custom apps defined in devcontainer.json are not applied to terraform-managed dev containers. If you need custom apps, define them as coder_app resources in Terraform instead.

Per-Container Customizations

Developers can customize individual dev containers using the customizations.coder block in their devcontainer.json file. Available options include:

  • ignore — Hide a dev container from Coder completely
  • autoStart — Control whether the container starts automatically (requires CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE to be enabled)
  • name — Set a custom agent name
  • displayApps — Control which built-in apps appear
  • apps — Define custom applications

For the full reference, see Customizing dev containers.

Complete Template Example

Here's a simplified template example that uses Dev Containers with manual configuration:

terraform {
  required_providers {
    coder  = { source = "coder/coder" }
    docker = { source = "kreuzwerker/docker" }
  }
}

provider "coder" {}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}

resource "coder_agent" "dev" {
  arch                    = "amd64"
  os                      = "linux"
  startup_script_behavior = "blocking"
  startup_script          = "sudo service docker start"
  shutdown_script         = "sudo service docker stop"
  # ...
}

module "devcontainers-cli" {
  count    = data.coder_workspace.me.start_count
  source   = "registry.coder.com/coder/devcontainers-cli/coder"
  agent_id = coder_agent.dev.id
}

resource "coder_devcontainer" "my-repository" {
  count            = data.coder_workspace.me.start_count
  agent_id         = coder_agent.dev.id
  workspace_folder = "/home/coder/my-repository"
}

# Attaching resources to dev containers is optional. By attaching
# this resource to the dev container, we are changing how the dev
# container will be treated by Coder. This limits the ability to
# customize the injected agent via the devcontainer.json file.
resource "coder_env" "env" {
  count    = data.coder_workspace.me.start_count
  agent_id = coder_devcontainer.my-repository[0].subagent_id
  name     = "MY_VAR"
  value    = "my-value"
}

Alternative: Project Discovery with Autostart

By default, discovered containers appear in the dashboard but developers must manually start them. To have them start automatically, enable autostart:

resource "docker_container" "workspace" {
  count = data.coder_workspace.me.start_count
  image = "codercom/oss-dogfood:latest"
  env = [
    # Project discovery is enabled by default, but autostart is not.
    # Enable autostart to automatically build and start discovered containers:
    "CODER_AGENT_DEVCONTAINERS_DISCOVERY_AUTOSTART_ENABLE=true",
    # ... Other environment variables.
  ]
  # ... Other container configuration.
}

With autostart enabled:

  • Discovered containers automatically build and start during workspace initialization
  • The coder_devcontainer resource is not required
  • Developers can work with multiple projects seamlessly

Note

When using project discovery, you still need to install the devcontainers CLI using the module or in your base image.

Example Template

The Docker (Dev Containers) starter template demonstrates Dev Containers integration using Docker-in-Docker. It includes the devcontainers-cli module, git-clone module, and the coder_devcontainer resource.

Next Steps