Terraform & OpenTofu
The CuliPulse provider lets you keep your monitors, webhooks and alert routing in code, next to the infrastructure they watch. Review changes in a pull request, apply them with one command, and recreate the same setup in another account whenever you need to.
It works with Terraform 1.5 or newer and OpenTofu 1.6 or newer, and is published on both
registries as culipulse/culipulse:
What you can manage
Section titled “What you can manage”| Name | What it is |
|---|---|
culipulse_http_monitor |
A website or API check, including headers, secret headers, login details, request body and assertions. |
culipulse_heartbeat_monitor |
A heartbeat (dead man’s switch) for cron jobs and backups. Gives you the ping URL to call. |
culipulse_webhook_channel |
A webhook that receives signed alerts. |
culipulse_channel_routing |
Which monitors send alerts to a channel — works for webhook, Slack and Telegram channels. |
culipulse_agents (data source) |
Looks up checkpoints (CuliPulse’s shared ones or your own agents) by region. |
culipulse_channel (data source) |
Finds a Slack or Telegram channel you connected in the app, by name. |
Other monitor types (TCP, UDP, ping, DNS, domain expiry, vendor status) can’t be managed with the provider yet. Keep managing them in the app — the provider leaves them alone.
1. Create an API token
Section titled “1. Create an API token”In the app, go to Settings → API Tokens and generate a token with Read + Write access (see API tokens). A read-only token can plan, but every change will fail.
Give the token to Terraform through an environment variable, so it never ends up in your code:
export CULIPULSE_API_TOKEN="cpk_..."2. Add the provider
Section titled “2. Add the provider”terraform { required_providers { culipulse = { source = "culipulse/culipulse" version = "~> 0.1" } }}
# Uses the CULIPULSE_API_TOKEN environment variable.provider "culipulse" {}Then run terraform init (or tofu init).
3. Describe your monitors
Section titled “3. Describe your monitors”This example checks an API from Singapore, watches a nightly backup, and sends both to your own webhook:
data "culipulse_agents" "singapore" { kind = "first_party" region = "sg"}
resource "culipulse_http_monitor" "api" { name = "API health" url = "https://api.example.com/health" interval_seconds = 300 agent_ids = data.culipulse_agents.singapore.ids expected_status = "200"}
resource "culipulse_heartbeat_monitor" "backup" { name = "Nightly backup" interval_seconds = 86400 grace_seconds = 1800}
resource "culipulse_webhook_channel" "incidents" { name = "Incident webhook" url = "https://hooks.example.com/culipulse"}
resource "culipulse_channel_routing" "incidents" { channel_id = culipulse_webhook_channel.incidents.id monitor_ids = [ culipulse_http_monitor.api.id, culipulse_heartbeat_monitor.backup.id, ]}
# Your backup job calls this URL when it finishes.output "backup_ping_url" { value = culipulse_heartbeat_monitor.backup.ping_url sensitive = true}Run terraform plan to see what will change, then terraform apply. Every attribute is
described in the provider reference.
Bringing existing monitors under Terraform
Section titled “Bringing existing monitors under Terraform”Everything the provider manages can be imported. A monitor’s id is in the address bar when
you open it in the app (.../app/monitors/mon_...). To find a channel’s id (it starts with
nch_), list your channels through the API:
curl -H "Authorization: Bearer $CULIPULSE_API_TOKEN" https://culipulse.dev/v1/channelsterraform import culipulse_http_monitor.api mon_...terraform import culipulse_heartbeat_monitor.backup mon_...terraform import culipulse_webhook_channel.incidents nch_...terraform import culipulse_channel_routing.incidents nch_...A few things can’t be read back from CuliPulse, so check them before your first apply after
an import:
- Secret headers, bearer tokens, login passwords and secret request bodies on an HTTP monitor. If your configuration doesn’t include them, the next apply removes them from the monitor. Add them to your configuration first.
- A webhook’s URL and signing secret. Add the
urlto your configuration; as long as it points at the same host, CuliPulse records it without recreating the webhook. The signing secret can’t be recovered after an import — keep using the one you copied when you created the webhook.
Good to know
Section titled “Good to know”- Secrets are stored in your Terraform state. Secret headers, passwords, tokens, webhook URLs, signing secrets and heartbeat ping URLs are marked sensitive, so they’re hidden in plan output — but they’re saved in the state file. Keep your state somewhere private and encrypted (for example a remote backend with encryption and restricted access).
- Changes to secrets made in the app aren’t detected. CuliPulse never shows secrets again once saved, so Terraform can’t notice if someone changes one in the app.
- Slack and Telegram channels are connected in the app. Both need you to approve the
connection by hand, so Terraform can’t create them. Connect them in the app, then use the
culipulse_channeldata source to route alerts to them. - Changing a webhook’s URL replaces the webhook and gives it a new signing secret. Update
your receiver with the new secret (available as
signing_secret). - Removing a
culipulse_channel_routingresource sets that channel back to receiving alerts from all monitors, which is the app’s default. - Large changes on the Free plan take longer. Each token can make 60 requests a minute on the Free plan (see Rate limits). The provider waits and retries automatically, so a big apply succeeds — it just takes a few minutes.
Getting help
Section titled “Getting help”Found a bug or missing a feature? Open an issue on GitHub or email [email protected].