+ "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"
0 commit comments