# Observability as Code

> Manage Uptrace organizations, projects, monitors, and notification channels with the Uptrace Terraform provider — version-controlled and reproducible across environments.

Uptrace lets you define your entire observability setup as code. Organizations, projects, alert monitors, notification channels, and team access are managed with [Terraform](https://www.terraform.io/) using the official [Uptrace provider](https://registry.terraform.io/providers/uptrace/uptrace/latest). Dashboards and transformation rules are managed as YAML files.

<video autoPlay="true" loop="true" muted="true" playsInline="true">
<source src="/video/product/alerting/alert-as-code.mp4" type="video/mp4" />
</video>

## What you can manage as code

<table>
<thead>
  <tr>
    <th>
      Component
    </th>
    
    <th>
      Method
    </th>
    
    <th>
      What it controls
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <a href="#organizations-and-projects">
        Organizations & projects
      </a>
    </td>
    
    <td>
      Terraform
    </td>
    
    <td>
      Orgs, projects, retention policies, ingest tokens
    </td>
  </tr>
  
  <tr>
    <td>
      <a href="#users-and-teams">
        Users & teams
      </a>
    </td>
    
    <td>
      Terraform
    </td>
    
    <td>
      User invitations, org roles, team membership, project access
    </td>
  </tr>
  
  <tr>
    <td>
      <a href="#monitors">
        Monitors
      </a>
    </td>
    
    <td>
      Terraform
    </td>
    
    <td>
      Alert thresholds, anomaly detection, MQL queries
    </td>
  </tr>
  
  <tr>
    <td>
      <a href="#notification-channels">
        Notification channels
      </a>
    </td>
    
    <td>
      Terraform
    </td>
    
    <td>
      Slack, PagerDuty, OpsGenie, webhooks, and more
    </td>
  </tr>
  
  <tr>
    <td>
      <a href="#dashboards">
        Dashboards
      </a>
    </td>
    
    <td>
      YAML
    </td>
    
    <td>
      Chart layouts, metric queries, table views, bundled monitors
    </td>
  </tr>
  
  <tr>
    <td>
      <a href="#transformations">
        Transformations
      </a>
    </td>
    
    <td>
      YAML
    </td>
    
    <td>
      Attribute renaming, sampling rules, drop filters
    </td>
  </tr>
</tbody>
</table>

## Why observability as code

- **Reproducibility** — replicate your monitoring setup across environments from the same source files
- **Version control** — every change is tracked in git history with author and rationale
- **Portability** — copy a working setup from staging to production in seconds
- **Collaboration** — the whole team can review and propose changes via pull requests
- **Automation** — integrate with CI/CD pipelines using `terraform plan` and `terraform apply`

## Terraform provider

Install the [Uptrace Terraform provider](https://registry.terraform.io/providers/uptrace/uptrace/latest) and configure it with your API endpoint and token:

```hcl
terraform {
  required_providers {
    uptrace = {
      source = "uptrace/uptrace"
    }
  }
}

provider "uptrace" {
  endpoint = "https://api.uptrace.dev"
  token    = var.uptrace_token
}

variable "uptrace_token" {
  type      = string
  sensitive = true
}
```

The provider also reads from `UPTRACE_ENDPOINT` and `UPTRACE_TOKEN` environment variables. For self-hosted Uptrace, point `endpoint` to your instance (e.g., `http://localhost:14318`).

### Organizations and projects

Create an organization, a project with a 30-day span retention policy, and an ingest token:

```hcl
resource "uptrace_org" "main" {
  name = "My Organization"
}

resource "uptrace_project" "api" {
  org_id         = uptrace_org.main.id
  name           = "api"
  span_retention = "30d"
}

resource "uptrace_project_token" "api" {
  project_id = uptrace_project.api.id
  name       = "default"
}

output "project_dsn" {
  description = "Ingest DSN — pass this to your OpenTelemetry SDK"
  value       = uptrace_project_token.api.dsn
  sensitive   = true
}
```

Projects support per-signal retention and query time-range defaults:

<table>
<thead>
  <tr>
    <th>
      Attribute
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        span_retention
      </code>
    </td>
    
    <td>
      Data retention for spans (e.g. <code>
        30d
      </code>
      
      , <code>
        2w
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        log_retention
      </code>
    </td>
    
    <td>
      Data retention for logs
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        event_retention
      </code>
    </td>
    
    <td>
      Data retention for events
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        metric_retention
      </code>
    </td>
    
    <td>
      Data retention for metrics
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        span_time_range
      </code>
    </td>
    
    <td>
      Default query time range for spans
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        log_time_range
      </code>
    </td>
    
    <td>
      Default query time range for logs
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        event_time_range
      </code>
    </td>
    
    <td>
      Default query time range for events
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        group_by_env
      </code>
    </td>
    
    <td>
      Group spans by environment
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        group_funcs_by_service
      </code>
    </td>
    
    <td>
      Group functions by service
    </td>
  </tr>
</tbody>
</table>

### Users and teams

Invite users, assign organization roles, and manage team-based project access:

```hcl
resource "uptrace_user" "alice" {
  email = "alice@example.com"
}

resource "uptrace_org_user" "alice" {
  org_id  = uptrace_org.main.id
  user_id = uptrace_user.alice.id
  role    = "admin"
}

resource "uptrace_team" "platform" {
  org_id = uptrace_org.main.id
  name   = "Platform"
}

resource "uptrace_team_user" "alice_platform" {
  org_id      = uptrace_org.main.id
  team_id     = uptrace_team.platform.id
  org_user_id = uptrace_org_user.alice.id
}

resource "uptrace_team_project" "platform_api" {
  org_id     = uptrace_org.main.id
  team_id    = uptrace_team.platform.id
  project_id = uptrace_project.api.id
}
```

Available organization roles: `owner`, `admin`, `member`, `viewer`, `billing_manager`, `collaborator`.

### Monitors

Define metric monitors with manual thresholds or automatic anomaly detection:

```hcl
resource "uptrace_metric_monitor" "error_rate" {
  project_id                = uptrace_project.api.id
  name                      = "High error rate"
  notify_everyone_by_email  = true

  params = {
    metrics = [
      { name = "uptrace_tracing_spans", alias = "$spans" }
    ]
    query  = "_error_rate | where _type = \"httpserver\" | group by service_name"
    column = { name = "_error_rate" }

    num_eval_points = 5
    detector = {
      manual = {
        max_value = 0.05
      }
    }
  }
}

resource "uptrace_metric_monitor" "latency_anomaly" {
  project_id = uptrace_project.api.id
  name       = "HTTP latency spike"

  params = {
    metrics = [
      { name = "uptrace_tracing_spans", alias = "$http_duration" }
    ]
    query  = "avg($http_duration)"
    column = { name = "avg($http_duration)", unit = "milliseconds" }

    detector = {
      auto = {
        tolerance       = "medium"
        training_period = 86400000
      }
    }
  }
}
```

Error monitors watch for anomalies in log and span data:

```hcl
resource "uptrace_error_monitor" "log_errors" {
  project_id = uptrace_project.api.id
  name       = "Notify on all errors"

  params = {
    metrics = [
      { name = "uptrace_tracing_logs", alias = "$logs" }
    ]
    query = "sum($logs) | where _system in (\"log:error\", \"log:fatal\")"
  }
}
```

### Notification channels

Route alerts to Slack, PagerDuty, OpsGenie, Telegram, Microsoft Teams, Google Chat, Mattermost, Pushover, ServiceNow, incident.io, Alertmanager, and generic webhooks:

```hcl
resource "uptrace_slack_channel" "alerts" {
  project_id  = uptrace_project.api.id
  name        = "slack-alerts"
  priorities  = ["high", "medium"]
  auth_method = "webhook"
  webhook_url = "https://hooks.slack.com/services/T00/B00/XXXX"
}

resource "uptrace_pagerduty_channel" "oncall" {
  project_id  = uptrace_project.api.id
  name        = "pagerduty-oncall"
  priorities  = ["high"]
  routing_key = var.pagerduty_routing_key
  severity    = "critical"
}

resource "uptrace_webhook_channel" "custom" {
  project_id = uptrace_project.api.id
  name       = "custom-webhook"
  priorities = ["high", "medium", "low"]
  url        = "https://example.com/hooks/alert"
}
```

Each channel can filter alerts by priority and optionally restrict to specific monitors:

```hcl
resource "uptrace_slack_channel" "critical_only" {
  project_id  = uptrace_project.api.id
  name        = "critical-errors"
  priorities  = ["high"]
  auth_method = "webhook"
  webhook_url = "https://hooks.slack.com/services/T00/B00/XXXX"

  match_all   = false
  monitor_ids = [uptrace_error_monitor.log_errors.id]
}
```

### Importing existing resources

Import resources already created in the UI into Terraform state:

```bash
terraform import uptrace_org.main <org_id>
terraform import uptrace_project.api <project_id>
terraform import uptrace_project_token.api <project_id>:<token_id>
terraform import uptrace_metric_monitor.error_rate <project_id>:<monitor_id>
terraform import uptrace_slack_channel.alerts <project_id>:<channel_id>
```

## Dashboards

Dashboard templates are YAML files. Uptrace activates them automatically when matching metrics arrive. You can export any dashboard as YAML, modify it, and import it across environments via the Uptrace UI or [MCP Server](/features/mcp).

```yaml
schema: v2
name: 'API Service: Overview'
tags: [otel, app]
version: v26.01.01

table:
  metrics:
    - http_server_request_duration as $dur
  query:
    - group by service_name
    - p99($dur) as p99_ms

grid_sections:
  - title: Latency
    items:
      - title: p99 latency by service
        metrics:
          - http_server_request_duration as $dur
        query:
          - p99($dur) group by service_name
```

Alert monitors can be bundled directly inside dashboard templates so dashboards and their alerts ship together as one file. See [Dashboard YAML Templates](/features/dashboards/yaml) for the full schema.

## Transformations

Transformation rules are YAML operations applied to every incoming span, log, or metric before storage. Keeping them in files makes your data pipeline auditable and reproducible.

Drop noisy spans before they reach storage:

```yaml
name: Drop healthcheck spans
scope: [spans]
type: drop
if: spanName() == "GET /healthz" && spanStatusCode() != "error"
```

Sample successful requests while keeping all errors:

```yaml
name: Sample 10% of successful requests
scope: [spans]
type: sample
fraction: 0.1
if: spanStatusCode() != "error"
```

See [Transformations](/features/transformations) for all available operations.

## A complete workflow

A typical project layout with Uptrace config in git:

```text
my-repo/
└── uptrace/
    ├── main.tf                # provider + org + projects
    ├── monitors.tf            # metric and error monitors
    ├── channels.tf            # notification channels
    ├── teams.tf               # users, teams, project access
    ├── variables.tf           # tokens, webhook URLs
    ├── outputs.tf             # project DSNs
    ├── dashboards/
    │   ├── api-service.yml    # dashboard + bundled monitors
    │   └── worker-service.yml
    └── transformations/
        ├── drop-healthchecks.yml
        └── sample-success.yml
```

Applying the config to a new environment:

1. **terraform apply** — creates orgs, projects, tokens, monitors, channels, and team access
2. Import dashboard YAML files via **Metrics → Dashboards → Import**
3. Apply transformation rules via **Project → Transformations → New Operation → From YAML**

Steps 2 and 3 can be automated using the [MCP Server](/features/mcp):

```sh
claude mcp add --transport http uptrace https://<your-uptrace-host>/mcp/<project_id> \
  --header "Authorization: Bearer <your-user-token>"
```

Once connected, an AI assistant can apply your dashboard YAML files directly using `create_dashboard_from_template` and `update_dashboard_from_template` tools.

## Self-hosted: seed data

For self-hosted Uptrace, define initial users and projects directly in `uptrace.yml`:

```yaml
seed_data:
  orgs:
    - key: my_org
      name: My Organization
  projects:
    - key: production
      name: Production
      org: my_org
    - key: staging
      name: Staging
      org: my_org
```

The Terraform provider works with both Cloud and self-hosted Uptrace. For self-hosted, set `endpoint` to your instance URL.

## Related

- [Alerting](/features/alerting) — monitor types and notification channel options
- [Dashboard YAML Templates](/features/dashboards/yaml) — full YAML schema for dashboards
- [Transformations](/features/transformations) — all available transformation operations
- [MCP Server](/features/mcp) — automate dashboard management from AI assistants
- [Data fixtures](/features/fixtures) — seed users and projects for self-hosted instances
