Terraform Provider Reference
Overview
The openstatus Terraform provider enables you to manage your monitoring infrastructure programmatically using HashiCorp Terraform. Define monitors, notification channels, and status pages as code — with full support for version control, automated deployments, and IaC workflows.
Key capabilities:
- Manage HTTP, TCP, and DNS monitors with assertions
- Configure notification channels (Slack, PagerDuty, email, webhooks, and more)
- Create and manage status pages with component groups
- Import existing resources into Terraform state
Installation
Declare the provider in your Terraform configuration. Terraform will automatically download it when you run terraform init.
terraform {
required_providers {
openstatus = {
source = "openstatusHQ/openstatus"
version = "~> 0.1"
}
}
}
For the latest version, refer to the Terraform Registry.
Provider Configuration
provider "openstatus" {
api_token = "YOUR_OPENSTATUS_API_TOKEN" # or set OPENSTATUS_API_TOKEN env var
}
| Argument | Type | Required | Description |
|---|---|---|---|
api_token | string | Yes | Your openstatus API token. Can also be set via the OPENSTATUS_API_TOKEN environment variable. |
base_url | string | No | Base URL for the openstatus API. Defaults to https://api.openstatus.dev/rpc. |
Resources
openstatus_http_monitor
Manages an HTTP monitor with support for custom headers, request bodies, and response assertions.
Arguments:
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | Monitor name (max 256 chars). | |
url | string | Yes | URL to monitor (max 2048 chars). | |
periodicity | string | Yes | Check frequency: 30s, 1m, 5m, 10m, 30m, 1h. | |
method | string | No | "GET" | HTTP method: GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS. |
body | string | No | "" | Request body for POST, PUT, PATCH methods. |
timeout | number | No | 45000 | Timeout in milliseconds (0–120000). |
degraded_at | number | No | computed | Response time threshold (ms) after which the monitor is considered degraded. |
retry | number | No | 3 | Number of retries on failure (0–10). |
follow_redirects | bool | No | true | Whether to follow HTTP redirects. |
active | bool | No | false | Whether the monitor is active. |
public | bool | No | false | Whether the monitor is visible on your public status page. |
description | string | No | Monitor description (max 1024 chars). | |
regions | set(string) | No | Regions to monitor from. See Available Regions. |
Blocks:
-
headers(max 20) — Custom HTTP headers to include with each request.key(string, required) — Header name.value(string, required) — Header value.
-
status_code_assertions(max 10) — Assert on response status codes.target(number, required) — Expected status code (100–599).comparator(string, required) — One of:eq,neq,gt,gte,lt,lte.
-
body_assertions(max 10) — Assert on response body content.target(string, required) — Expected value.comparator(string, required) — One of:contains,not_contains,eq,neq,empty,not_empty,gt,gte,lt,lte.
-
header_assertions(max 10) — Assert on response headers.key(string, required) — Header name to assert on.target(string, required) — Expected value.comparator(string, required) — Same comparators asbody_assertions.
Read-only attributes: id (string), status (string: active, degraded, error, unknown).
Example — Basic health check:
resource "openstatus_http_monitor" "website" {
name = "Website Availability"
url = "https://www.example.com"
periodicity = "1m"
active = true
public = true
regions = ["fly-iad", "fly-ams", "fly-syd"]
}
Example — API monitor with assertions and headers:
resource "openstatus_http_monitor" "api" {
name = "API Health Check"
description = "Monitors the /health endpoint with full assertions."
url = "https://api.example.com/health"
periodicity = "5m"
method = "GET"
timeout = 30000
active = true
regions = ["fly-iad", "fly-ams", "fly-nrt"]
headers {
key = "Authorization"
value = "Bearer ${var.api_token}"
}
headers {
key = "Accept"
value = "application/json"
}
status_code_assertions {
target = 200
comparator = "eq"
}
body_assertions {
target = "ok"
comparator = "contains"
}
header_assertions {
key = "Content-Type"
target = "application/json"
comparator = "contains"
}
}
Example — POST monitor with request body:
resource "openstatus_http_monitor" "webhook" {
name = "Webhook Endpoint"
url = "https://api.example.com/webhooks/health"
periodicity = "10m"
method = "POST"
active = true
regions = ["fly-iad"]
headers {
key = "Content-Type"
value = "application/json"
}
body = jsonencode({
type = "health_check"
})
status_code_assertions {
target = 202
comparator = "eq"
}
}
Import:
terraform import openstatus_http_monitor.website <monitor_id>
openstatus_tcp_monitor
Manages a TCP connection monitor to verify that a port is open and reachable.
Arguments:
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | Monitor name (max 256 chars). | |
uri | string | Yes | Target in host:port format (max 2048 chars). | |
periodicity | string | Yes | Check frequency: 30s, 1m, 5m, 10m, 30m, 1h. | |
timeout | number | No | 45000 | Timeout in milliseconds (0–120000). |
degraded_at | number | No | computed | Degradation threshold in milliseconds. |
retry | number | No | 3 | Number of retries on failure (0–10). |
active | bool | No | false | Whether the monitor is active. |
public | bool | No | false | Whether the monitor is publicly visible. |
description | string | No | Monitor description (max 1024 chars). | |
regions | set(string) | No | Regions to monitor from. See Available Regions. |
Read-only attributes: id (string), status (string).
Example — Database port check:
resource "openstatus_tcp_monitor" "database" {
name = "PostgreSQL Port Check"
description = "Ensures the database port is open and reachable."
uri = "db.example.com:5432"
periodicity = "1m"
timeout = 10000
active = true
regions = ["fly-iad", "fly-fra"]
}
Example — Redis monitor:
resource "openstatus_tcp_monitor" "redis" {
name = "Redis Connection"
uri = "redis.example.com:6379"
periodicity = "30s"
active = true
regions = ["fly-iad", "fly-ams", "fly-nrt"]
}
Import:
terraform import openstatus_tcp_monitor.database <monitor_id>
openstatus_dns_monitor
Manages a DNS monitor with support for record type assertions.
Arguments:
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | Monitor name (max 256 chars). | |
uri | string | Yes | Domain name to monitor (max 2048 chars). | |
periodicity | string | Yes | Check frequency: 30s, 1m, 5m, 10m, 30m, 1h. | |
timeout | number | No | 45000 | Timeout in milliseconds (0–120000). |
degraded_at | number | No | computed | Degradation threshold in milliseconds. |
retry | number | No | 3 | Number of retries on failure (0–10). |
active | bool | No | false | Whether the monitor is active. |
public | bool | No | false | Whether the monitor is publicly visible. |
description | string | No | Monitor description (max 1024 chars). | |
regions | set(string) | No | Regions to monitor from. See Available Regions. |
Blocks:
record_assertions(max 10) — Assert on DNS record values.record(string, required) — DNS record type:A,AAAA,CNAME,MX,TXT.target(string, required) — Expected value.comparator(string, required) — One of:eq,neq,contains,not_contains.
Read-only attributes: id (string), status (string).
Example — A record validation:
resource "openstatus_dns_monitor" "main_domain" {
name = "DNS A Record Check"
description = "Verifies that example.com resolves to the correct IP."
uri = "example.com"
periodicity = "10m"
active = true
regions = ["fly-iad", "fly-ams"]
record_assertions {
record = "A"
comparator = "eq"
target = "93.184.216.34"
}
}
Example — MX record validation:
resource "openstatus_dns_monitor" "email" {
name = "Email MX Record Check"
uri = "example.com"
periodicity = "30m"
active = true
regions = ["fly-iad"]
record_assertions {
record = "MX"
comparator = "contains"
target = "mail.example.com"
}
}
Import:
terraform import openstatus_dns_monitor.main_domain <monitor_id>
openstatus_notification
Manages a notification channel. Supports 12 provider types: Discord, Email, Slack, PagerDuty, OpsGenie, Webhook, Telegram, SMS, WhatsApp, Google Chat, Grafana OnCall, and ntfy.
Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
name | string | No | Notification channel name. |
provider_type | string | Yes | Provider type (see supported values below). |
monitor_ids | set(string) | No | Set of monitor IDs to associate with this notification. |
Supported provider_type values: discord, email, slack, pagerduty, opsgenie, webhook, telegram, sms, whatsapp, google_chat, grafana_oncall, ntfy.
Provider-specific blocks — use exactly one block matching your provider_type:
| Block | Arguments |
|---|---|
discord | webhook_url (string, required, sensitive) |
email | email (string, required) |
slack | webhook_url (string, required, sensitive) |
pagerduty | integration_key (string, required, sensitive) |
opsgenie | api_key (string, required, sensitive), region (string, required: us or eu) |
webhook | endpoint (string, required), headers (optional list of key/value objects) |
telegram | chat_id (string, required) |
sms | phone_number (string, required) |
whatsapp | phone_number (string, required) |
google_chat | webhook_url (string, required, sensitive) |
grafana_oncall | webhook_url (string, required, sensitive) |
ntfy | topic (string, required), server_url (string, optional), token (string, optional, sensitive) |
Read-only attributes: id (string), created_at (string), updated_at (string).
Example — Slack notification:
resource "openstatus_notification" "slack_alerts" {
name = "Slack Alerts"
provider_type = "slack"
monitor_ids = [openstatus_http_monitor.api.id]
slack {
webhook_url = var.slack_webhook_url
}
}
Example — PagerDuty notification:
resource "openstatus_notification" "pagerduty" {
name = "PagerDuty Escalation"
provider_type = "pagerduty"
monitor_ids = [
openstatus_http_monitor.api.id,
openstatus_tcp_monitor.database.id,
]
pagerduty {
integration_key = var.pagerduty_key
}
}
Example — Email notification:
resource "openstatus_notification" "email" {
name = "On-Call Email"
provider_type = "email"
monitor_ids = [openstatus_http_monitor.api.id]
email {
email = "oncall@example.com"
}
}
Example — Custom webhook:
resource "openstatus_notification" "custom_webhook" {
name = "Custom Webhook"
provider_type = "webhook"
webhook {
endpoint = "https://api.example.com/alerts"
headers {
key = "Authorization"
value = "Bearer ${var.webhook_token}"
}
headers {
key = "Content-Type"
value = "application/json"
}
}
}
Example — Discord notification:
resource "openstatus_notification" "discord" {
name = "Discord Alerts"
provider_type = "discord"
discord {
webhook_url = var.discord_webhook_url
}
}
Import:
terraform import openstatus_notification.slack_alerts <notification_id>
openstatus_status_page
Manages a status page with access control and branding.
Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Page title (1–256 chars). |
slug | string | Yes | URL slug (1–256 chars). Used in the status page URL. |
description | string | No | Page description (max 1024 chars). |
homepage_url | string | No | Link to your homepage. |
contact_url | string | No | Link to your contact page. |
icon | string | No | URL of the icon to display. |
custom_domain | string | No | Custom domain (DNS must point to openstatus first). |
access_type | string | No | Access control: public, password, or email-domain. |
password | string | No | Required when access_type is password. Sensitive. |
auth_email_domains | list(string) | No | Required when access_type is email-domain. |
Read-only attributes: id (string), published (bool), theme (string: system, light, dark), created_at (string), updated_at (string).
Example — Public status page:
resource "openstatus_status_page" "main" {
title = "Example Inc. Status"
slug = "example-status"
description = "Real-time status for all Example Inc. services."
homepage_url = "https://example.com"
contact_url = "https://example.com/support"
}
Example — Password-protected status page:
resource "openstatus_status_page" "internal" {
title = "Internal Status"
slug = "internal-status"
description = "Status page for internal services."
access_type = "password"
password = var.status_page_password
}
Example — Email-domain restricted status page:
resource "openstatus_status_page" "company" {
title = "Company Status"
slug = "company-status"
access_type = "email-domain"
auth_email_domains = ["example.com", "subsidiary.com"]
}
Import:
terraform import openstatus_status_page.main <page_id>
openstatus_status_page_component
Manages a component on a status page. Components can be linked to a monitor or be static.
Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
page_id | string | Yes | Status page ID this component belongs to. Forces replacement if changed. |
type | string | Yes | Component type: monitor or static. Forces replacement if changed. |
monitor_id | string | No | Required when type is monitor. The monitor ID to display. |
name | string | No | Component display name (max 256 chars). |
description | string | No | Component description (max 1024 chars). |
order | number | No | Display order on the status page. |
group_id | string | No | Component group ID this belongs to. |
group_order | number | No | Display order within its group. |
Read-only attributes: id (string), created_at (string), updated_at (string).
Example — Monitor component:
resource "openstatus_status_page_component" "api" {
page_id = openstatus_status_page.main.id
type = "monitor"
monitor_id = openstatus_http_monitor.api.id
name = "API"
order = 1
}
Example — Static component:
resource "openstatus_status_page_component" "third_party" {
page_id = openstatus_status_page.main.id
type = "static"
name = "Third-party Services"
description = "Status of external dependencies."
order = 2
}
Import:
terraform import openstatus_status_page_component.api <page_id>/<component_id>
openstatus_status_page_component_group
Manages a component group on a status page, allowing you to organize components visually.
Arguments:
| Argument | Type | Required | Description |
|---|---|---|---|
page_id | string | Yes | Status page ID this group belongs to. Forces replacement if changed. |
name | string | Yes | Group name (1–256 chars). |
Read-only attributes: id (string), created_at (string), updated_at (string).
Example:
resource "openstatus_status_page_component_group" "infrastructure" {
page_id = openstatus_status_page.main.id
name = "Infrastructure"
}
resource "openstatus_status_page_component_group" "applications" {
page_id = openstatus_status_page.main.id
name = "Applications"
}
Import:
terraform import openstatus_status_page_component_group.infrastructure <page_id>/<group_id>
Data Sources
openstatus_monitor
Look up a single monitor by ID. Works for HTTP, TCP, and DNS monitors.
data "openstatus_monitor" "existing" {
id = "123"
}
output "monitor_name" {
value = data.openstatus_monitor.existing.name
}
output "monitor_type" {
value = data.openstatus_monitor.existing.type
}
Computed attributes: type (http, tcp, dns), name, url (HTTP only), uri (TCP/DNS only), periodicity, method (HTTP only), active, public, description, timeout, status.
openstatus_monitors
List all monitors with pagination.
data "openstatus_monitors" "all" {
limit = 100
offset = 0
}
output "total_monitors" {
value = length(data.openstatus_monitors.all.monitors)
}
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number | No | 50 | Max results (1–100). |
offset | number | No | 0 | Pagination offset. |
Computed: monitors — list of objects with id, name, type.
openstatus_notification
Look up a notification channel by ID.
data "openstatus_notification" "existing" {
id = "456"
}
output "notification_provider" {
value = data.openstatus_notification.existing.provider_type
}
Computed attributes: name, provider_type, monitor_ids, created_at, updated_at.
openstatus_status_page
Look up a status page by ID.
data "openstatus_status_page" "existing" {
id = "789"
}
output "status_page_url" {
value = data.openstatus_status_page.existing.slug
}
Computed attributes: title, slug, description, homepage_url, contact_url, icon, custom_domain, published, access_type, password (sensitive), auth_email_domains, theme, created_at, updated_at.
Available Regions
Monitors can run from any of the following 28 regions:
Fly.io:
fly-ams, fly-arn, fly-bom, fly-cdg, fly-dfw, fly-ewr, fly-fra, fly-gru, fly-iad, fly-jnb, fly-lax, fly-lhr, fly-nrt, fly-ord, fly-sjc, fly-sin, fly-syd, fly-yyz
Koyeb:
koyeb-fra, koyeb-par, koyeb-sfo, koyeb-sin, koyeb-tyo, koyeb-was
Railway:
railway-us-west2, railway-us-east4, railway-europe-west4, railway-asia-southeast1
Full End-to-End Example
This example sets up a complete monitoring stack: HTTP and TCP monitors, Slack notifications, and a public status page with grouped components.
terraform {
required_providers {
openstatus = {
source = "openstatusHQ/openstatus"
version = "~> 0.1"
}
}
}
provider "openstatus" {
api_token = var.openstatus_api_token
}
# --- Variables ---
variable "openstatus_api_token" {
type = string
sensitive = true
}
variable "slack_webhook_url" {
type = string
sensitive = true
}
# --- Monitors ---
resource "openstatus_http_monitor" "api" {
name = "API Health"
description = "Monitors the main API health endpoint."
url = "https://api.example.com/health"
periodicity = "5m"
method = "GET"
timeout = 30000
active = true
public = true
regions = ["fly-iad", "fly-ams", "fly-nrt"]
status_code_assertions {
target = 200
comparator = "eq"
}
body_assertions {
target = "ok"
comparator = "contains"
}
}
resource "openstatus_http_monitor" "website" {
name = "Website"
url = "https://www.example.com"
periodicity = "1m"
active = true
public = true
regions = ["fly-iad", "fly-ams", "fly-syd"]
status_code_assertions {
target = 200
comparator = "eq"
}
}
resource "openstatus_tcp_monitor" "database" {
name = "PostgreSQL"
uri = "db.example.com:5432"
periodicity = "1m"
timeout = 10000
active = true
regions = ["fly-iad"]
}
resource "openstatus_dns_monitor" "domain" {
name = "DNS Resolution"
uri = "example.com"
periodicity = "10m"
active = true
regions = ["fly-iad", "fly-ams"]
record_assertions {
record = "A"
comparator = "eq"
target = "93.184.216.34"
}
}
# --- Notifications ---
resource "openstatus_notification" "slack" {
name = "Slack Alerts"
provider_type = "slack"
monitor_ids = [
openstatus_http_monitor.api.id,
openstatus_http_monitor.website.id,
openstatus_tcp_monitor.database.id,
]
slack {
webhook_url = var.slack_webhook_url
}
}
# --- Status Page ---
resource "openstatus_status_page" "main" {
title = "Example Inc. Status"
slug = "example-status"
description = "Real-time status for all Example Inc. services."
homepage_url = "https://example.com"
contact_url = "https://example.com/support"
}
resource "openstatus_status_page_component_group" "web" {
page_id = openstatus_status_page.main.id
name = "Web Services"
}
resource "openstatus_status_page_component_group" "infra" {
page_id = openstatus_status_page.main.id
name = "Infrastructure"
}
resource "openstatus_status_page_component" "api_component" {
page_id = openstatus_status_page.main.id
type = "monitor"
monitor_id = openstatus_http_monitor.api.id
name = "API"
group_id = openstatus_status_page_component_group.web.id
order = 1
group_order = 1
}
resource "openstatus_status_page_component" "website_component" {
page_id = openstatus_status_page.main.id
type = "monitor"
monitor_id = openstatus_http_monitor.website.id
name = "Website"
group_id = openstatus_status_page_component_group.web.id
order = 1
group_order = 2
}
resource "openstatus_status_page_component" "db_component" {
page_id = openstatus_status_page.main.id
type = "monitor"
monitor_id = openstatus_tcp_monitor.database.id
name = "Database"
group_id = openstatus_status_page_component_group.infra.id
order = 2
group_order = 1
}
Related Resources
- HTTP Monitor Reference — Detailed specification for HTTP monitor configuration.
- TCP Monitor Reference — Detailed specification for TCP monitor configuration.
- DNS Monitor Reference — Detailed specification for DNS monitor configuration.
- CLI Reference — Manage monitors using the openstatus command-line interface.
- Terraform Registry — Official provider page with version history.
- Provider Source Code — GitHub repository for the Terraform provider.