Usage

Note

This feature is experimental and may change without notice in future releases.

Startup coordination is built around the concept of units. You declare units in your Coder workspace template using the coder exp sync command in coder_script resources. When the Coder agent starts, it keeps an in-memory directed acyclic graph (DAG) of all units of which it is aware. When you need to synchronize with another unit, you can use coder exp sync start $UNIT_NAME to block until all dependencies of that unit have been marked complete.

What is a unit?

A unit is a named phase of work, typically corresponding to a script or initialization task.

  • Units may declare dependencies on other units, creating an explicit ordering for workspace initialization.
  • Units must be registered before they can be marked as complete.
  • Units may be marked as dependencies before they are registered.
  • Units must not declare cyclic dependencies. Attempting to create a cyclic dependency will result in an error.

Requirements

Important

The coder exp sync command is only available from Coder version >=v2.30 onwards.

To use startup dependencies in your templates, you must:

  • Enable the Coder Agent Socket Server.
  • Modify your workspace startup scripts to run in parallel and declare dependencies as required using coder exp sync.

Enable the Coder Agent Socket Server

The agent socket server provides the communication layer for startup coordination. To enable it, set CODER_AGENT_SOCKET_SERVER_ENABLED=true in the environment in which the agent is running. The exact method for doing this depends on your infrastructure platform:

resource "docker_container" "workspace" { count = data.coder_workspace.me.start_count image = "codercom/enterprise-base:ubuntu" name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" env = [ "CODER_AGENT_SOCKET_SERVER_ENABLED=true" ] command = ["sh", "-c", coder_agent.main.init_script] }

Declare Dependencies in your Workspace Startup Scripts

Here's a simple example of a script that depends on another unit completing first:

#!/bin/bash UNIT_NAME="my-setup" # Declare dependency on git-clone coder exp sync want "$UNIT_NAME" "git-clone" # Wait for dependencies and mark as started coder exp sync start "$UNIT_NAME" # Do your work here echo "Running after git-clone completes" # Signal completion coder exp sync complete "$UNIT_NAME"

This script will wait until the git-clone unit completes before starting its own work.

Best Practices

Test your changes before rolling out to all users

Before rolling out to all users:

  1. Create a test workspace from the updated template
  2. Check workspace build logs for sync messages
  3. Verify all units reach "completed" status
  4. Test workspace functionality

Once you're satisfied, promote the new template version.

Handle missing CLI gracefully

Not all workspaces will have the Coder CLI available in $PATH. Check for availability of the Coder CLI before using sync commands:

if command -v coder > /dev/null 2>&1; then coder exp sync start "$UNIT_NAME" else echo "Coder CLI not available, continuing without coordination" fi

Complete units that start successfully

Units must call coder exp sync complete to unblock dependent units. Use trap to ensure completion even if your script exits early or encounters errors:

SYNC_STARTED=0 if coder exp sync start "$UNIT_NAME"; then SYNC_STARTED=1 fi cleanup_sync() { if [ "$SYNC_STARTED" -eq 1 ]; then coder exp sync complete "$UNIT_NAME" fi } trap cleanup_sync EXIT

Use descriptive unit names

Names should explain what the unit does, not its position in a sequence:

  • Good: git-clone, env-setup, database-migration
  • Avoid: step1, init, script-1

Prefix a unique name to your units

When using coder exp sync in modules, note that unit names like git-clone might be common. Prefix the name of your module to your units to ensure that your unit does not conflict with others.

  • Good: <module>.git-clone, <module>.claude
  • Bad: git-clone, claude

Document dependencies

Add comments explaining why dependencies exist:

resource "coder_script" "ide_setup" { # Depends on git-clone because we need .vscode/extensions.json # Depends on env-setup because we need $NODE_PATH configured script = <<-EOT coder exp sync want "ide-setup" "git-clone" coder exp sync want "ide-setup" "env-setup" # ... EOT }

Avoid circular dependencies

The Coder Agent detects and rejects circular dependencies, but they indicate a design problem:

# This will fail coder exp sync want "unit-a" "unit-b" coder exp sync want "unit-b" "unit-a"

Frequently Asked Questions

How do I identify scripts that can benefit from startup coordination?

Look for these patterns in existing templates:

  • sleep commands used to order scripts
  • Using files to coordinate startup between scripts (e.g. touch /tmp/startup-complete)
  • Scripts that fail intermittently on startup
  • Comments like "must run after X" or "wait for Y"

Will this slow down my workspace?

No. The socket server adds minimal overhead, and the default polling interval is 1 second, so waiting for dependencies adds at most a few seconds to startup. You are more likely to notice an improvement in startup times as it becomes easier to manage complex dependencies in parallel.

How do units interact with each other?

Units with no dependencies run immediately and in parallel. Only units with unsatisfied dependencies wait for their dependencies.

How long can a dependency take to complete?

By default, coder exp sync start has a 5-minute timeout to prevent indefinite hangs. Upon timeout, the command will exit with an error code and print timeout waiting for dependencies of unit <unit_name> to stderr.

You can adjust this timeout as necessary for long-running operations:

coder exp sync start "long-operation" --timeout 10m

Is state stored between restarts?

No. Sync state is kept in-memory only and resets on workspace restart. This is intentional to ensure clean initialization on every start.