Skip to content

Coder Workspace

Coder is a self-hosted development environment platform that provisions dedicated workspaces for each user. The Ansible Epic Coder workspace runs as an AWS ECS Fargate container connected to a shared EFS volume, and is accessible from VS Code Desktop, VS Code Web, or any terminal via coder ssh.

Advantages over the manual VS Code tunnel

  • No login codes — your workspace starts immediately with your identity pre-configured
  • Persistent home directory across restarts (SSH keys, shell history, VS Code extensions, Claude Code auth)
  • Per-user isolation — your workspace is yours alone, no sharing or coordination required
  • Optional self-hosted GitHub Actions runner in every workspace
  • Selectable CPU and memory per workspace

Accessing Your Workspace

Coder Dashboard

Navigate to https://coder.sapphirehealth.org and log in with your Sapphire credentials. Your workspace appears on the dashboard. Click Start if it is stopped, then connect using one of the methods below.

VS Code Desktop

  1. Install the Coder VS Code extension.
  2. Open the Command Palette (Ctrl+Shift+P) and select Coder: Connect to workspace.
  3. Follow the prompts to authenticate and select your workspace.

VS Code Desktop connects through the Coder agent — no inbound firewall rules or open ports are required on your machine or the container.

VS Code Web (optional)

If Enable VS Code Web is set to true for your workspace, a VS Code Web button appears on the workspace dashboard. Click it to open a full VS Code experience in your browser without installing anything locally.

Note

VS Code Web is disabled by default to save Fargate resources. Enable it in your workspace settings if you prefer browser-based editing.

Terminal (SSH)

coder ssh <workspace-name>

Workspace Parameters

These can be set at workspace creation and changed later via Settings on the workspace dashboard.

Parameter Default Options Description
CPU 2048 (2 vCPU) 512 / 1024 / 2048 Fargate task CPU units.
Memory 4096 (4 GB) 2048 / 4096 / 8192 Fargate task memory in MB.
Enable VS Code Web false true / false Start a browser-based VS Code Web server. Leave disabled for VS Code Desktop users.
Install Claude Code CLI false true / false Install @anthropic-ai/claude-code at workspace start. Run claude in any terminal to use it.
Container Image Production (base:latest) Production / Testing Which container image the workspace runs. Keep the production default; the Testing option is a candidate slot used only while a test build is published (see Container Testing).
Git username / Git email (blank) Any value Optional overrides for git config user.name / user.email. Blank uses your Entra SSO profile name and email.

The aws-gh-runner template additionally has Enable GitHub Actions Runner (default true there), GitHub Runner URL, and Custom Startup Command parameters.

