> ## Documentation Index
> Fetch the complete documentation index at: https://ona.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# automations.yaml schema

> Field reference for tasks and services in automations.yaml.

Ona loads tasks and services from `.ona/automations.yaml` in your repository root. You can change this location in [Projects](/ona/projects/overview).

```yaml theme={null}
services:
  # long-running processes (databases, servers)

tasks:
  # one-off actions (build, test, seed)
```

The key (e.g., `database`, `buildAll`) is used to reference the item in dependencies and CLI commands. Keys must match the pattern `^[a-zA-Z0-9_-]{1,128}$` (alphanumeric, underscores, and hyphens, 1-128 characters).

## Service schema

Services are long-running processes that stay active throughout your session.

### Service fields

| Field              | Type     | Required | Description                                                                                                                  |
| ------------------ | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `name`             | string   | Yes      | Display name shown in UI and logs                                                                                            |
| `description`      | string   | No       | Description of what the service does                                                                                         |
| `commands`         | object   | No       | Lifecycle commands (see below)                                                                                               |
| `triggeredBy`      | array    | No       | When to automatically start (see [Triggers](#triggers))                                                                      |
| `role`             | string   | No       | Service role: `default`, `editor`, or `ai-agent`                                                                             |
| `runsOn`           | object   | No       | Execution environment (see [Execution environment](#execution-environment))                                                  |
| `readinessTimeout` | duration | No       | Max time to wait for `commands.ready` before startup fails (for example `30m`, `1h`). `0s` disables timeout. Range: `0s-24h` |

### Service commands

The `commands` object controls the service lifecycle:

| Field   | Required                  | Description                                                                                                                                                                                                            |
| ------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `start` | Yes (if commands defined) | Command to start and run the service. **Must stay running**: the service is active only while this process is alive. If it exits with code 0, the service transitions to Stopped. Non-zero exit transitions to Failed. |
| `ready` | No                        | Readiness check command. Runs repeatedly until it exits with code 0. Service stays in Starting phase until ready succeeds. If not set, the service is considered ready as soon as `start` begins running.              |
| `stop`  | No                        | Custom stop command. If not set, start receives SIGTERM when stop is requested. If set, stop runs first, then start receives SIGKILL.                                                                                  |

<Info>
  When stopping a service, if the process doesn't exit within 2 minutes, SIGKILL is sent automatically.
</Info>

### Service example

```yaml theme={null}
services:
  database:
    name: PostgreSQL
    description: The backend database
    triggeredBy:
      - postEnvironmentStart
    commands:
      start: docker run --rm --name database postgres:latest
      ready: docker exec database pg_isready
      stop: docker stop database

  backend:
    name: Application Backend
    description: The application backend
    role: default
    triggeredBy:
      - postEnvironmentStart
    commands:
      start: cd backend && go run main.go
```

### Service phases

Services transition through these phases during their lifecycle:

| Phase      | Description                                                    |
| ---------- | -------------------------------------------------------------- |
| `STARTING` | Start command running, readiness check pending (if configured) |
| `RUNNING`  | Service is running and ready                                   |
| `STOPPING` | Service is being stopped                                       |
| `STOPPED`  | Service stopped normally (exit code 0)                         |
| `FAILED`   | Service failed (non-zero exit code)                            |

## Task schema

Tasks are one-off actions that run to completion.

### Task fields

| Field                     | Type    | Required | Description                                                                                                                                                                                                                                                                                                            |
| ------------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                    | string  | Yes      | Display name shown in UI and logs                                                                                                                                                                                                                                                                                      |
| `command`                 | string  | Yes      | Shell command to execute                                                                                                                                                                                                                                                                                               |
| `description`             | string  | No       | Description of what the task does                                                                                                                                                                                                                                                                                      |
| `triggeredBy`             | array   | No       | When to automatically run (see [Triggers](#triggers))                                                                                                                                                                                                                                                                  |
| `dependsOn`               | array   | No       | Task keys that must complete before this task starts. In a dependency chain, only the final task should have an automatic trigger - dependent tasks are started automatically when needed.                                                                                                                             |
| `prebuildRequiresSuccess` | boolean | No       | When `true` and the task has a `prebuild` or `beforeSnapshot` trigger, a non-successful outcome (failed or stopped) fails the entire prebuild. Defaults to `false` (task failures produce a warning but the prebuild still succeeds). See [Requiring task success in prebuilds](#requiring-task-success-in-prebuilds). |
| `runsOn`                  | object  | No       | Execution environment (see [Execution environment](#execution-environment))                                                                                                                                                                                                                                            |

### Task example

```yaml theme={null}
tasks:
  buildAll:
    name: Build All
    description: Builds all code
    command: go build .

  runUnitTests:
    name: Run unit tests
    command: go test -v ./...
    dependsOn:
      - buildAll

  validate:
    name: Validate
    description: Builds and tests the code
    triggeredBy:
      - postEnvironmentStart
    dependsOn:
      - buildAll
      - runUnitTests
    command: echo "Validation complete"
```

### Task execution phases

Task executions transition through these phases:

| Phase       | Description                               |
| ----------- | ----------------------------------------- |
| `PENDING`   | Task waiting to start                     |
| `RUNNING`   | Task is executing                         |
| `SUCCEEDED` | Task completed successfully (exit code 0) |
| `FAILED`    | Task failed (non-zero exit code)          |
| `STOPPED`   | Task was stopped before completion        |

## Triggers

Control when tasks and services run automatically:

| Trigger                 | Services | Tasks | Description                                                                                                                  |
| ----------------------- | -------- | ----- | ---------------------------------------------------------------------------------------------------------------------------- |
| `manual`                | ✓        | ✓     | Triggered by explicit user action via CLI or UI                                                                              |
| `postDevcontainerStart` | ✓        | ✓     | After the Dev Container starts in a user environment (first start or rebuild). Does **not** fire during prebuilds.           |
| `postEnvironmentStart`  | ✓        | ✓     | Every time the environment starts or resumes                                                                                 |
| `prebuild`              | ✓        | ✓     | During prebuild execution only. Does **not** fire when a user environment starts from a prebuild. No user secrets available. |

<Warning>
  User secrets are not available during prebuild execution because prebuilds run without user context.
</Warning>

### Additional triggers (API only)

The following triggers can only be set via the [API](/ona/api-reference), not in `automations.yaml`:

| Trigger            | Services | Tasks | Description                                                                                                                               |
| ------------------ | -------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `postMachineStart` | ✓        | ✓     | After the VM starts, before the Dev Container is ready. Requires `runsOn: machine`. Used for machine-level services like security agents. |
| `beforeSnapshot`   | ✗        | ✓     | After all prebuild tasks complete, before the snapshot is taken. Used for tasks that must run last during prebuilds, such as IDE warmup.  |

### Prebuilds and triggers

During a prebuild, **only** automations (tasks and services) with the `prebuild` trigger run. All other triggers (`postDevcontainerStart`, `postEnvironmentStart`) are skipped. This is intentional: prebuilds run without user context (no user secrets), so running automations during a prebuild is opt-in.

When a user creates an environment from a prebuild, the normal triggers fire: `postDevcontainerStart`, `postEnvironmentStart`, etc. The `prebuild` trigger does **not** fire in user environments.

**Common pattern — tasks**: Use both triggers on the same task when you want it to run during prebuilds *and* when the Dev Container is rebuilt in a user environment:

```yaml theme={null}
tasks:
  install-deps:
    name: Install dependencies
    triggeredBy:
      - prebuild
      - postDevcontainerStart
    command: npm ci
```

In this example, `npm ci` runs during the prebuild (so the snapshot includes `node_modules`) and also runs if a user rebuilds their Dev Container (which creates a fresh container without the snapshot).

**Common pattern — services**: Start a service during prebuilds so that prebuild tasks can depend on it:

```yaml theme={null}
services:
  database:
    name: Database server
    commands:
      start: docker run --rm --name database -p 5432:5432 postgres:16
      ready: docker exec database pg_isready
    triggeredBy:
      - prebuild
      - postDevcontainerStart
```

The prebuild waits for all services to become ready before snapshotting. Without a `ready` command, the service is considered ready immediately, so the prebuild may snapshot before the service has finished initializing (e.g., before a Docker image is fully pulled). Always define a `ready` command for prebuild services so that setup work is captured in the snapshot. If the service fails, the prebuild still completes.

### Requiring task success in prebuilds

By default, a task that fails during a prebuild produces a warning but the prebuild still succeeds and creates a snapshot. Set `prebuildRequiresSuccess: true` to treat a non-successful outcome (failed or stopped) as a prebuild failure instead — no snapshot is created and the prebuild moves to the **Failed** phase.

This applies only to tasks with a `prebuild` or `beforeSnapshot` trigger. The flag is ignored for other triggers.

```yaml theme={null}
tasks:
  build:
    name: Build project
    triggeredBy:
      - prebuild
    prebuildRequiresSuccess: true
    command: make build
```

If multiple tasks have `prebuildRequiresSuccess: true` and more than one fails, the failure message lists all of them.

## Execution environment

By default, tasks and services run inside the Dev Container. The `runsOn` field lets you change where they execute.

### Run on the host machine

Use `runsOn: machine` to run directly on the VM, outside the Dev Container. This is useful for services that need to start before the Dev Container is ready (e.g., with the `postMachineStart` API trigger) or that need direct access to the host.

```yaml theme={null}
services:
  security-agent:
    name: Security Agent
    commands:
      start: /opt/agent/run
    runsOn:
      machine: {}
```

### Run in a Docker container

Use `runsOn: docker` to run in a separate Docker container with a specific image:

```yaml theme={null}
tasks:
  lint:
    name: Lint
    command: golangci-lint run ./...
    runsOn:
      docker:
        image: golangci/golangci-lint:latest
        environment:
          - GOLANGCI_LINT_CACHE=/tmp/cache
```

| Field                | Type   | Required | Description                                        |
| -------------------- | ------ | -------- | -------------------------------------------------- |
| `docker.image`       | string | Yes      | Docker image to run in                             |
| `docker.environment` | array  | No       | Environment variables to set (format: `KEY=VALUE`) |

## Complete example

This example demonstrates all available schema features:

```yaml theme={null}
services:
  # Containerized service using docker run
  redis:
    name: Redis Cache
    description: In-memory cache for session data
    role: default
    triggeredBy:
      - postDevcontainerStart
    commands:
      start: |
        if docker inspect redis >/dev/null 2>&1; then
          docker start -a redis
        else
          docker run --name redis -p 6379:6379 redis:7-alpine redis-server --appendonly yes
        fi
      ready: docker exec redis redis-cli ping | grep -q PONG
      stop: docker stop redis

  # Service running in the Dev Container
  backend:
    name: API Server
    description: Main application backend
    triggeredBy:
      - postDevcontainerStart
      - manual
    commands:
      start: cd backend && go run main.go
      ready: curl -sf http://localhost:8080/health

  # AI agent service
  code-assistant:
    name: Code Assistant
    description: AI-powered code analysis
    role: ai-agent
    triggeredBy:
      - manual
    commands:
      start: ./bin/assistant serve

tasks:
  # Task that runs during prebuild (no user secrets available)
  install-deps:
    name: Install dependencies
    description: Install all project dependencies
    triggeredBy:
      - prebuild
      - postDevcontainerStart
    command: npm ci && go mod download

  # Task with dependencies on other tasks
  build:
    name: Build project
    description: Compile all source code
    dependsOn:
      - install-deps
    command: npm run build && go build ./...

  # Lint task
  lint:
    name: Lint code
    description: Run linters
    command: golangci-lint run ./...

  # Manual task for running tests
  test:
    name: Run tests
    description: Execute the full test suite
    triggeredBy:
      - manual
    dependsOn:
      - build
    command: npm test && go test -v ./...
```

## Iterating on Tasks and Services

You can iterate on tasks and services using the [CLI](/ona/integrations/cli) which is available by default in every Ona environment. The CLI can

* reload the tasks and services file using:

```bash theme={null}
ona automations update [optional-path-to-automations.yaml]
```

* start a task or service:

```bash theme={null}
ona automations service start ...
ona automations task start ...
```

## Using Tasks and Services outside of an environment

The CLI commands to interact with an environment’s tasks and services are also available outside of an environment. The following snippet brings up an environment, adds a task, runs it, waits for the task to complete and brings the environment back down again:

```bash theme={null}
# ona env create will set the environment context to the newly created env
ona env create https://github.com/some/repo

# add the task to the environment
cat <<EOF | ona automations update -
tasks:
  build:
    command: go build ./...
EOF

# run it
ona automations task start build

# stop the environment
ona env stop
```
