Skip to content

Terraform Provider

The official okstatus Terraform provider lets you manage monitors, monitor groups, incidents, status pages, alert channels, and maintenance windows as code — useful for provisioning monitoring alongside the infrastructure it watches, or keeping a fleet of monitors in version control instead of clicking through the dashboard.

The provider is published on the Terraform Registry and its full resource/data-source reference lives there. This guide only covers getting started — see the registry docs for every field.

terraform {
required_providers {
okstatus = {
source = "clearcloud-fr/okstatus"
version = "~> 1.6"
}
}
}
provider "okstatus" {
# api_key can also come from the OKSTATUS_API_KEY environment variable.
# api_key = "oks_live_..."
}

Prefer the environment variable over hardcoding the key in your .tf files:

Terminal window
export OKSTATUS_API_KEY="oks_live_..."
resource "okstatus_monitor" "api" {
name = "Production API"
url = "https://api.example.com/health"
type = "http"
interval = 60
regions = ["fr-dc3", "de-fra", "es-mad"]
accepted_status_codes = ["200-299"]
tags = ["prod", "payments"]
keyword = "\"status\":\"ok\""
follow_redirects = false
}

Which regions values are actually allowed depends on your plan — a region your plan doesn’t include fails at apply with a 400, not at plan. See Regions and intervals for the per-plan list.

An HTTP check can send its own request — method, body and headers:

resource "okstatus_monitor" "webhook" {
name = "Order webhook"
url = "https://api.example.com/orders/ping"
http_method = "POST"
http_body = jsonencode({ ping = true })
http_body_encoding = "json"
http_headers = { Authorization = "Bearer ${var.probe_token}" }
}

http_headers is sensitive and write-only: OKStatus never returns request headers, so Terraform sends them when the attribute changes and cannot see a header edited in the dashboard. ignore_tls = true accepts a self-signed certificate.

Other check types take their own settings:

resource "okstatus_monitor" "spf" {
name = "SPF record"
url = "example.com"
type = "dns"
dns_record_type = "TXT"
dns_expected_value = "v=spf1"
}
resource "okstatus_monitor" "database" {
name = "Database port"
url = "db.example.com"
type = "tcp"
port = 5432
}

type takes every check type of OKStatus except AI models (which have okstatus_llm_monitor): grpc, push, group, postgres, mysql, mongodb, smtp, imap and redis.

resource "okstatus_monitor" "orders_db" {
name = "Orders database"
url = "postgres://db.example.com:5432/orders"
type = "postgres"
db_query = "SELECT 1"
basic_auth_user = "okstatus_ro"
basic_auth_password = var.orders_db_password
}
resource "okstatus_monitor" "backup" {
name = "Nightly backup"
url = "nightly-backup"
type = "push"
interval = 86400
grace_period = 3600
}

A push monitor exposes its push_token (sensitive) — the token of the ping URL your job calls. Changing type or grace_period replaces the monitor.

resource "okstatus_monitor" "partner_api" {
name = "Partner API"
url = "https://partner.example.com/health"
auth = {
type = "oauth2"
token_url = "https://login.example.com/oauth/token"
client_id = "okstatus-probe"
client_secret = var.partner_client_secret
}
}

auth.type is bearer, apikey, cookie, oauth2 or mtls — see Authentication for what each one does. Basic auth, and the account of a database, SMTP, IMAP or Redis monitor, go in basic_auth_user / basic_auth_password.

Credentials are write-only: OKStatus never returns them, so Terraform sends them when they change and cannot see an edit made in the dashboard. Removing them from the configuration removes them from the monitor. Never write user:password@ in url — the provider refuses it.

resource "okstatus_monitor" "staging" {
name = "Staging"
url = "https://staging.example.com"
paused = true # no checks
muted = true # checks, but no alerts
}

tags, paused, muted, keyword, follow_redirects, port, the HTTP request options and the DNS settings have no default. An attribute you do not write keeps the value the monitor already has, including one set in the dashboard — so adding the provider to existing monitors, or upgrading it, never resets them. To remove a value, say so: tags = [], keyword = "", paused = false.

Alert channel config uses the backend’s own field names (camelCase), not Terraform’s usual snake_case — see the okstatus_alert_channel reference for the shape of every channel type.

resource "okstatus_alert_channel" "team_discord" {
type = "discord"
config = {
webhookUrl = "https://discord.com/api/webhooks/..."
}
}
resource "okstatus_monitor" "checkout" {
name = "Checkout"
url = "https://shop.example.com/checkout/health"
alert_channel_ids = [okstatus_alert_channel.team_discord.id]
}

alert_channel_ids is the complete list of the monitor’s channels; [] detaches them all. Without the attribute, Terraform does not manage the monitor’s channels at all. To attach a channel created in the dashboard, look it up with data.okstatus_alert_channels.

resource "okstatus_monitor_group" "payments" {
name = "Payments"
description = "Everything on the checkout path"
monitor_ids = [
okstatus_monitor.api.id,
okstatus_monitor.checkout.id,
]
}

A monitor belongs to one group at most, and monitor_ids is the group’s complete membership. Destroying the group keeps its monitors.

A group can carry alert_channel_ids of its own: a monitor that joins the group gets them attached. If you also set alert_channel_ids on that monitor, repeat the group’s channels there — otherwise the next apply detaches them from the monitor.

Private agents are enrolled from the dashboard. Terraform reads them and gives you the region value that runs a monitor on one of them:

data "okstatus_private_agents" "all" {}
locals {
paris_agent = one([for a in data.okstatus_private_agents.all.agents : a if a.name == "dc-paris"])
}
resource "okstatus_monitor" "intranet" {
name = "Intranet"
url = "https://intranet.example.com"
regions = [local.paris_agent.region]
}

regions = ["private"] lets any of your agents run the check.

Resource / data sourceManages
okstatus_monitorEvery monitor type except AI models, with credentials and alert channels
okstatus_llm_monitorAI model (LLM) monitors (Max and above)
okstatus_monitor_groupMonitor groups (the folders of the Monitors page)
okstatus_alert_channelEmail, Discord, webhook, Slack, Telegram, and more
okstatus_incidentIncidents and their timestamped updates
okstatus_maintenanceScheduled maintenance windows
okstatus_status_pagePublic status pages
data.okstatus_monitorsLook up existing monitors (e.g. to reference by name)
data.okstatus_monitor_groupsLook up existing groups and their monitors
data.okstatus_alert_channelsLook up existing alert channels
data.okstatus_private_agentsYour private agents and the region value of each
data.okstatus_orgRead the current organization’s plan and settings

slo_target on okstatus_monitor and okstatus_monitor_group sets an SLO (Max and Enterprise). Terraform manages the objective; the budget is read with the API (GET /api/v1/slos) or the MCP connector (list_slos).

Not managed by Terraform: enrolling a private agent, and SLA reports.

Full schema, every field, and defaults: registry.terraform.io/providers/clearcloud-fr/okstatus.

Resources created through the dashboard can be brought under Terraform management with terraform import, using the resource’s ID (visible in the dashboard URL or via the REST API):

Terminal window
terraform import okstatus_monitor.api <monitor-id>
terraform import okstatus_monitor_group.payments <group-id>

An imported monitor keeps its tags, keyword and paused state until you write those attributes.