The coder CLI is always installed and authenticated as you (no parameter — it's a prerequisite for the Molecule test-VM workflow): the version-matched agent binary is linked onto PATH, and your Coder session token is placed in the workspace environment.

Fargate CPU/memory constraints

Not all CPU and memory combinations are valid. 2 vCPU (2048) requires at least 4096 MB memory. See the AWS documentation for all valid combinations.


Persistent Storage

Because Fargate containers are ephemeral, the workspace uses a shared EFS volume mounted at /home/ansible/source. Your personal directory is /home/ansible/source/<username>, and the following home directory paths are symlinked to EFS on every start so they survive container restarts:

Path What persists
~/.vscode-server/extensions/ VS Code Desktop extensions
~/.vscode-server/data/User/ VS Code Desktop settings (themes, keybindings, snippets)
~/.vscode/ VS Code Web extensions and state
~/.ssh/ SSH keys and config
~/.claude/ Claude Code settings and conversation history
~/.claude.json Claude Code authentication token
~/.bash_history Shell history

Shell history is flushed after every command (PROMPT_COMMAND="history -a"), so it is preserved even if the terminal closes uncleanly.

The VS Code server binaries under ~/.vscode-server/ are deliberately not persisted: they re-download on the first VS Code Desktop connect after a workspace restart (~30–60s). This keeps server state disposable — a corrupt or version-mismatched server never survives a restart.


GitHub Authentication

Coder integrates with GitHub via OAuth. Connect your GitHub account once in the Coder dashboard under Account → External Authentication, and authentication is handled automatically for both git operations over HTTPS and gh CLI commands — no passwords, tokens, or SSH keys required for GitHub access.

The Coder agent configures GIT_ASKPASS transparently so git clone, git push, git pull, and similar commands authenticate without any prompts. GH_TOKEN is also set in every terminal for the gh CLI.

The token is refreshed dynamically each time a new terminal opens, so it stays current as long as your Coder session is active.

Token Expiry

OAuth tokens can expire during a long-running terminal session. If gh CLI commands start failing with authentication errors, refresh the token manually:

export GH_TOKEN=$(/tmp/coder-agent external-auth access-token primary-github)

Modifying the Template

The workspace is defined in coder/aws/dev-container/main.tf. All infrastructure values are declared as locals at the top of the file — edit those rather than searching for hardcoded strings throughout.

Hardcoded Locals

Local Current Value Description
aws_region us-west-2 AWS region
vpc_id vpc-0955c9f0b9dad2b3e VPC for the ECS task
subnet_id subnet-0ba94effeab197e2d Subnet (public, with IGW)
efs_id fs-04661a8d21c22cb2c Shared EFS file system
account_id 271851283454 AWS account ID
sg_id sg-0a684dcb6fe276813 Shared security group
efs_ap_id fsap-0ecbddc08d79d817f Shared EFS access point
ecs_cluster_arn arn:aws:ecs:...:cluster/CoderCluster ECS cluster
log_group_name /ecs/CoderLogGroup CloudWatch log group
github_runner_pat_secret_arn arn:aws:secretsmanager:...:github_runner_pat-H25W0G Secrets Manager ARN for the GitHub runner PAT

Common Changes

Adding a new workspace parameter:

data "coder_parameter" "my_param" {
  name         = "my_param"
  display_name = "My Parameter"
  description  = "Description shown in the dashboard."
  type         = "bool"
  default      = false
  mutable      = true
}

Adding a startup script:

resource "coder_script" "my_script" {
  agent_id     = coder_agent.main.id
  display_name = "My Script"
  run_on_start = true
  script       = <<-EOF
    #!/bin/sh
    set -e
    echo "Running my script..."
  EOF
}

Tip

Add start_blocks_login = false to any long-running script (one that does not exit) so it does not block the terminal with a "startup scripts still running" banner.

Adding a persistent symlink — edit the coder_script.persist resource and add a link_persist call:

link_persist /home/ansible/.myapp "$PERSIST/.myapp"

For a file rather than a directory, follow the pattern used for ~/.claude.json in the persist script (check for an existing real file, seed EFS if present, then create the symlink).

Exposing a port as a dashboard app:

resource "coder_app" "myapp" {
  agent_id     = coder_agent.main.id
  slug         = "myapp"
  display_name = "My App"
  url          = "http://localhost:3000"
  icon         = "/icon/code.svg"
  subdomain    = false
  share        = "owner"
}

Coder CLI

The Coder CLI is required to push template changes and to SSH into workspaces from a terminal.

Install

winget install Coder.Coder
curl -fsSL https://coder.sapphirehealth.org/install.sh | sh

Log In

coder login https://coder.sapphirehealth.org

This opens a browser window to complete authentication with your Sapphire credentials. The session token is saved locally and reused on subsequent commands.

Keep It Updated

The CLI version must match the server version. Check for updates with:

winget upgrade Coder.Coder
curl -fsSL https://coder.sapphirehealth.org/install.sh | sh

Deploying Template Changes

After editing coder/aws/dev-container/main.tf, push the new template version with:

coder templates push aws-dev-container --directory coder/aws/dev-container

Note

Pushing a new template version does not affect running workspaces. Each user must click Update in their workspace dashboard and restart to pick up the changes.

Commit and push any changes to this repository as well so the template stays in sync with source control:

git add coder/aws/dev-container/main.tf
git commit -m "describe your change"
git push

Workspace Lifecycle

Event What happens
Start ECS service desired_count set to 1; new task definition revision created with a fresh Coder agent token; startup scripts run in order
Stop ECS service desired_count set to 0; SIGTERM sent to the container; startup.sh cleanly deregisters the GitHub Actions runner if enabled
Update New template version applied; workspace must be restarted to take effect

Startup Scripts

Startup scripts run concurrently — there is no guaranteed ordering between them, so scripts that share a resource must tolerate contention (e.g. the install scripts guard their apt usage against each other's locks):

  • Persist User Data — creates the per-user EFS directory and home directory symlinks
  • SSH Agent (Ansible key) — loads the managed-host SSH key into a shared agent
  • Git Config (registry module) — sets user.name/user.email from your Entra SSO profile, or your overrides
  • VS Code Web (if enabled) — starts code serve-web in the background
  • Claude Code CLI (if enabled) — installs Node.js and @anthropic-ai/claude-code
  • Install Coder CLI — links the version-matched coder binary onto PATH (always runs)
  • GitHub Actions Runner (aws-gh-runner template only, if enabled) — registers and starts the runner; logs stream to the Scripts panel

Troubleshooting

VS Code: "the connection to the terminal's pty host process is unresponsive"

Symptom: VS Code Desktop connects to the workspace but terminals never load, showing "the connection to the terminal's pty host process is unresponsive". The workspace itself shows healthy in the Coder dashboard — the wedge is in VS Code's server layer, not the workspace.

Fix, in escalating order:

  1. In VS Code: F1Terminal: Restart Pty Host.
  2. In VS Code: F1Remote-SSH: Kill VS Code Server on Host, close the window, reconnect.
  3. Restart the workspace from the Coder dashboard. Server binaries are not persisted, so a restart always gets a clean server; your extensions and settings survive.

Workspaces on template versions older than 2026-07-21

Older template versions persisted the entire ~/.vscode-server/ on EFS, so corrupt server state survived restarts. If a restart does not fix it (and updating the workspace to the current template version is not an option), open the workspace Terminal from the Coder dashboard (it talks to the Coder agent directly, so it works while the pty host is dead) and run:

rm -rf ~/.vscode-server/cli ~/.vscode-server/code-* ~/.vscode-server/.cli*.log

Use subpaths exactly as shown — on those versions ~/.vscode-server is a symlink into your EFS persist directory, and deleting the symlink itself leaves the corrupt state in place. Then reconnect from VS Code Desktop.