Terraform Provider Reference
Not yet available on FTStatus
This reference describes a capability that isn't wired up on FTStatus yet. Talk to support if you need it for your account.
The openstatus Terraform provider lets you 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.ftstatus.com/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 FTStatus 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 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.
