---
title: Coordinating development runtime dependencies
description: Learn how to coordinate development services that depend on each other at runtime.
url: "https://turborepo.dev/docs/guides/coordinating-runtime-dependencies"
docs_index: /llms.txt
lastUpdated: 2026-10-21
product: turborepo
type: guide
summary: "Combine a readiness probe with `with` and `dependsOn` to coordinate runtime dependencies between development services."
prerequisites:
  - /docs/crafting-your-repository/developing-applications
related:
  - /docs/reference/configuration
  - /docs/reference/run
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

Some development tasks need another service to be available before they can start. For example, a frontend may need an API to be accepting requests before its development server starts.

> This pattern is intended for coordinating services during local development. For CI and production, use the service orchestration and readiness capabilities provided by your CI provider or deployment platform.

Turborepo can coordinate this workflow by combining:

- [`with`](/docs/reference/configuration#with) to start the service alongside the task that needs it
- [`dependsOn`](/docs/reference/configuration#dependson) to wait for a finite readiness probe to succeed
- [`persistent`](/docs/reference/configuration#persistent) to mark the service as long-running

This guide configures packages named `web` and `api`, where the API's development server exposes a health endpoint at `http://localhost:3001/health`.

## Create a readiness probe

The readiness probe should retry until the service is available, exit successfully when it is ready, and fail after a timeout. Keep the readiness condition specific to your service so it can check everything the dependent task requires, such as a database connection or completed initialization.

```js title="./apps/api/scripts/wait-for-ready.mjs"
const healthUrl = "http://localhost:3001/health";
const deadline = Date.now() + 60_000;

while (Date.now() < deadline) {
  try {
    const response = await fetch(healthUrl, {
      signal: AbortSignal.timeout(1_000),
    });

    if (response.ok) {
      console.log(`API is ready at ${healthUrl}`);
      process.exit(0);
    }
  } catch {
    // The service may still be starting. Try again until the deadline.
  }

  await new Promise((resolve) => setTimeout(resolve, 500));
}

console.error(`API did not become ready at ${healthUrl} within 60 seconds`);
process.exit(1);
```

Add scripts for the long-running service and its finite readiness probe:

```json title="./apps/api/package.json"
{
  "name": "api",
  "scripts": {
    "dev": "node ./server.mjs",
    "dev:ready": "node ./scripts/wait-for-ready.mjs"
  }
}
```

The `api#dev:ready` script does not start the API. It only reports whether the API started by `api#dev` is ready.

The `web` application also needs a `dev` script:

```json title="./apps/web/package.json"
{
  "name": "web",
  "scripts": {
    "dev": "start-web"
  }
}
```

## Register the tasks

Register both task names in the root `turbo.json`. The readiness probe must not be cached because its result depends on the state of a running service rather than files in the repository.

```json title="./turbo.json"
{
  "$schema": "https://turborepo.dev/schema.json",
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true
    },
    "dev:ready": {
      "cache": false
    }
  }
}
```

Next, use a [Package Configuration](/docs/reference/package-configurations) to describe the `web` application's runtime requirements:

```json title="./apps/web/turbo.json"
{
  "extends": ["//"],
  "tasks": {
    "dev": {
      "with": ["api#dev"],
      "dependsOn": ["api#dev:ready"]
    }
  }
}
```

When `web#dev` is selected, Turborepo:

1. Adds the persistent `api#dev` task to the run because of `with`.
2. Starts `api#dev` and the non-persistent `api#dev:ready` probe.
3. Starts `web#dev` after the probe exits successfully.
4. Continues monitoring `api#dev` and `web#dev` until you stop Turborepo or either task exits.

Do not add `api#dev` to `dependsOn`. A persistent task does not exit, so it cannot complete and unblock a dependent task. The finite `api#dev:ready` probe provides that completion signal instead.

## Run the application

Select the `web` application to start the complete workflow:

```bash title="Terminal"
turbo run dev --filter=web
```

The default concurrency has enough capacity for this workflow. If you set [`--concurrency`](/docs/reference/run#--concurrency-number--percentage), use at least `3` so Turborepo has a slot for each persistent task and another for the readiness probe.

If the API does not become ready before the probe's timeout, the probe exits with an error and `web#dev` does not start. Turborepo stops all tasks when you interrupt the run.

## Probe other readiness conditions

An HTTP endpoint is only one way to determine readiness. The probe script can wait for any condition your service exposes, including:

- A TCP port accepting connections
- A file or Unix socket being created
- A database accepting queries
- A specific initialization check succeeding

You can implement the probe yourself, as shown above, or use a utility such as [`wait-on`](https://www.npmjs.com/package/wait-on). In either case, ensure the probe retries transient failures, has a timeout, and exits with a non-zero status when the service does not become ready.

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)