# Workspace Startup Coordination Usage

<Callout type="info" title="Note">
  This feature is experimental and may change without notice in future releases.
</Callout>

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? [#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 [#requirements]

<Callout type="idea" title="Important">
  The `coder exp sync` command is only available from Coder version >=v2.30 onwards.
</Callout>

To use startup dependencies in your templates, you must:

* Modify your workspace startup scripts to run in parallel
* Declare dependencies as required using `coder exp sync`

### Declare Dependencies in your Workspace Startup Scripts [#declare-dependencies-in-your-workspace-startup-scripts]

<Tabs items="[&#x22;Single Dependency&#x22;, &#x22;Multiple Dependencies&#x22;]" groupId="coder-docs-tab:multiple-dependencies-single-dependency" persist="true">
  <Tab value="Single Dependency">
    Here's a simple example of a script that depends on another unit completing
    first:

    ```sh
    #!/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.
  </Tab>

  <Tab value="Multiple Dependencies">
    If your unit depends on multiple other units, you can declare all dependencies
    before starting:

    ```sh
    #!/bin/bash
    UNIT_NAME="my-app"
    DEPENDENCIES="git-clone,env-setup,database-migration"

    # Declare all dependencies
    if [ -n "$DEPENDENCIES" ]; then
      IFS=',' read -ra DEPS <<< "$DEPENDENCIES"
      for dep in "${DEPS[@]}"; do
        dep=$(echo "$dep" | xargs)  # Trim whitespace
        if [ -n "$dep" ]; then
          coder exp sync want "$UNIT_NAME" "$dep"
        fi
      done
    fi

    # Wait for all dependencies
    coder exp sync start "$UNIT_NAME"

    # Your work here
    echo "All dependencies satisfied, starting application"

    # Signal completion
    coder exp sync complete "$UNIT_NAME"
    ```
  </Tab>
</Tabs>

## Inspect Unit State [#inspect-unit-state]

Use `coder exp sync list` to see all registered units and their current state:

```sh
coder exp sync list
```

Example output:

```sh
UNIT           STATUS     READY
git-clone      completed  true
env-setup      started    true
ide-configure  pending    false
```

To inspect a single unit and its dependencies in detail, use `coder exp sync status <unit>`.

To verify the agent socket is reachable, use `coder exp sync ping`.

## Best Practices [#best-practices]

### Test your changes before rolling out to all users [#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 using `coder exp sync list`
4. Test workspace functionality

Once you're satisfied, [promote the new template version](/beta-docs/reference/cli/templates_versions_promote/).

### Handle missing CLI gracefully [#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:

```sh
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 [#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:

```sh

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 [#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 [#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 [#document-dependencies]

Add comments explaining why dependencies exist:

```tf
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 [#avoid-circular-dependencies]

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

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

## Frequently Asked Questions [#frequently-asked-questions]

### How do I identify scripts that can benefit from startup coordination? [#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? [#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? [#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? [#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:

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

### Is state stored between restarts? [#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.
