# Manage the services under test

> Declare how Axx starts, waits for, stops and cleans up the services under test, keep them running with axx up, attach services you run yourself, and start only the services a run needs.

Source: https://axx.nimbusxr.us/next/guides/manage-services/

`services:` in `axx.yaml` describes the system under test: your service and anything it needs, such as databases, brokers and mocks. `axx run` starts them, waits until they are ready, runs the scenarios, stops them and runs their cleanup.

A service is what runs on its own and waits for whoever connects to it, by an address. A program its user starts, uses and ends is not a service, even when it is what you test: a command-line tool, or an MCP server its clients run over stdio. The step that uses it runs it, as its user would: the cli pack's and the mcp pack's `command`.

## Declare a service

```yaml title="axx.yaml"
services:
  infra:
    dir: ./infra                          # relative to axx.yaml
    command: docker compose up --wait postgres kafka
    ready:
      tcp: localhost:5432
    cleanup: docker compose down -v --remove-orphans

  api:
    dependsOn: [infra]
    command: ./gradlew bootRun            # or npm start, go run ./cmd/api, ...
    env:
      SPRING_PROFILES_ACTIVE: acceptance
    ready:
      http:
        url: http://localhost:8080/actuator/health
      timeout: 120s
      interval: 1s
    stop:
      signal: SIGTERM
      grace: 20s
```

Each key is described in the [configuration reference](https://axx.nimbusxr.us/next/references/config/#services). A command that exits `0` before the service is ready is fine, which is what `docker compose up -d` or `--wait` does: Axx keeps polling the `ready` checks.

If you also run a compose file yourself while you develop, give the stack Axx starts from it a name of its own, in `command` and `cleanup` alike: `docker compose -p parcels-axx up`. Compose otherwise names both stacks after the folder, so Axx's takes over yours, and `down -v` deletes your stack's volumes, your local data with them. `axx init` names the stack it writes so.

Service output goes to `.axx/logs/services.log`. When a service fails to start, the error shows the last lines of its output and a stable code from [`AXX-E0400` to `AXX-E0415`](https://axx.nimbusxr.us/next/references/error-codes/#service-lifecycle).

## Keep services running between runs

```sh
axx up            # start every enabled service (or: axx up api) and wait until ready
axx run           # reuses the running services: no start, no stop
axx run --tags @smoke
axx env           # which services are running, left over from a killed run, or not cleaned up
axx down          # stop the services and run their cleanup
```

This is the fastest local loop, and the one agents should use. The services keep their data from run to run, and `axx run` warns so when it reuses them: a scenario that creates something (registers a parcel by its reference, say) finds what an earlier run created and fails where a fresh start would pass. `axx down`, then `axx up`, starts them afresh; check a change on fresh services before you trust its result.

## When a run cannot start them

An agent whose sandbox keeps it from Docker cannot start services that run with `docker compose`. `axx host`, started in the project on your machine, starts them for its runs: each run asks the host for its services, the host starts them as `axx.yaml` says and cleans up after the run, and it starts only a definition you approved. [Run tests from an agent's sandbox](https://axx.nimbusxr.us/next/guides/run-from-a-sandbox/) shows how.

## When a run is stopped by force

A run that is killed before it can stop its services (stopped by force from an editor, say, or crashed) leaves them in `.axx/run/state.json`. The next run cleans up first, as `axx down` does: it stops what is left, runs the cleanups, says so, and starts from a clean slate. What belongs to a run still going, or to `axx up`, is left alone: a run then starts no service and says what is left (`AXX-E0415`).

## When a cleanup fails

A cleanup can fail: `docker compose down` without access to Docker, say. The run reports it (`AXX-E0411`), and `.axx/run/state.json` keeps the cleanup, so what it should have removed is not forgotten. Until it succeeds:

- runs and `axx up` try it again first, and start no service while it fails, saying what is left (`AXX-E0415`): containers, volumes and requests recorded by a mock would be where the next run starts from;
- `axx down` runs the cleanup again, and says `cleaned up after an earlier run` once it succeeds;
- `axx doctor` lists what is left, and so do `axx env` and the MCP `env` tool's `status`, with each service `running`, `left over` from a run that was killed, or `not cleaned up`.

## Run a service yourself

To run the service from your IDE (with breakpoints, hot reload, a profiler), tell Axx not to start it. Axx still starts everything else and waits for its readiness checks:

```sh
axx run --attach api
```

`--no-start` skips starting, stopping and cleaning up every service; use it when the whole system is already running somewhere.

## Debug a service

Give the service a debug command and a debugger to wait for:

```yaml title="axx.yaml"
services:
  api:
    command: ./gradlew bootRun
    debug:
      command: ./gradlew bootRun -PappJvmArgs=-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005,suspend=n
      debugger:
        type: java            # java, go, nodejs or python
        port: 5005
        mode: ide-listens     # the IDE listens, the service connects (default for java)
      onUnavailable: retry    # retry (default), fail, or fallback to the normal command
      retry: {attempts: 10, delay: 3s}
```

```sh
axx run --debug          # every service with a debug block
axx run --debug=api      # only api
axx up --debug=api
```

With `mode: ide-listens`, start a listening debugger in your IDE first (in IntelliJ IDEA: a *Remote JVM Debug* configuration in *Listen to remote JVM* mode on port 5005), then run Axx. With `mode: app-listens` (delve, `--inspect`, debugpy), the service opens the port and you attach; the [parcels example](https://github.com/nimbusxr/axx/tree/main/examples/parcels) runs its Go service under Delve this way. If no debugger is listening, `onUnavailable` decides whether to wait, fail with [`AXX-E0409`](https://axx.nimbusxr.us/next/references/error-codes/#axx-e0409), or run without debugging.

## Start only what a run needs

In a repository with several services, start only the ones the selected scenarios need:

```yaml title="axx.yaml"
active:
  enabled: true
  onNoTags: fallback       # no selected scenario has a tag: start every enabled service (or: error)

services:
  orders:
    command: ./gradlew :orders:bootRun
    active: {tags: ["@orders"]}
  billing:
    command: ./gradlew :billing:bootRun
    active: {tags: ["@billing", "@payments"]}
  wiremock:
    command: docker compose up wiremock   # no active.tags: always starts
```

A service with `active.tags` starts when any selected scenario carries one of its tags, and the services in its `dependsOn` start with it. Services without `active.tags` always start. `axx run --tags @billing` starts `billing` and `wiremock`, not `orders`.

A service can also wait for what the scenarios use, with `active.uses`: it starts when a selected scenario uses a step of one of its packs, no tag needed. A service only the mobile apps call, say:

```yaml title="axx.yaml"
services:
  push-gateway:
    command: docker compose up push-gateway
    active: {uses: [mobile-android, mobile-ios]}
```
