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
- Install the Coder VS Code extension.
- Open the Command Palette (
Ctrl+Shift+P) and select Coder: Connect to workspace. - 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)
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:
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:
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
Log In
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:
Deploying Template Changes
After editing coder/aws/dev-container/main.tf, push the new template version with:
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:
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.emailfrom your Entra SSO profile, or your overrides - VS Code Web (if enabled) — starts
code serve-webin the background - Claude Code CLI (if enabled) — installs Node.js and
@anthropic-ai/claude-code - Install Coder CLI — links the version-matched
coderbinary onto PATH (always runs) - GitHub Actions Runner (
aws-gh-runnertemplate 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:
- In VS Code:
F1→ Terminal: Restart Pty Host. - In VS Code:
F1→ Remote-SSH: Kill VS Code Server on Host, close the window, reconnect. - 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:
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.