chore: remove the migrated dogfood template sources (#29469)

Deletes the dogfood template sources that were migrated to the
infrastructure repository, leaving only the image build inputs in this
repo.

Stacked on #29400 (which removes the `deploy_template` job and
`dogfood/main.tf`); depends on coder/dogfood#551 being applied first.
#29468 stacks on top of this branch.

## Changes

- Delete `dogfood/coder/main.tf`, `dogfood/coder-envbuilder/`,
`dogfood/vscode-coder/main.tf`, and the template READMEs and guide
- Delete `dogfood/coder/devcontainer.json`: its only reader was the
retired `coder-envbuilder` template, and it referenced a `Dockerfile`
removed in #24267
- Add `dogfood/README.md` documenting what remains here and where
template changes go
- Drop the now-empty dependabot entries (`/dogfood/coder-envbuilder`
docker, `dogfood/*/` terraform)

Kept: `dogfood/coder/ubuntu-22.04/`, `dogfood/coder/ubuntu-26.04/`,
`dogfood/coder/Makefile`, and `dogfood/vscode-coder/Dockerfile`. Image
builds, tags, and `scripts/dogfood/*` are unchanged; the contract with
the templates is the published Docker tag.

Checks: `actionlint`, `shellcheck`,
`compute-base-sha.sh`/`compute-final-sha.sh` still resolve, docs lint
and `make lint/emdash` clean.

---

Generated by Coder Agents on behalf of @jdomeracki-coder.

---------

