Thomas Kosiewski f605d6bcb4 feat(dogfood/coder): add brew and mise to ubuntu images (#24618)
This adds Homebrew and mise to the Ubuntu dogfood images and makes mise
shims win
PATH resolution for the `coder` user. It installs Homebrew in
`/home/linuxbrew/.linuxbrew`, installs the latest mise release
(`v2026.4.19`) via
its verified GitHub release artifact, exposes mise at
`/usr/local/bin/mise`, wires
`HOMEBREW_*` and `MISE_DATA_DIR`, and adds build-time checks for both
tools. The
mise executable target lives in writable `/opt/mise/bin` so `mise
self-update`
can replace it as the `coder` user. This also adds `libc6-dev` to the Go
utility
stages so the existing CGO-backed tool installs keep building on newer
Ubuntu
bases.

The dogfood template now mounts a dedicated `/home/linuxbrew/` Docker
volume in
addition to `/home/coder/`. Fresh volumes are seeded from the
image-baked
Homebrew tree on first mount, while user-installed formulae persist
across
workspace container recreation.

I revalidated the bootstrap on jammy and resolute base images with fresh
mounted
`/home/coder` and `/home/linuxbrew` volumes. In those runs, `brew
install hello`
succeeded, `mise doctor` reported no PATH or activation problems, `mise
self-update --force --yes --no-plugins 2026.4.19` succeeded as `coder`,
and
`mise use --global github:BurntSushi/ripgrep@14.1.1` moved `rg`
resolution to
the mise shim after container recreation.

---

<details>
<summary>📋 Implementation Plan</summary>

# Plan: add `mise` and Homebrew to the dogfood Ubuntu images with
mise-first PATH

## Goal

- Make both dogfood Ubuntu images ship `brew` and `mise`.
- Ensure `mise doctor` does **not** complain about activation/PATH
ordering in the shell entrypoints we support.
- Keep the implementation robust against the persistent `/home/coder`
volume used by the dogfood template.

## Verified context

- The relevant image definitions are:
  - `dogfood/coder/ubuntu-22.04/Dockerfile`
  - `dogfood/coder/ubuntu-26.04/Dockerfile`
- The dogfood template mounts a persistent home volume at `/home/coder/`
in
`dogfood/coder/main.tf:840-843`, so required image-baked state should
not
  live only under `/home/coder`.
- Both Dockerfiles already manipulate PATH in multiple places:
  - Go appended early (`:26`)
- Cargo prepended (`ubuntu-26.04/Dockerfile:202-206`; mirrored in 22.04)
- Node via nvm prepended (`ubuntu-26.04/Dockerfile:245-255`; mirrored in
    22.04)
  - Final `coder` PATH prepends `/home/coder/go/bin`
    (`ubuntu-26.04/Dockerfile:348-358`; mirrored in 22.04)
- `COPY files /` is already present in both Dockerfiles, so adding new
global
  shell-init files is possible without Terraform changes.
- `scripts/lib.sh:94-124` uses `command -v` for dependency detection, so
PATH
  order is the practical repo-level behavior we need to control.
- `.github/workflows/dogfood.yaml:99-126` builds both Ubuntu variants,
and the
22.04 image is still tagged `latest`, so both Dockerfiles must be
updated in
  the same change.

## Recommended implementation

### Phase 1 — Bootstrap Homebrew and `mise` in both Ubuntu Dockerfiles

1. Update both Dockerfiles in parallel:
   - `dogfood/coder/ubuntu-22.04/Dockerfile`
   - `dogfood/coder/ubuntu-26.04/Dockerfile`
2. Add the minimum explicit Homebrew prerequisites that are missing from
the
   current apt package set.
- The images already install `build-essential`, `curl`, `file`, and
`git`.
- Audit whether `procps` must be added explicitly for Homebrew’s Linux
     requirements.
3. Install Homebrew in the supported Linux prefix:
   - Prefix: `/home/linuxbrew/.linuxbrew`
- Keep the install/build logic in the Dockerfile, before `USER coder`.
- Make the resulting prefix writable by `coder` before switching users.
Prefer the smallest-diff approach that leaves `brew install ...` usable
as
     `coder`.
4. Install `mise` to a stable image-owned path instead of relying on
   `~/.local/bin`:
   - Preferred binary path: `/usr/local/bin/mise`
- Use a pinned installation method that fits the current Dockerfile
style
(versioned release asset or otherwise explicitly pinned installer path).
5. Add defensive build-time sanity checks near the install steps so the
image
   fails early if assumptions are wrong:
   - `test -x /usr/local/bin/mise`
   - `test -x /home/linuxbrew/.linuxbrew/bin/brew`
   - `brew --version`
   - `mise --version`

**Quality gate:** both Dockerfiles build locally, and the resulting
container can
run `brew --version` and `mise --version` as `coder`.

### Phase 2 — Make `mise` win PATH resolution by default

1. After `USER coder` in both Dockerfiles, define stable environment
variables
   for the final shell/runtime behavior:
   - `HOMEBREW_PREFIX=/home/linuxbrew/.linuxbrew`
   - `MISE_DATA_DIR=/home/coder/.local/share/mise`
   - `MISE_ACTIVATE_AGGRESSIVE=1` only if later shell activation proves
     necessary
2. Replace the final PATH composition so it resolves in this order:
   1. `mise` shims
   2. Homebrew `bin`/`sbin`
   3. Existing `/home/coder/go/bin`
   4. Existing image/system PATH
3. Keep the current Go/Rust/Node setup intact aside from the final PATH
ordering. Add a short Dockerfile comment explaining that `mise` shims
must be
first so `mise doctor` and `command -v` resolve `mise`-managed tools
ahead of
   Homebrew/system binaries.
4. Do **not** rely on image-baked `mise` state under `/home/coder` for
the
   initial implementation. The goal here is binary availability and path
   precedence, not preinstalling shared `mise` toolchains.

**Quality gate:** in a fresh container as `coder`, `echo "$PATH"` shows
`mise` shims before Homebrew, and `mise doctor`/`mise doctor path` show
no PATH
or activation problem in the tested shell entrypoints.

### Phase 3 — Add shell-init hardening only if smoke tests prove it is
needed

1. Start with the Dockerfile `ENV PATH` solution as the default
behavior.
2. If dogfooding shows that supported login shells still need shell
integration
beyond the final `ENV PATH`, add minimal global shell-init files under:
   - `dogfood/coder/ubuntu-22.04/files/etc/profile.d/`
   - `dogfood/coder/ubuntu-26.04/files/etc/profile.d/`
3. If these files are needed, keep them narrowly scoped:
   - a Homebrew file that exports/evals `brew shellenv`
- a `mise` file that only reinforces the intended shims-first behavior
4. Avoid touching per-user dotfiles in `/home/coder`; they are the wrong
place
   for required image behavior because of the persistent home volume.

**Quality gate:** if profile.d files are added, login-shell smoke tests
pass and
we do not introduce new PATH-order regressions versus the
Dockerfile-only path.

## Acceptance criteria

- Both Ubuntu dogfood Dockerfiles are updated in one change and still
build.
- `brew` is installed in `/home/linuxbrew/.linuxbrew` and is usable as
`coder`.
- `mise` is installed at `/usr/local/bin/mise` and is usable as `coder`.
- `mise doctor` does not report an activation/PATH-ordering problem in
the
  shell entrypoints we verify.
- Final PATH precedence is:
  1. `mise` shims
  2. Homebrew `bin`/`sbin`
  3. existing user/tool paths
  4. system paths
- Existing dogfood workflows still work for Go/Rust/Node tooling after
the PATH
  change.
- The change passes the dogfood image CI path in
`.github/workflows/dogfood.yaml`.

## Dogfooding and verification

1. Build both images locally:
   - `dogfood/coder/ubuntu-22.04`
   - `dogfood/coder/ubuntu-26.04`
2. Run each image with an empty mounted home volume at `/home/coder` to
mimic
the actual dogfood runtime constraint instead of only testing the
image’s
   baked filesystem.
3. Capture a short terminal recording and screenshots for each variant
showing:
   - `brew --prefix`
   - `brew --version`
   - `mise --version`
   - `echo "$PATH"`
   - `mise doctor`
   - `mise doctor path`
4. Verify at least one login-shell path and one non-login-shell path, so
we can
tell whether Dockerfile `ENV PATH` is sufficient or whether
`/etc/profile.d`
   hardening is required.
5. Add one tool-resolution smoke test that proves `mise` wins when
configured:
   - install/use a small `mise`-managed runtime as `coder`
   - run `which -a <tool>`
   - run `<tool> --version`
6. Verify existing image behavior did not regress:
   - `go version`
   - `node --version`
   - any other must-have image tools that were already on PATH
7. Preserve the artifacts from dogfooding for review:
   - screenshots attached to the change summary
- a short screen recording (or terminal recording) covering the smoke
test

## Risks and decision points

- **Homebrew ownership model:** installing Homebrew during `docker
build` is not
  enough by itself; the prefix must end up writable for `coder`.
- **Scope control:** the initial change should solve `mise doctor` by
fixing PATH
  precedence, not by introducing a larger `mise`-managed tool bootstrap.
- **Shell-init uncertainty:** if the dogfood terminal entrypoints do not
source
`/etc/profile`, a Dockerfile `ENV PATH` fix may be sufficient and
profile.d
may be unnecessary. This should be decided by smoke tests, not by
assumption.
- **Persistent home behavior:** avoid any required implementation detail
that
  only works if fresh volumes copy image-baked `/home/coder` contents.

<details>
<summary>Why this is the lowest-risk path</summary>

This plan keeps the initial implementation focused on the user’s stated
goal:
install Homebrew and `mise`, then guarantee that `mise`-controlled paths
win so
`mise doctor` stays quiet.

The main repo-specific constraint is the persistent `/home/coder`
volume. That
pushes required binaries and ownership-sensitive state out of
`/home/coder`
where possible, and it argues against relying on user dotfiles for
required
image behavior.

Starting with Dockerfile-level install steps plus a final PATH reorder
keeps the
diff small, makes behavior consistent across shells, and gives us a
clean place
to add shell-init hardening only if the smoke tests prove it is
necessary.
</details>

</details>

---
_Generated with [`mux`](https://github.com/coder/mux) • Model:
`openai:gpt-5.5` • Thinking: `xhigh`_
2026-05-07 09:24:08 +02:00
2022-04-04 11:55:06 -05:00

Coder Logo Light Coder Logo Dark

Self-Hosted Cloud Development Environments and AI Agents

Coder Banner Light Coder Banner Dark

Quickstart | Docs | Why Coder | Premium

discord release godoc Go Report Card OpenSSF Best Practices OpenSSF Scorecard license

Coder is a self-hosted platform for cloud development environments and AI coding agents. Workspaces are defined with Terraform, connected through a secure Wireguard® tunnel, and automatically shut down when not used. Coder Agents runs a native AI coding agent whose loop executes in the control plane on your infrastructure, with no API keys in workspaces.

  • Define cloud development environments in Terraform
    • EC2 VMs, Kubernetes Pods, Docker Containers, etc.
  • Automatically shutdown idle resources to save on costs
  • Onboard developers in seconds instead of days
  • Delegate coding work to AI agents on your infrastructure
    • Bring any model (Anthropic, OpenAI, Google, Bedrock, self-hosted)
    • No LLM credentials in workspaces, user identity on every action
    • Centralized model governance, cost tracking, and audit logging

Coder platform showing templates and a running workspace

Quickstart

The most convenient way to try Coder is to install it on your local machine and experiment with provisioning cloud development environments using Docker (works on Linux, macOS, and Windows).

# First, install Coder
curl -L https://coder.com/install.sh | sh

# Start the Coder server (caches data in ~/.cache/coder)
coder server

# Navigate to http://localhost:3000 to create your initial user,
# create a Docker template and provision a workspace

Install

The easiest way to install Coder is to use the install script for Linux and macOS. For Windows, use the latest ..._installer.exe file from GitHub Releases.

curl -L https://coder.com/install.sh | sh

You can run the install script with --dry-run to see the commands that will be used to install without executing them. Run the install script with --help for additional flags.

See install for additional methods.

Once installed, you can start a production deployment with a single command:

# Automatically sets up an external access URL on *.try.coder.app
coder server

# Requires a PostgreSQL instance (version 13 or higher) and external access URL
coder server --postgres-url <url> --access-url <url>

Use coder --help to get a list of flags and environment variables. See the install guides for a complete tutorial.

Documentation

Browse the documentation or visit a specific section below:

  • Workspaces: Workspaces contain the IDEs, dependencies, and configuration information needed for software development
  • Templates: Templates are written in Terraform and describe the infrastructure for workspaces
  • Coder Agents: Delegate coding work to AI agents running on your self-hosted infrastructure
  • Administration: Learn how to operate Coder
  • Premium: Learn about paid features built for large teams
  • IDEs: Connect your existing editor to a workspace

Support

Feel free to open an issue if you have questions, run into bugs, or have a feature request.

Join our Discord to provide feedback on in-progress features and chat with the community using Coder!

Integrations

New integrations are always in progress. Open an issue to request one. Contributions are welcome in any official or community repository.

Official

Community

Contributing

New contributors are always welcome. If you are new to the Coder codebase, see the contribution guide to get started.

Hiring

Apply on the careers page if you are interested in joining the team.

S
Description
Provision remote development environments via Terraform
Readme AGPL-3.0
919 MiB
Languages
Go 74.9%
TypeScript 23.1%
Shell 0.8%
HCL 0.3%
PLpgSQL 0.3%
Other 0.4%