Observability as Code

Uptrace lets you define your observability setup as code. Organizations, projects, alert monitors, notification channels, and team access can all be managed with Terraform using the official Uptrace provider — making your monitoring setup reproducible, auditable, and consistent across environments.

Dashboards and data transformation rules are managed as YAML files, imported via the Uptrace UI or MCP Server.

Why Observability as Code

When your observability config lives in code rather than UI state:

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

What you can manage as code

ComponentMethodWhat it controls
Organizations & projectsTerraformOrgs, projects, retention policies, ingest tokens
Users & teamsTerraformUser invitations, org roles, team membership, project access
MonitorsTerraformAlert thresholds, anomaly detection, MQL queries
Notification channelsTerraformSlack, PagerDuty, OpsGenie, webhooks, and more
DashboardsYAMLChart layouts, metric queries, table views, bundled monitors
TransformationsYAMLAttribute renaming, sampling rules, drop filters

Terraform provider

Install the Uptrace Terraform provider 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:

AttributeDescription
span_retentionData retention for spans (e.g. 30d, 2w)
log_retentionData retention for logs
event_retentionData retention for events
metric_retentionData retention for metrics
span_time_rangeDefault query time range for spans
log_time_rangeDefault query time range for logs
event_time_rangeDefault query time range for events
group_by_envGroup spans by environment
group_funcs_by_serviceGroup functions by service

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 trend 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 your team's tools. The provider supports 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 using monitor_ids:

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.

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 for the full schema.

Transformations

Transformation rules are YAML operations applied to every incoming span, log, or metric before storage. Keeping them in files means your data pipeline is 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 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

Step 1 is fully automated and can run in CI/CD pipelines. Dashboards and transformations (steps 2–3) are applied through the UI or via the MCP Server.

Automating dashboards via MCP

If you use AI assistants (Claude, Cursor, Continue), Uptrace's built-in MCP Server exposes dashboard management as tools — no UI required. You can create, update, and delete dashboards programmatically from your AI assistant using the same YAML format.

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:

MCP ToolWhat it does
create_dashboard_from_templateCreate a new dashboard from YAML
update_dashboard_from_templateUpdate an existing dashboard from YAML
get_dashboard_yamlExport a dashboard as YAML
delete_dashboardDelete a dashboard by ID

See MCP Server for setup instructions.

Self-hosted: seed data

For self-hosted Uptrace, you can also 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 setups, set the endpoint to your instance URL.