Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/testdata/coder_templates_init_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ USAGE:
Get started with a templated template.

OPTIONS:
--id aws-devcontainer|aws-linux|aws-windows|azure-linux|digitalocean-linux|docker|docker-devcontainer|docker-envbuilder|gcp-devcontainer|gcp-linux|gcp-vm-container|gcp-windows|kubernetes|kubernetes-devcontainer|nomad-docker|scratch|tasks-docker
--id aws-devcontainer|aws-linux|aws-windows|azure-linux|digitalocean-linux|docker|docker-devcontainer|docker-envbuilder|gcp-devcontainer|gcp-linux|gcp-vm-container|gcp-windows|incus|kubernetes|kubernetes-devcontainer|nomad-docker|scratch|tasks-docker
Specify a given example template by ID.

———
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/cli/templates_init.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions examples/examples.gen.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,19 @@
],
"markdown": "\n# Remote Development on Google Compute Engine (Windows)\n\n## Prerequisites\n\n### Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith Google Cloud. For example, run `gcloud auth application-default login` to\nimport credentials on the system and user running coderd. For other ways to\nauthenticate [consult the Terraform\ndocs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started#adding-credentials).\n\nCoder requires a Google Cloud Service Account to provision workspaces. To create\na service account:\n\n1. Navigate to the [CGP\n console](https://console.cloud.google.com/projectselector/iam-admin/serviceaccounts/create),\n and select your Cloud project (if you have more than one project associated\n with your account)\n\n1. Provide a service account name (this name is used to generate the service\n account ID)\n\n1. Click **Create and continue**, and choose the following IAM roles to grant to\n the service account:\n\n - Compute Admin\n - Service Account User\n\n Click **Continue**.\n\n1. Click on the created key, and navigate to the **Keys** tab.\n\n1. Click **Add key** \u003e **Create new key**.\n\n1. Generate a **JSON private key**, which will be what you provide to Coder\n during the setup process.\n\n## Architecture\n\nThis template provisions the following resources:\n\n- GCP VM (ephemeral)\n- GCP Disk (persistent, mounted to root)\n\nCoder persists the root volume. The full filesystem is preserved when the workspace restarts. See this [community example](https://github.com/bpmct/coder-templates/tree/main/aws-linux-ephemeral) of an ephemeral AWS instance.\n\n\u003e **Note**\n\u003e This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case.\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n"
},
{
"id": "incus",
"url": "",
"name": "Incus System Container with Docker",
"description": "Develop in an Incus System Container with Docker using Incus",
"icon": "/icon/lxc.svg",
"tags": [
"incus",
"lxc",
"lxd"
],
"markdown": "\n# Incus System Container with Docker\n\nDevelop in an Incus System Container and run nested Docker containers using Incus.\n\n## Architecture\n\nThis template uses the [Incus guest API](https://linuxcontainers.org/incus/docs/main/dev-incus/) (`/dev/incus/sock`) to deliver the Coder agent token and URL into the container without any host filesystem coupling. This means:\n\n- **The provisioner does not need to run on the Incus host.** There are no bind mounts or local file writes. All configuration is passed via Incus `user.*` config keys and read from inside the container at runtime.\n- **The agent binary is downloaded automatically.** The standard Coder init script fetches the correct binary from the Coder server on every boot, keeping it in sync with the server version.\n- **The agent token is refreshed on every start.** Terraform updates the `user.coder_agent_token` config key each workspace start. A watcher service inside the container listens for config changes via the guest API events endpoint and restarts the agent when a new token arrives.\n\n### Boot sequence\n\n1. **First boot (cloud-init):** Creates the workspace user, writes the bootstrap scripts and systemd units, installs `curl` and `git`, and enables the services. Cloud-init only runs once.\n2. **Every boot (systemd):**\n - `coder-agent-config.service` (oneshot) reads `CODER_AGENT_TOKEN` and `CODER_AGENT_URL` from the Incus guest API and writes them to `/opt/coder/init.env`.\n - `coder-agent.service` loads the env file and runs the Coder init script, which downloads the agent binary and starts it.\n - `coder-agent-watcher.service` streams config change events from the guest API. If the Incus provider updates the token *after* the container has already booted (a known provider ordering issue), the watcher detects the change, re-fetches the config, and restarts the agent.\n\n### Packages\n\nEssential packages (`curl`, `git`) are installed via cloud-init on first boot, before the agent starts. Additional packages (e.g. `docker.io`) are installed via a non-blocking [`coder_script`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script) that runs on each workspace start. It does not block login; users can connect to the workspace immediately while packages install in the background. On subsequent starts, it detects packages are already installed and skips the installation.\n\n## Prerequisites\n\n1. Install [Incus](https://linuxcontainers.org/incus/) on a machine reachable by the Coder provisioner.\n2. Allow Coder to access the Incus socket.\n\n - If you're running Coder as a system service, run `sudo usermod -aG incus-admin coder` and restart the Coder service.\n - If you're running Coder as a Docker Compose service, get the group ID of the `incus-admin` group by running `getent group incus-admin` and add the following to your `compose.yaml` file:\n\n ```yaml\n services:\n coder:\n volumes:\n - /var/lib/incus/unix.socket:/var/lib/incus/unix.socket\n group_add:\n - 996 # Replace with the group ID of the `incus-admin` group\n ```\n\n3. Create a storage pool named `coder` by running `incus storage create coder btrfs` (or use another [supported driver](https://linuxcontainers.org/incus/docs/main/reference/storage_drivers/)).\n\n## Usage\n\n\u003e **Note:** This template requires a container image with cloud-init installed, such as `images:debian/13/cloud` or `images:ubuntu/24.04/cloud`. Images are pulled automatically from the [Linux Containers image server](https://images.linuxcontainers.org/).\n\n1. Run `coder templates push --directory .` from this directory.\n2. Create a workspace from the template in the Coder UI.\n\n## Parameters\n\n| Parameter | Description | Default |\n|--------------------|--------------------------------------------------------------------------------------------|--------------------------|\n| **Image** | Container image with cloud-init. Options: Debian 13, Debian 12, Ubuntu 24.04, Ubuntu 22.04 | `images:debian/13/cloud` |\n| **CPU** | Number of CPUs (1-8) | `1` |\n| **Memory** | Memory in GB (1-16) | `2` |\n| **Storage pool** | Incus storage pool name | `coder` |\n| **Git repository** | Clone a git repo inside the workspace | *(empty)* |\n\n## Extending this template\n\nSee the [lxc/incus](https://registry.terraform.io/providers/lxc/incus/latest/docs) Terraform provider documentation to add the following features to your Coder template:\n\n- Remote Incus hosts (HTTPS)\n- Additional volume mounts\n- Custom networks\n- GPU passthrough\n- More\n\nWe also welcome contributions!\n"
},
{
"id": "kubernetes",
"url": "",
Expand Down
1 change: 1 addition & 0 deletions examples/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
//go:embed templates/gcp-linux
//go:embed templates/gcp-vm-container
//go:embed templates/gcp-windows
//go:embed templates/incus
//go:embed templates/kubernetes
//go:embed templates/kubernetes-devcontainer
//go:embed templates/nomad-docker
Expand Down
36 changes: 29 additions & 7 deletions examples/lima/README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,55 @@
---
name: Run Coder in Lima
description: Quickly stand up Coder using Lima
tags: [local, docker, vm, lima]
tags: [local, docker, incus, vm, lima]
---

# Run Coder in Lima

This provides a sample [Lima](https://github.com/lima-vm/lima) configuration for Coder.
This provides sample [Lima](https://github.com/lima-vm/lima) configurations for Coder.
This lets you quickly test out Coder in a self-contained environment.
The Docker configuration runs workspaces in Docker containers; the Incus configuration runs workspaces in Incus system containers (with Docker available inside each workspace).

> Prerequisite: You must have `lima` installed and available to use this.

## Getting Started
## Getting Started (Docker)

- Run `limactl start --name=coder https://raw.githubusercontent.com/coder/coder/main/examples/lima/coder.yaml`
This configuration (`coder-docker.yaml`) creates a VM to run Coder workspaces in Docker.

- Run `limactl start --name=coder https://raw.githubusercontent.com/coder/coder/main/examples/lima/coder-docker.yaml`
- You can use the configuration as-is, or edit it to your liking.

This will:

- Start an Ubuntu 22.04 VM
- Install Docker and Terraform from the official repos
- Install Coder using the [installation script](../../docs/install/install.sh.md)
- Generates an initial user account `admin@coder.com` with a randomly generated password (stored in the VM under `/home/${USER}.linux/.config/coderv2/password`)
- Initializes a [sample Docker template](https://github.com/coder/coder/tree/main/examples/templates/docker) for creating workspaces
- Generate an initial user account `admin@coder.com` with a randomly generated password (stored in the VM under `/home/${USER}.linux/.config/coderv2/password`)
- Initialize a [sample Docker template](https://github.com/coder/coder/tree/main/examples/templates/docker) for creating workspaces

Once this completes, you can visit `http://localhost:3000` and start creating workspaces!

Alternatively, enter the VM with `limactl shell coder` and run `coder templates init` to start creating your own templates!

## Getting Started (Incus)

This configuration (`coder-incus.yaml`) creates a VM to run Coder workspaces in Incus.

- Run `limactl start --name=coder-incus https://raw.githubusercontent.com/coder/coder/main/examples/lima/coder-incus.yaml`
- You can use the configuration as-is, or edit it to your liking.

This will:

- Start a Debian 13 VM
- Install Incus from the Debian repos and Terraform via the Coder installer
- Install Coder using the [installation script](../../docs/install/install.sh.md)
- Generate an initial user account `admin@coder.com` with a randomly generated password (stored in the VM under `/home/${USER}.linux/.config/coderv2/password`)
- Initialize a [sample Incus template](https://github.com/coder/coder/tree/main/examples/templates/incus) for creating workspaces
Comment thread
johnstcn marked this conversation as resolved.

Once this completes, you can visit `http://localhost:3000` and start creating workspaces!

Alternatively, enter the VM with `limactl shell coder-incus` and run `coder templates init` to start creating your own templates!

## Further Information

- To learn more about Lima, [visit the the project's GitHub page](https://github.com/lima-vm/lima/).
- To learn more about Lima, [visit the project's GitHub page](https://github.com/lima-vm/lima/).
6 changes: 3 additions & 3 deletions examples/lima/coder.yaml → examples/lima/coder-docker.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Deploy Coder in Lima via the install script
# Deploy Coder in Lima with Docker via the install script
# See: https://coder.com/docs/install
# $ limactl start ./coder.yaml
# $ limactl start ./coder-docker.yaml
# $ limactl shell coder
# The web UI is accessible on http://localhost:3000 -- ports are forwarded automatically by lima:
# The web UI is accessible on http://localhost:3000. Ports are forwarded automatically by Lima.
# $ coder login http://localhost:3000

# This example requires Lima v0.8.3 or later.
Expand Down
151 changes: 151 additions & 0 deletions examples/lima/coder-incus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Deploy Coder in Lima with Incus
# See: https://coder.com/docs/install
# $ limactl start ./coder-incus.yaml
# $ limactl shell coder-incus
# The web UI is accessible on http://localhost:3000. Ports are forwarded automatically by Lima.
# $ coder login http://localhost:3000

minimumLimaVersion: "2.0.0"

images:
- location: "https://cloud.debian.org/images/cloud/trixie/20260327-2429/debian-13-genericcloud-amd64-20260327-2429.qcow2"
arch: "x86_64"
digest: "sha512:09559ec27d263997827dd8cddf76e97ea8e0f1803380aa501ea7eaa4b4968cd76ffef4ec7eb07ef1a9ccbeb0925a5020492ea9ed53eb167d62f3a2285039912c"
- location: "https://cloud.debian.org/images/cloud/trixie/20260327-2429/debian-13-genericcloud-arm64-20260327-2429.qcow2"
arch: "aarch64"
digest: "sha512:cb25e88240d8760c860f780c42257472f7c63c1ab54368c4eaa4ddb44e1e6224df8e719ee7ab0fb0d52d5de505f98034dd44ee73a9d9dcf66a2035215f1e8512"
# Fallback to the latest release image.
# Hint: run `limactl prune` to invalidate the cache
- location: "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-amd64-daily.qcow2"
arch: "x86_64"
- location: "https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-arm64-daily.qcow2"
arch: "aarch64"

# Disable 9p mounts; they are not supported by the Debian cloud image kernel.
mountTypesUnsupported: [9p]

# Your home directory is mounted read-only
mounts:
- location: "~"
containerd:
system: false
user: false
provision:
- mode: system
script: |
#!/bin/bash
set -eux -o pipefail
command -v incus >/dev/null 2>&1 && exit 0
export DEBIAN_FRONTEND=noninteractive
# Wait for any apt locks from unattended-upgrades on first boot
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do sleep 1; done
# Incus is available natively in Debian Trixie
apt-get update
apt-get install -qqy incus btrfs-progs
# Initialize Incus with preseed config.
# We use an explicit subnet because --minimal's auto-detection fails
# when Lima's own bridge already claims the common ranges.
cat <<'PRESEED' | incus admin init --preseed
networks:
- name: incusbr0
type: bridge
config:
ipv4.address: 10.155.0.1/24
ipv4.nat: "true"
ipv6.address: none
storage_pools:
- name: coder
driver: btrfs
profiles:
- name: default
devices:
eth0:
name: eth0
network: incusbr0
type: nic
root:
path: /
pool: coder
type: disk
PRESEED
# Give the Lima user access to Incus
usermod -aG incus-admin {{.User}}
- mode: system
script: |
#!/bin/bash
set -eux -o pipefail
command -v coder >/dev/null 2>&1 && exit 0
export DEBIAN_FRONTEND=noninteractive
export HOME=/root
# Wait for any apt locks from unattended-upgrades on first boot
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do sleep 1; done
# Using install.sh --with-terraform requires unzip to be available.
apt-get update
apt-get install -qqy unzip
curl -fsSL https://coder.com/install.sh | sh -s -- --with-terraform
# Ensure Coder has access to the Incus socket
usermod -aG incus-admin coder
# Ensure coder listens on all interfaces
sed -i 's/CODER_HTTP_ADDRESS=.*/CODER_HTTP_ADDRESS=0.0.0.0:3000/' /etc/coder.d/coder.env
# Also set the access URL to host.lima.internal for fast deployments
sed -i 's#CODER_ACCESS_URL=.*#CODER_ACCESS_URL=http://host.lima.internal:3000#' /etc/coder.d/coder.env
# Ensure coder starts on boot
systemctl enable coder
systemctl start coder
# Wait for Terraform to be installed
timeout 60s bash -c 'until /usr/local/bin/terraform version >/dev/null 2>&1; do sleep 1; done'
- mode: user
script: |
#!/bin/bash
set -eux -o pipefail
# If we are already logged in, nothing to do
coder templates list >/dev/null 2>&1 && exit 0
# Set up initial user
[ ! -e ~/.config/coderv2/session ] && coder login http://localhost:3000 \
--first-user-username admin \
--first-user-email admin@coder.com \
--first-user-password "$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c12 | tee ${HOME}/.config/coderv2/password)"
# Create an initial Incus template
coder templates init --id incus
Comment thread
johnstcn marked this conversation as resolved.
pushd ./incus
coder templates push incus --yes
popd
rm -rf ./incus
probes:
- description: "incus to be installed"
script: |
#!/bin/bash
set -eux -o pipefail
if ! timeout 30s bash -c "until command -v incus >/dev/null 2>&1; do sleep 3; done"; then
echo >&2 "incus is not installed yet"
exit 1
fi
hint: |
See `/var/log/lima-guestagent.log` or run `limactl shell coder-incus` to debug.
- description: "coder to be installed"
script: |
#!/bin/bash
set -eux -o pipefail
if ! timeout 30s bash -c "until command -v coder >/dev/null 2>&1; do sleep 3; done"; then
echo >&2 "coder is not installed yet"
exit 1
fi
hint: |
See `/var/log/lima-guestagent.log` or run `limactl shell coder-incus` to debug.
message: |
All Done! Your Coder instance is accessible at http://localhost:3000

Username: "admin@coder.com"
Password: Run `LIMA_INSTANCE={{.Instance.Name}} lima cat /home/${USER}.linux/.config/coderv2/password`

Create your first workspace:
------
limactl shell {{.Instance.Name}}
coder create my-workspace --template incus
------

Get started creating your own template now:
------
limactl shell {{.Instance.Name}}
cd && coder templates init
------
Loading