Co-authored-by: McKayla はな <kayla@tree.camp>
This commit is contained in:
Jakub Domeracki
2026-09-18 09:55:21 +02:00
committed by GitHub
co-authored by McKayla はな
parent 0ea777a341
commit 95a3f01ecc
12 changed files with 21 additions and 2286 deletions
-2
View File
@@ -40,7 +40,6 @@ updates:
- package-ecosystem: "docker"
directories:
- "/dogfood/coder"
- "/dogfood/coder-envbuilder"
- "/scripts"
- "/examples/templates/docker/build"
- "/examples/parameters/build"
@@ -101,7 +100,6 @@ updates:
- package-ecosystem: "terraform"
directories:
- "dogfood/*/"
- "examples/templates/*/"
schedule:
interval: "weekly"
+1 -1
View File
@@ -77,7 +77,7 @@ The Coder backend is organized into multiple packages and directories, each with
* [queries](../../../coderd/database/queries): contains SQL files with queries, `sqlc` compiles them to [Go functions](../../../coderd/database/queries.sql.go)
* [sqlc.yaml](../../../coderd/database/sqlc.yaml): defines mappings between SQL types and custom Go structures
* [codersdk](../../../codersdk): user-facing API entities used by CLI and site to communicate with `coderd` endpoints
* [dogfood](../../../dogfood): Terraform definition of the dogfood cluster deployment
* [dogfood](../../../dogfood): build inputs for the images used by Coder's dogfood workspaces. The workspace templates that consume these images live in an internal Coder infrastructure repository.
* [enterprise](../../../enterprise): enterprise-only features, notice similar file structure to repository root (`audit`, `cli`, `cmd`, `coderd`, etc.)
* [coderd](../../../enterprise/coderd)
* [prebuilds](../../../enterprise/coderd/prebuilds): core logic of prebuilt workspaces - reconciliation loop
+19
View File
@@ -0,0 +1,19 @@
# Dogfood workspace images
This directory holds the build inputs for the images used by Coder's own
dogfood workspaces:
| Path | Image |
| ------------------------- | ------------------------------------------ |
| `coder/ubuntu-26.04/` | `codercom/oss-dogfood:26.04`, `:latest` |
| `vscode-coder/Dockerfile` | `codercom/oss-dogfood-vscode-coder:latest` |
`.github/workflows/dogfood.yaml` builds each base image, layers the tools from
the repository's `mise.toml` and `mise.lock` with `mise oci build`, validates the
result by running `make gen`, `fmt`, `lint`, and a Linux build inside it, and
pushes to Docker Hub on `main`. Use `coder/Makefile` to build locally.
The workspace templates that consume these images live in an internal Coder
infrastructure repository, not here. The only contract between the two is the
published image tag, which the templates resolve to a digest. Template changes
therefore go to the internal infrastructure repository; image changes go here.
-15
View File
@@ -1,15 +0,0 @@
# envbuilder dogfood template
This template uses the same image as the [dogfood](../dogfood) template, but
builds it on-demand using the latest _preview_ version of [envbuilder](https://github.com/coder/envbuilder).
In theory, it should work with any Git repository containing a `devcontainer.json`.
The Git repository specified by `devcontainer_repo` is cloned into `/workspaces` upon startup and the container is built from the devcontainer located under the path specified by `devcontainer_dir`.
The `region` parameters are the same as for the [dogfood](../dogfood) template.
The `/workspaces` directory is persisted as a Docker volume, so any changes you make to the dogfood Dockerfile or devcontainer.json will be applied upon restarting your workspace.
## Personalization
The startup script runs your `~/personalize` file if it exists.
You also have a persistent home directory under `/home/coder`.
-443
View File
@@ -1,443 +0,0 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
docker = {
source = "kreuzwerker/docker"
version = "~> 4.0"
}
envbuilder = {
source = "coder/envbuilder"
}
}
}
locals {
// These are cluster service addresses mapped to Tailscale nodes.
// Ask #dogfood-admins for help.
// NOTE: keep these up to date with those in ../coder/main.tf!
docker_host = {
"" = "tcp://rubinsky-pit-cdr-dev.tailscale.svc.cluster.local:2375"
"us-pittsburgh" = "tcp://rubinsky-pit-cdr-dev.tailscale.svc.cluster.local:2375"
// For legacy reasons, this host is labelled `eu-helsinki` but it's
// actually in Germany now.
"eu-helsinki" = "tcp://katerose-fsn-cdr-dev.tailscale.svc.cluster.local:2375"
"ap-sydney" = "tcp://wolfgang-syd-cdr-dev.tailscale.svc.cluster.local:2375"
"za-jnb" = "tcp://greenhill-jnb-cdr-dev.tailscale.svc.cluster.local:2375"
}
envbuilder_repo = "ghcr.io/coder/envbuilder-preview"
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
// Envbuilder clones repos to /workspaces by default.
repo_dir = "/workspaces/coder"
}
data "coder_parameter" "devcontainer_repo" {
type = "string"
name = "Devcontainer Repository"
default = "https://github.com/coder/coder"
description = "Repo containing a devcontainer.json. This is only cloned once."
mutable = false
}
data "coder_parameter" "devcontainer_dir" {
type = "string"
name = "Devcontainer Directory"
default = "dogfood/coder/"
description = "Directory containing a devcontainer.json relative to the repository root"
mutable = true
}
data "coder_parameter" "region" {
type = "string"
name = "Region"
icon = "/emojis/1f30e.png"
default = "us-pittsburgh"
option {
icon = "/emojis/1f1fa-1f1f8.png"
name = "Pittsburgh"
value = "us-pittsburgh"
}
option {
icon = "/emojis/1f1e9-1f1ea.png"
name = "Falkenstein"
// For legacy reasons, this host is labelled `eu-helsinki` but it's
// actually in Germany now.
value = "eu-helsinki"
}
option {
icon = "/emojis/1f1e6-1f1fa.png"
name = "Sydney"
value = "ap-sydney"
}
option {
icon = "/emojis/1f1ff-1f1e6.png"
name = "Johannesburg"
value = "za-jnb"
}
}
# This file is mounted as a Kubernetes secret on provisioner pods.
# It contains the required credentials for the envbuilder cache repo.
variable "envbuilder_cache_dockerconfigjson_path" {
type = string
sensitive = true
}
provider "docker" {
host = lookup(local.docker_host, data.coder_parameter.region.value)
registry_auth {
address = "us-central1-docker.pkg.dev"
config_file = pathexpand(var.envbuilder_cache_dockerconfigjson_path)
}
}
provider "coder" {}
data "coder_external_auth" "github" {
id = "github"
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
module "slackme" {
source = "dev.registry.coder.com/coder/slackme/coder"
version = "1.0.33"
agent_id = coder_agent.dev.id
auth_provider_id = "slack"
}
module "dotfiles" {
source = "dev.registry.coder.com/coder/dotfiles/coder"
version = "1.4.2"
agent_id = coder_agent.dev.id
}
module "personalize" {
source = "dev.registry.coder.com/coder/personalize/coder"
version = "1.0.33"
agent_id = coder_agent.dev.id
}
module "code-server" {
source = "dev.registry.coder.com/coder/code-server/coder"
version = "1.5.2"
agent_id = coder_agent.dev.id
folder = local.repo_dir
auto_install_extensions = true
}
module "jetbrains" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/coder/jetbrains/coder"
version = "~> 1.0"
agent_id = coder_agent.dev.id
agent_name = "dev"
folder = local.repo_dir
}
module "filebrowser" {
source = "dev.registry.coder.com/coder/filebrowser/coder"
version = "1.1.6"
agent_id = coder_agent.dev.id
}
module "coder-login" {
source = "dev.registry.coder.com/coder/coder-login/coder"
version = "1.1.1"
agent_id = coder_agent.dev.id
}
resource "coder_agent" "dev" {
arch = "amd64"
os = "linux"
dir = local.repo_dir
env = {
OIDC_TOKEN : data.coder_workspace_owner.me.oidc_access_token,
}
startup_script_behavior = "blocking"
# The following metadata blocks are optional. They are used to display
# information about your workspace in the dashboard. You can remove them
# if you don't want to display any information.
metadata {
display_name = "CPU Usage"
key = "cpu_usage"
order = 0
script = "coder stat cpu"
interval = 10
timeout = 1
}
metadata {
display_name = "RAM Usage"
key = "ram_usage"
order = 1
script = "coder stat mem"
interval = 10
timeout = 1
}
metadata {
display_name = "CPU Usage (Host)"
key = "cpu_usage_host"
order = 2
script = "coder stat cpu --host"
interval = 10
timeout = 1
}
metadata {
display_name = "RAM Usage (Host)"
key = "ram_usage_host"
order = 3
script = "coder stat mem --host"
interval = 10
timeout = 1
}
metadata {
display_name = "Swap Usage (Host)"
key = "swap_usage_host"
order = 4
script = <<EOT
#!/bin/bash
echo "$(free -b | awk '/^Swap/ { printf("%.1f/%.1f", $3/1024.0/1024.0/1024.0, $2/1024.0/1024.0/1024.0) }') GiB"
EOT
interval = 10
timeout = 1
}
metadata {
display_name = "Load Average (Host)"
key = "load_host"
order = 5
# get load avg scaled by number of cores
script = <<EOT
#!/bin/bash
echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
EOT
interval = 60
timeout = 1
}
metadata {
display_name = "Disk Usage (Host)"
key = "disk_host"
order = 6
script = "coder stat disk --path /"
interval = 600
timeout = 10
}
metadata {
display_name = "Word of the Day"
key = "word"
order = 7
script = <<EOT
#!/bin/bash
curl -o - --silent https://www.merriam-webster.com/word-of-the-day 2>&1 | awk ' $0 ~ "Word of the Day: [A-z]+" { print $5; exit }'
EOT
interval = 86400
timeout = 5
}
startup_script = <<-EOT
set -eux -o pipefail
# Allow synchronization between scripts.
trap 'touch /tmp/.coder-startup-script.done' EXIT
# BUG: Kaniko does not symlink /run => /var/run properly, resulting in
# /var/run/ owned by root:root
# WORKAROUND: symlink it manually
sudo ln -s /run /var/run
# Start Docker service
sudo service docker start
# Chown /var/run/docker.sock as even though we are a member of the Docker group
# it did not exist at the start of the workspace. This can be worked around with
# `newgrp docker` but this is annoying to have to do manually.
for attempt in $(seq 1 10); do
if sudo docker info > /dev/null; then break; fi
sleep 1
done
sudo chmod a+rw /var/run/docker.sock
# Install playwright dependencies
# We want to use the playwright version from site/package.json
# Check if the directory exists At workspace creation as the coder_script runs in parallel so clone might not exist yet.
while ! [[ -f "${local.repo_dir}/site/package.json" ]]; do
sleep 1
done
cd "${local.repo_dir}/site" && pnpm install && pnpm playwright:install
EOT
}
resource "docker_volume" "home_volume" {
name = "coder-${data.coder_workspace.me.id}-home"
# Protect the volume from being deleted due to changes in attributes.
lifecycle {
ignore_changes = all
}
# Add labels in Docker to keep track of orphan resources.
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.owner_id"
value = data.coder_workspace_owner.me.id
}
labels {
label = "coder.workspace_id"
value = data.coder_workspace.me.id
}
# This field becomes outdated if the workspace is renamed but can
# be useful for debugging or cleaning out dangling volumes.
labels {
label = "coder.workspace_name_at_creation"
value = data.coder_workspace.me.name
}
}
resource "docker_volume" "workspaces" {
name = "coder-${data.coder_workspace.me.id}"
# Protect the volume from being deleted due to changes in attributes.
lifecycle {
ignore_changes = all
}
# Add labels in Docker to keep track of orphan resources.
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.owner_id"
value = data.coder_workspace_owner.me.id
}
labels {
label = "coder.workspace_id"
value = data.coder_workspace.me.id
}
# This field becomes outdated if the workspace is renamed but can
# be useful for debugging or cleaning out dangling volumes.
labels {
label = "coder.workspace_name_at_creation"
value = data.coder_workspace.me.name
}
}
# This file is mounted as a Kubernetes secret on provisioner pods.
# It contains the required credentials for the envbuilder cache repo.
data "local_sensitive_file" "envbuilder_cache_dockerconfigjson" {
filename = var.envbuilder_cache_dockerconfigjson_path
}
data "docker_registry_image" "envbuilder" {
name = "${local.envbuilder_repo}:latest"
}
resource "docker_image" "envbuilder" {
name = "${local.envbuilder_repo}@${data.docker_registry_image.envbuilder.sha256_digest}"
pull_triggers = [data.docker_registry_image.envbuilder.sha256_digest]
keep_locally = true
}
locals {
cache_repo = "us-central1-docker.pkg.dev/coder-dogfood-v2/envbuilder-cache/coder-dogfood"
envbuilder_env = {
"CODER_AGENT_TOKEN" : coder_agent.dev.token,
"CODER_AGENT_URL" : data.coder_workspace.me.access_url,
"ENVBUILDER_GIT_USERNAME" : data.coder_external_auth.github.access_token,
# "ENVBUILDER_GIT_URL" : data.coder_parameter.devcontainer_repo.value, # The provider sets this via the `git_url` property.
"ENVBUILDER_DEVCONTAINER_DIR" : data.coder_parameter.devcontainer_dir.value,
"ENVBUILDER_INIT_SCRIPT" : coder_agent.dev.init_script,
"ENVBUILDER_FALLBACK_IMAGE" : "codercom/oss-dogfood:latest", # This image runs if builds fail
"ENVBUILDER_PUSH_IMAGE" : "true", # Push the image to the remote cache
# "ENVBUILDER_CACHE_REPO" : local.cache_repo, # The provider sets this via the `cache_repo` property.
"ENVBUILDER_DOCKER_CONFIG_BASE64" : data.local_sensitive_file.envbuilder_cache_dockerconfigjson.content_base64,
"USE_CAP_NET_ADMIN" : "true",
# Set git commit details correctly
"GIT_AUTHOR_NAME" : coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name),
"GIT_AUTHOR_EMAIL" : data.coder_workspace_owner.me.email,
"GIT_COMMITTER_NAME" : coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name),
"GIT_COMMITTER_EMAIL" : data.coder_workspace_owner.me.email,
}
}
# Check for the presence of a prebuilt image in the cache repo
# that we can use instead.
resource "envbuilder_cached_image" "cached" {
count = data.coder_workspace.me.start_count
builder_image = docker_image.envbuilder.name
git_url = data.coder_parameter.devcontainer_repo.value
cache_repo = local.cache_repo
extra_env = local.envbuilder_env
}
resource "docker_container" "workspace" {
count = data.coder_workspace.me.start_count
image = envbuilder_cached_image.cached.0.image
name = local.container_name
# Hostname makes the shell more user friendly: coder@my-workspace:~$
hostname = data.coder_workspace.me.name
# CPU limits are unnecessary since Docker will load balance automatically
memory = 32768
runtime = "sysbox-runc"
# Use environment computed from the provider
env = envbuilder_cached_image.cached.0.env
host {
host = "host.docker.internal"
ip = "host-gateway"
}
volumes {
container_path = "/home/coder/"
volume_name = docker_volume.home_volume.name
read_only = false
}
volumes {
container_path = local.repo_dir
volume_name = docker_volume.workspaces.name
read_only = false
}
capabilities {
add = ["CAP_NET_ADMIN", "CAP_SYS_NICE"]
}
# Add labels in Docker to keep track of orphan resources.
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.owner_id"
value = data.coder_workspace_owner.me.id
}
labels {
label = "coder.workspace_id"
value = data.coder_workspace.me.id
}
labels {
label = "coder.workspace_name"
value = data.coder_workspace.me.name
}
}
resource "coder_metadata" "container_info" {
count = data.coder_workspace.me.start_count
resource_id = coder_agent.dev.id
item {
key = "memory"
value = docker_container.workspace[0].memory
}
item {
key = "runtime"
value = docker_container.workspace[0].runtime
}
item {
key = "region"
value = data.coder_parameter.region.option[index(data.coder_parameter.region.option.*.value, data.coder_parameter.region.value)].name
}
}
-24
View File
@@ -1,24 +0,0 @@
# dogfood template
Ammar is this template's admin.
## Personalization
The startup script runs your `~/personalize` file if it exists.
## Hosting
Coder dogfoods on a beefy, single Teraswitch machine.
- We decided to use a bare metal provider for best-in-class cost-to-performance.
- We decided to use a single machine (vertical scaling) for fast parallelized builds and tests.
## Provisioner Configuration
Our dogfood coderd box runs an SSH tunnel to our dogfood Docker host's docker socket.
The socket is mounted onto `/var/run/dogfood-docker.sock` in the coderd box.
The SSH tunnel command can be found hanging out in the screen session named `forward`.
The tunnel and corresponding SSH key is owned by root.
-9
View File
@@ -1,9 +0,0 @@
{
"name": "Develop Coder on Coder using Envbuilder",
"build": {
"dockerfile": "Dockerfile"
},
"features": {},
"runArgs": ["--cap-add=SYS_PTRACE"]
}
-125
View File
@@ -1,125 +0,0 @@
# Dogfooding Guide
This guide explains how to
[dogfood](https://www.techopedia.com/definition/30784/dogfooding) coder for
employees at Coder.
## How to
The following explains how to do certain things related to dogfooding.
### Dogfood using Coder's Deployment
1. Go to
[https://dogfood.cdr.dev/templates/coder-ts](https://dogfood.cdr.dev/templates/coder-ts)
1. If you don't have an account, sign in with GitHub
2. If you see a dialog/pop-up, hit "Cancel" (this is because of Rippling)
2. Create a workspace
3. [Connect with your favorite IDE](https://coder.com/docs/user-guides/workspace-access)
4. Clone the repo: `git clone git@github.com:coder/coder.git`
5. Follow the [contributing guide](https://coder.com/docs/CONTRIBUTING)
### Run Coder in your Coder Workspace
1. Clone the Git repo
`[https://github.com/coder/coder](https://github.com/coder/coder)` and `cd`
into it
2. Run `sudo apt update` and then `sudo apt install -y netcat`
- skip this step if using the `coder` template
3. Run `make bin`
<aside>
💡 If you run into the following error:
```js
pg_dump: server version: 13.7 (Debian 13.7-1.pgdg110+1); pg_dump version: 11.16 (Ubuntu 11.16-1.pgdg20.04+1)
pg_dump: aborting because of server version mismatch
```
Don’t fret! This is a known issue. To get around it:
1. Add `export DB_FROM=coderdb` to your `.bashrc` (make sure you
`source ~/.bashrc`)
2. Run `sudo service postgresql start`
3. Run `sudo -u postgres psql` (this will open the PostgreSQL CLI)
4. Run `postgres-# alter user postgres password 'postgres';`
5. Run `postgres-# CREATE DATABASE coderdb;`
6. Run `postgres-# grant all privileges on database coderdb to postgres;`
7. Run `exit` to exit the PostgreSQL terminal
8. Try `make bin` again.
</aside>
4. Run `./scripts/develop.sh` which will start _two_ separate processes:
1. `[http://localhost:3000](http://localhost:3000)` — backend API server
👈 Backend devs will want to talk to this
2. `[http://localhost:8080](http://localhost:8080)` — Node.js dev server
👈 Frontend devs will want to talk to this
5. Ensure that you’re logged in: `./scripts/coder-dev.sh list` — should return
no workspace. If this returns an error, double-check the output of running
`scripts/develop.sh`.
6. A template named `docker` will have automatically been created for you. If you just
want to create a workspace quickly, you can run `./scripts/coder-dev.sh create myworkspace -t docker`
and this will get you going quickly!
7. To create your own template, you can do:
`./scripts/coder-dev.sh templates init` and choose your preferred option.
For example, choosing “Develop in Docker” will create a new folder `docker`
that contains the bare bones for starting a Docker workspace template. Then,
enter the folder that was just created and customize as you wish.
<aside>
💡 **For all Docker templates:**
This step depends on whether you are developing on a Coder v1 workspace, versus a Coder v2 workspace, versus a VM, versus locally. In any case, check the output of the command `docker context ls` to determine where your Docker daemon is listening. Then open `./docker/main.tf` and check inside the block `provider "docker"` that the variable `"host"` is set correctly.
</aside>
## Troubleshooting
### My Docker containers keep failing and I have no idea what's going on
```console
✔ Queued [236ms]
✔ Setting up [5ms]
⧗ Starting workspace
Terraform 1.1.9
coder_agent.dev: Plan to create
docker_volume.home_volume: Plan to create
docker_container.workspace[0]: Plan to create
Plan: 3 to add, 0 to change, 0 to destroy.
coder_agent.dev: Creating...
coder_agent.dev: Creation complete after 0s [id=b2f132bd-9af1-48a7-81dc-187a18ee00d5]
docker_volume.home_volume: Creating...
docker_volume.home_volume: Creation complete after 0s [id=coder-maf-mywork-root]
docker_container.workspace[0]: Creating...
docker_container.workspace[0]: Creation errored after 0s
Error: container exited immediately
✘ Starting workspace [2045ms]
terraform apply: exit status 1
Run 'coder create --help' for usage.
```
Check the output of `docker ps -a`
- If you see a container with the status `Exited` run
`docker logs <container name>` and see what the issue with the container
output is
Enable verbose container logging for Docker:
```shell
sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.orig
sudo cat > /etc/docker/daemon.json << EOF
{
"debug": true,
"log-driver": "journald"
}
EOF
sudo systemctl restart docker
# You should now see container logs in journald.
# Try starting a workspace again and see what the actual error is!
sudo journalctl -u docker -f
```
### Help! I'm still blocked
Post in the #dogfood Slack channel internally or open a Discussion on GitHub and
tag @jsjoeio or @bpmct
File diff suppressed because it is too large Load Diff
-35
View File
@@ -1,35 +0,0 @@
# vscode-coder template
This template is for developing the
[coder/vscode-coder](https://github.com/coder/vscode-coder) VS Code extension.
## Personalization
The template includes a `personalize` module that runs your `~/personalize`
file if it exists.
## Testing
The workspace comes with Playwright Chromium, GTK libraries, xauth, and a
D-Bus daemon pre-configured for running tests headlessly, the same way CI
does.
Integration tests launch a real VS Code instance and require a virtual
framebuffer. Run them with `xvfb-run -a pnpm test:integration` to match
CI behavior.
See the repo's
[AGENTS.md](https://github.com/coder/vscode-coder/blob/main/AGENTS.md)
for the full list of commands.
## Hosting
Coder dogfoods on a single Teraswitch bare metal machine for best-in-class
cost-to-performance. Workspaces run as Docker containers with regional
Tailscale endpoints for Pittsburgh, Falkenstein, Sydney, and Cape Town.
## Provisioner Configuration
The dogfood coderd box runs an SSH tunnel to the Docker host's socket,
mounted at `/var/run/dogfood-docker.sock`. The tunnel runs in a screen
session named `forward` and is owned by root.
-574
View File
@@ -1,574 +0,0 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
version = ">= 2.13.0"
}
docker = {
source = "kreuzwerker/docker"
version = "~> 4.0"
}
}
}
locals {
// These are cluster service addresses mapped to Tailscale nodes.
// Ask Dean or Kyle for help.
docker_host = {
"" = "tcp://rubinsky-pit-cdr-dev.tailscale.svc.cluster.local:2375"
"us-pittsburgh" = "tcp://rubinsky-pit-cdr-dev.tailscale.svc.cluster.local:2375"
// For legacy reasons, this host is labelled `eu-helsinki` but it's
// actually in Germany now.
"eu-helsinki" = "tcp://katerose-fsn-cdr-dev.tailscale.svc.cluster.local:2375"
"ap-sydney" = "tcp://wolfgang-syd-cdr-dev.tailscale.svc.cluster.local:2375"
"za-cpt" = "tcp://schonkopf-cpt-cdr-dev.tailscale.svc.cluster.local:2375"
}
repo_base_dir = data.coder_parameter.repo_base_dir.value == "~" ? "/home/coder" : replace(data.coder_parameter.repo_base_dir.value, "/^~\\//", "/home/coder/")
repo_dir = replace(try(module.git-clone[0].repo_dir, ""), "/^~\\//", "/home/coder/")
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
}
# --- Parameters ---
data "coder_parameter" "repo_base_dir" {
type = "string"
name = "Repository Base Directory"
default = "~"
description = "The directory specified will be created (if missing) and [coder/vscode-coder](https://github.com/coder/vscode-coder) will be automatically cloned into [base directory]/vscode-coder."
mutable = true
}
locals {
default_regions = {
"north-america" : "us-pittsburgh"
"europe" : "eu-helsinki"
"australia" : "ap-sydney"
"africa" : "za-cpt"
}
user_groups = data.coder_workspace_owner.me.groups
user_region = coalescelist([
for g in local.user_groups :
local.default_regions[g] if contains(keys(local.default_regions), g)
], ["us-pittsburgh"])[0]
}
data "coder_parameter" "region" {
type = "string"
name = "Region"
icon = "/emojis/1f30e.png"
default = local.user_region
option {
icon = "/emojis/1f1fa-1f1f8.png"
name = "Pittsburgh"
value = "us-pittsburgh"
}
option {
icon = "/emojis/1f1e9-1f1ea.png"
name = "Falkenstein"
// For legacy reasons, this host is labelled `eu-helsinki` but it's
// actually in Germany now.
value = "eu-helsinki"
}
option {
icon = "/emojis/1f1e6-1f1fa.png"
name = "Sydney"
value = "ap-sydney"
}
option {
icon = "/emojis/1f1ff-1f1e6.png"
name = "Cape Town"
value = "za-cpt"
}
}
data "coder_parameter" "res_mon_memory_threshold" {
type = "number"
name = "Memory usage threshold"
default = 80
description = "The memory usage threshold used in resources monitoring to trigger notifications."
mutable = true
validation {
min = 0
max = 100
}
}
data "coder_parameter" "res_mon_volume_threshold" {
type = "number"
name = "Volume usage threshold"
default = 90
description = "The volume usage threshold used in resources monitoring to trigger notifications."
mutable = true
validation {
min = 0
max = 100
}
}
data "coder_parameter" "res_mon_volume_path" {
type = "string"
name = "Volume path"
default = "/home/coder"
description = "The path monitored in resources monitoring to trigger notifications."
mutable = true
}
data "coder_parameter" "use_ai_gateway" {
type = "bool"
name = "Use AI Gateway"
default = true
description = "If enabled, AI requests will be sent via AI Gateway."
mutable = true
}
# Fallback when AI Gateway is disabled. Injected by the pipeline that
# pushes this template.
variable "anthropic_api_key" {
type = string
description = "Anthropic API key, used when AI Gateway is disabled."
default = ""
sensitive = true
}
data "coder_parameter" "ide_choices" {
type = "list(string)"
name = "Select IDEs"
form_type = "multi-select"
mutable = true
description = "Choose one or more IDEs to enable in your workspace"
default = jsonencode(["vscode", "code-server", "cursor"])
option {
name = "VS Code Desktop"
value = "vscode"
icon = "/icon/code.svg"
}
option {
name = "code-server"
value = "code-server"
icon = "/icon/code.svg"
}
option {
name = "VS Code Web"
value = "vscode-web"
icon = "/icon/code.svg"
}
option {
name = "Cursor"
value = "cursor"
icon = "/icon/cursor.svg"
}
option {
name = "Windsurf"
value = "windsurf"
icon = "/icon/windsurf.svg"
}
option {
name = "Zed"
value = "zed"
icon = "/icon/zed.svg"
}
}
data "coder_parameter" "vscode_channel" {
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode") ? 1 : 0
type = "string"
name = "VS Code Desktop channel"
description = "Choose the VS Code Desktop channel"
mutable = true
default = "stable"
option {
value = "stable"
name = "Stable"
icon = "/icon/code.svg"
}
option {
value = "insiders"
name = "Insiders"
icon = "/icon/code-insiders.svg"
}
}
# --- Providers and data sources ---
provider "docker" {
host = lookup(local.docker_host, data.coder_parameter.region.value)
}
provider "coder" {}
data "coder_external_auth" "github" {
id = "github"
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
data "coder_workspace_tags" "tags" {
tags = {
"cluster" : "dogfood-v2"
"env" : "gke"
}
}
# --- Modules ---
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/coder/dotfiles/coder"
version = "1.4.2"
agent_id = coder_agent.dev.id
}
module "git-config" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/coder/git-config/coder"
version = "1.0.34"
agent_id = coder_agent.dev.id
allow_email_change = true
}
module "git-clone" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/coder/git-clone/coder"
version = "1.3.0"
agent_id = coder_agent.dev.id
url = "https://github.com/coder/vscode-coder"
base_dir = local.repo_base_dir
post_clone_script = <<-EOT
#!/usr/bin/env bash
set -eux -o pipefail
coder exp sync start git-clone
coder exp sync complete git-clone
EOT
}
module "personalize" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/coder/personalize/coder"
version = "1.0.33"
agent_id = coder_agent.dev.id
}
module "code-server" {
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "code-server") ? data.coder_workspace.me.start_count : 0
source = "dev.registry.coder.com/coder/code-server/coder"
version = "1.5.2"
agent_id = coder_agent.dev.id
folder = local.repo_dir
auto_install_extensions = true
group = "Web Editors"
}
module "vscode-web" {
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode-web") ? data.coder_workspace.me.start_count : 0
source = "dev.registry.coder.com/coder/vscode-web/coder"
version = "1.6.2"
agent_id = coder_agent.dev.id
folder = local.repo_dir
extensions = ["github.copilot"]
auto_install_extensions = true
accept_license = true
group = "Web Editors"
}
module "copyparty" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/djarbz/copyparty/coder"
version = "1.0.2"
agent_id = coder_agent.dev.id
subdomain = true
pinned_version = "v1.20.23"
arguments = [
# copyparty listens on all interfaces by default; the agent proxies localhost.
"-i", "127.0.0.1",
# Serve the home directory at the web root with all permissions.
"-v", "/home/coder:/:A",
]
}
module "coder-login" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/coder/coder-login/coder"
version = "1.1.1"
agent_id = coder_agent.dev.id
}
module "cursor" {
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "cursor") ? data.coder_workspace.me.start_count : 0
source = "dev.registry.coder.com/coder/cursor/coder"
version = "1.4.1"
agent_id = coder_agent.dev.id
folder = local.repo_dir
}
module "windsurf" {
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "windsurf") ? data.coder_workspace.me.start_count : 0
source = "dev.registry.coder.com/coder/windsurf/coder"
version = "1.3.2"
agent_id = coder_agent.dev.id
folder = local.repo_dir
}
module "zed" {
count = contains(jsondecode(data.coder_parameter.ide_choices.value), "zed") ? data.coder_workspace.me.start_count : 0
source = "dev.registry.coder.com/coder/zed/coder"
version = "1.1.5"
agent_id = coder_agent.dev.id
agent_name = "dev"
folder = local.repo_dir
}
# --- Agent ---
resource "coder_agent" "dev" {
arch = "amd64"
os = "linux"
dir = local.repo_dir
env = merge(
{
OIDC_TOKEN : data.coder_workspace_owner.me.oidc_access_token,
},
data.coder_parameter.use_ai_gateway.value ? {
ANTHROPIC_BASE_URL : "https://dogfood.cdr.dev/api/v2/ai-gateway/anthropic",
ANTHROPIC_AUTH_TOKEN : data.coder_workspace_owner.me.session_token,
OPENAI_BASE_URL : "https://dogfood.cdr.dev/api/v2/ai-gateway/openai/v1",
OPENAI_API_KEY : data.coder_workspace_owner.me.session_token,
} : {}
)
startup_script_behavior = "blocking"
display_apps {
vscode = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode") && try(data.coder_parameter.vscode_channel[0].value, "stable") == "stable"
vscode_insiders = contains(jsondecode(data.coder_parameter.ide_choices.value), "vscode") && try(data.coder_parameter.vscode_channel[0].value, "stable") == "insiders"
}
metadata {
display_name = "CPU Usage"
key = "cpu_usage"
order = 0
script = "coder stat cpu"
interval = 10
timeout = 1
}
metadata {
display_name = "RAM Usage"
key = "ram_usage"
order = 1
script = "coder stat mem"
interval = 10
timeout = 1
}
metadata {
display_name = "/home Usage"
key = "home_usage"
order = 2
script = "sudo du -sh /home/coder | awk '{print $1}'"
interval = 3600
timeout = 60
}
metadata {
display_name = "Word of the Day"
key = "word"
order = 3
script = <<EOT
#!/usr/bin/env bash
curl -o - --silent https://www.merriam-webster.com/word-of-the-day 2>&1 | awk ' $0 ~ "Word of the Day: [A-z]+" { print $5; exit }'
EOT
interval = 86400
timeout = 5
}
resources_monitoring {
memory {
enabled = true
threshold = data.coder_parameter.res_mon_memory_threshold.value
}
volume {
enabled = true
threshold = data.coder_parameter.res_mon_volume_threshold.value
path = data.coder_parameter.res_mon_volume_path.value
}
}
startup_script = <<-EOT
#!/usr/bin/env bash
set -eux -o pipefail
function cleanup() {
coder exp sync complete agent-startup
touch /tmp/.coder-startup-script.done
}
trap cleanup EXIT
coder exp sync start agent-startup
# Start dbus to suppress noisy Electron/Chromium errors in tests.
sudo mkdir -p /run/dbus
sudo dbus-daemon --system 2>/dev/null || true
if ! gh api user --jq .login >/dev/null 2>&1; then
echo "Logging into GitHub CLI..."
if ! coder external-auth access-token github | gh auth login --hostname github.com --with-token; then
echo "GitHub CLI authentication failed; gh commands may not work."
fi
else
echo "GitHub CLI already has working credentials."
fi
EOT
}
# --- Scripts ---
resource "coder_script" "install-deps" {
agent_id = coder_agent.dev.id
display_name = "Installing Dependencies"
run_on_start = true
start_blocks_login = false
script = <<EOT
#!/usr/bin/env bash
set -euo pipefail
trap 'coder exp sync complete install-deps' EXIT
coder exp sync want install-deps git-clone
coder exp sync start install-deps
cd "${local.repo_dir}" && pnpm install
# Playwright maintains the list of system libs each Chromium version
# needs, so we don't have to track them in the Dockerfile.
# Electron additionally requires libgtk-3-0 which Playwright skips
# because its bundled Chromium doesn't use GTK. xauth is needed
# by xvfb-run for integration tests.
sudo npx --yes playwright install-deps chromium
sudo apt-get install -y --no-install-recommends libgtk-3-0 xauth
EOT
}
# --- Docker resources ---
resource "coder_metadata" "home_volume" {
resource_id = docker_volume.home_volume.id
daily_cost = 1
}
resource "docker_volume" "home_volume" {
name = "coder-${data.coder_workspace.me.id}-home"
lifecycle {
ignore_changes = all
}
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.owner_id"
value = data.coder_workspace_owner.me.id
}
labels {
label = "coder.workspace_id"
value = data.coder_workspace.me.id
}
labels {
label = "coder.workspace_name_at_creation"
value = data.coder_workspace.me.name
}
}
data "docker_registry_image" "vscode_coder" {
name = "codercom/oss-dogfood-vscode-coder:latest"
}
resource "docker_image" "vscode_coder" {
name = "codercom/oss-dogfood-vscode-coder:latest@${data.docker_registry_image.vscode_coder.sha256_digest}"
pull_triggers = [
data.docker_registry_image.vscode_coder.sha256_digest,
filesha1("Dockerfile"),
]
keep_locally = true
}
resource "docker_container" "workspace" {
lifecycle {
ignore_changes = [name, hostname, labels, env, entrypoint]
}
count = data.coder_workspace.me.start_count
image = docker_image.vscode_coder.name
name = local.container_name
hostname = data.coder_workspace.me.name
entrypoint = ["sh", "-c", coder_agent.dev.init_script]
memory = 8192
# Allow the agent to finish cleanup traps on shutdown.
destroy_grace_seconds = 60
stop_timeout = 60
stop_signal = "SIGINT"
env = [
"CODER_AGENT_TOKEN=${coder_agent.dev.token}",
]
host {
host = "host.docker.internal"
ip = "host-gateway"
}
volumes {
container_path = "/home/coder/"
volume_name = docker_volume.home_volume.name
read_only = false
}
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.owner_id"
value = data.coder_workspace_owner.me.id
}
labels {
label = "coder.workspace_id"
value = data.coder_workspace.me.id
}
labels {
label = "coder.workspace_name"
value = data.coder_workspace.me.name
}
}
resource "coder_metadata" "container_info" {
count = data.coder_workspace.me.start_count
resource_id = docker_container.workspace[0].id
item {
key = "memory"
value = docker_container.workspace[0].memory
}
item {
key = "region"
value = data.coder_parameter.region.option[index(data.coder_parameter.region.option.*.value, data.coder_parameter.region.value)].name
}
}
module "claude-code" {
count = data.coder_workspace.me.start_count
source = "dev.registry.coder.com/coder/claude-code/coder"
version = "5.4.1"
enable_ai_gateway = data.coder_parameter.use_ai_gateway.value
anthropic_api_key = data.coder_parameter.use_ai_gateway.value ? "" : var.anthropic_api_key
agent_id = coder_agent.dev.id
workdir = local.repo_dir
}
resource "coder_app" "claude" {
agent_id = coder_agent.dev.id
slug = "claude"
display_name = "Claude Code"
icon = "/icon/claude.svg"
open_in = "slim-window"
command = <<-EOT
#!/bin/bash
set -e
cd "${local.repo_dir}"
exec tmux new-session -A -s claude claude
EOT
}
+1 -1
View File
@@ -52,7 +52,7 @@ fi
# Helper: run a make target inside the image.
#
# Mounts /home/coder/ as a single named volume to mirror the dogfood
# workspace template (dogfood/coder/main.tf), so caches (Go modules,
# workspace template, so caches (Go modules,
# Go build, pnpm store, mise data, etc.) persist the same way they do
# in real workspaces. Per-cache subpath volumes would come up
# root-owned on first mount because Docker creates non-existent