# Managing Dokploy with a Terraform provider

> The whole stack behind this site is one Terraform repo - project, environments, apps, domains, DNS - and the goal state is an empty plan.

- date: 2026-08-30
- tags: terraform, dokploy, infra

Everything behind this site is declared in one Terraform repo:
the Dokploy project, a prod and a dev environment, the applications,
their domains, and the Cloudflare DNS records that point at them. One
`terraform plan` shows the whole estate. The goal state is an empty
plan, and reaching it after every change is the entire workflow.

## Two environments, one map

There will always be exactly two environments here, so they live in a
map and a module handles the rest:

```hcl
locals {
  environments = {
    prod = { branch = "master", hosts = ["wihan.dev", "www.wihan.dev"] }
    dev  = { branch = "dev",    hosts = ["dev.wihan.dev"] }
  }
}

module "app" {
  source   = "./modules/web-service"
  for_each = local.environments

  name     = "wihan-dev-app-${each.key}"
  branch   = each.value.branch
  hosts    = each.value.hosts
}
```

The module creates the Dokploy application, one domain per host, and
one proxied CNAME per host. Adding an environment would be one map
entry, which is exactly the amount of ceremony it deserves. Deploys
themselves stay with Dokploy: push to a branch, the webhook fires,
the right environment rebuilds.

## Two lessons from running it

First: do not reach through a resource's computed collections. The
project resource returns its environments list, but the API recomputes
it on every read, so every downstream reference goes unknown and the
plan churns. Resolve the environment by name through a data source
chain instead, and the plan settles.

Second: compose services get their reverse-proxy labels injected at
deploy time. Add a domain to an already-running stack and Terraform
applies cleanly while the router serves a 404, because the running
containers never heard about it. The fix is one redeploy call after
the domain lands. Terraform tells you what is declared; it cannot tell
you what the containers were told.

The provider is `vanillauys/dokploy` on the registry. Building it is
[its own story](/writing/creating-a-dokploy-terraform-provider).