Skip to main content
Ona loads tasks and services from .ona/automations.yaml in your repository root. You can change this location in Projects.
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

FieldTypeRequiredDescription
namestringYesDisplay name shown in UI and logs
descriptionstringNoDescription of what the service does
commandsobjectNoLifecycle commands (see below)
triggeredByarrayNoWhen to automatically start (see Triggers)
rolestringNoService role: default, editor, or ai-agent
runsOnobjectNoExecution environment (see Execution environment)

Service commands

The commands object controls the service lifecycle:
FieldRequiredDescription
startYes (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.
readyNoReadiness 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.
stopNoCustom stop command. If not set, start receives SIGTERM when stop is requested. If set, stop runs first, then start receives SIGKILL.
When stopping a service, if the process doesn’t exit within 2 minutes, SIGKILL is sent automatically.

Service example

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:
PhaseDescription
STARTINGStart command running, readiness check pending (if configured)
RUNNINGService is running and ready
STOPPINGService is being stopped
STOPPEDService stopped normally (exit code 0)
FAILEDService failed (non-zero exit code)

Task schema

Tasks are one-off actions that run to completion.

Task fields

FieldTypeRequiredDescription
namestringYesDisplay name shown in UI and logs
commandstringYesShell command to execute
descriptionstringNoDescription of what the task does
triggeredByarrayNoWhen to automatically run (see Triggers)
dependsOnarrayNoTask 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.
runsOnobjectNoExecution environment (see Execution environment)

Task example

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:
PhaseDescription
PENDINGTask waiting to start
RUNNINGTask is executing
SUCCEEDEDTask completed successfully (exit code 0)
FAILEDTask failed (non-zero exit code)
STOPPEDTask was stopped before completion

Triggers

Control when tasks and services run automatically:
TriggerServicesTasksDescription
manualTriggered by explicit user action via CLI or UI
postDevcontainerStartAfter the Dev Container starts in a user environment (first start or rebuild). Does not fire during prebuilds.
postEnvironmentStartEvery time the environment starts or resumes
prebuildDuring prebuild execution only. Does not fire when a user environment starts from a prebuild. No user secrets available.
User secrets are not available during prebuild execution because prebuilds run without user context.

Additional triggers (API only)

The following triggers can only be set via the API, not in automations.yaml:
TriggerServicesTasksDescription
postMachineStartAfter the VM starts, before the Dev Container is ready. Requires runsOn: machine. Used for machine-level services like security agents.
beforeSnapshotAfter 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:
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:
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.

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.
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:
tasks:
  lint:
    name: Lint
    command: golangci-lint run ./...
    runsOn:
      docker:
        image: golangci/golangci-lint:latest
        environment:
          - GOLANGCI_LINT_CACHE=/tmp/cache
FieldTypeRequiredDescription
docker.imagestringYesDocker image to run in
docker.environmentarrayNoEnvironment variables to set (format: KEY=VALUE)

Complete example

This example demonstrates all available schema features:
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 which is available by default in every Ona environment. The CLI can
  • reload the tasks and services file using:
ona automations update [optional-path-to-automations.yaml]
  • start a task or service:
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:
# 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