Skip to content

Using dev_overrides for Local Terraform Provider Development

When you are building a Terraform provider, the default installation mechanism works against you. Every time you want to test a change, Terraform looks up the provider in a registry. That means you either publish a pre-release to the Terraform Registry on every iteration, configure a private local mirror, or wire up a complex network mirror configuration just to try out a two-line fix. None of those options belong in a tight edit-compile-test loop.

The dev_overrides block in the Terraform CLI configuration file solves this. It tells Terraform to skip the registry entirely for a named provider and load the binary from a local path instead. On macOS and Linux, this file is ~/.terraformrc. On Windows, it is %APPDATA%\terraform.rc.

The Problem with the Default Workflow

Terraform's normal provider installation flow looks like this:

  1. terraform init reads required_providers in your configuration.
  2. Terraform contacts the registry (or a mirror) to resolve the provider version.
  3. The provider binary is downloaded and cached in the .terraform directory.

This is exactly right for production use. During provider development, it becomes an obstacle. You need to test a change you just made to the Go source, and the registry has no idea that change exists. Cutting a pre-release tag, waiting for CI, and updating your test consumer configuration on every iteration adds minutes to what should be a seconds-long feedback loop.

Configuring dev_overrides

Add the following block to the Terraform CLI configuration file (~/.terraformrc on macOS and Linux; %APPDATA%\terraform.rc on Windows):

provider_installation {

  dev_overrides {
    "vmware/vsphere" = "/Users/tenthirtyam/go/bin"
  }

  direct {}
}

Two things to notice:

  • The path is your Go install binary directory. When you run go install inside the provider repository, Go places the compiled binary in $GOBIN if it is set, or falls back to $GOPATH/bin if not. Terraform will pick it up from that location directly.
  • direct {} is required. Without it, dev_overrides takes over the entire provider installation block, and Terraform will not be able to resolve any other provider from the registry. The direct {} stanza tells Terraform to handle all providers that are not listed in dev_overrides using the normal registry path.

Finding Your Go Binary Path

If you are unsure where go install is placing binaries, confirm it at the terminal:

go env GOBIN

If this prints a non-empty path, that is the directory to use in your dev_overrides configuration.

If it is empty, Go falls back to $GOPATH/bin. In that case, run:

go env GOPATH

Append /bin to the returned value. For example, if go env GOPATH returns /Users/tenthirtyam/go, your binary path is /Users/tenthirtyam/go/bin.

go env GOBIN

If this prints a non-empty path, use that value in your dev_overrides configuration.

If it is empty, Go falls back to $GOPATH\bin. In that case, run:

go env GOPATH

Append \bin to the returned value. For example, C:\Users\tenthirtyam\go\bin.

The Development Loop

With dev_overrides in place, the workflow collapses to three steps:

1. Make a change to the provider source.

2. Build and install the binary.

go install

Run this from the root of the provider repository. Go compiles the provider and writes the binary to $GOBIN (or $GOPATH/bin if GOBIN is not set). Because dev_overrides points Terraform at that exact directory, no additional copy or installation step is needed.

3. Test the change.

terraform plan

Or run terraform apply against a test configuration that exercises the resource or data source you just modified. After you have run terraform init at least once in this working directory, Terraform skips re-installing the overridden provider on subsequent runs and loads the binary straight from your Go binary directory.

Repeat from step one. The whole cycle takes as long as go install runs, which is typically a few seconds for an incremental build.

What terraform init still does with dev_overrides

When dev_overrides is active, terraform init will print a warning and skip installation of the overridden provider binary, but you still must run terraform init at least once in each working directory (and again whenever you change your backend, modules, or add other providers). After that initial init, you can iterate with go install + terraform plan or terraform apply without rerunning terraform init just to pick up provider changes. This is expected behavior and not an error.

The Runtime Warning

Terraform prints a notice whenever dev_overrides is active:

│ Warning: Provider development overrides are in effect
│ The following provider development overrides are set in the CLI configuration:
│  - vmware/vsphere in /Users/tenthirtyam/go/bin
│ The behavior may therefore not match any released version of the provider and
│ applying changes may cause the state to become incompatible with published
│ releases.

This is intentional. It is a reminder that the binary on disk does not correspond to any published version and that the state file produced during testing may not be compatible with a release version of the provider. The warning does not indicate a problem.

What to Keep in Mind

Do not use dev_overrides in CI or production.

The Terraform CLI configuration file (~/.terraformrc on macOS and Linux; %APPDATA%\terraform.rc on Windows) with a dev_overrides block is a local developer configuration. It should never be committed to version control and should never be present on CI runners or in any environment where reproducible, registry-sourced providers are required.

CI pipelines should always install providers through the normal terraform init path so that the exact published version is used and the installation is reproducible across runs.

The provider address must match exactly.

The key in dev_overrides must match the provider source address in your required_providers block exactly, including the namespace. vmware/vsphere and registry.terraform.io/vmware/vsphere are equivalent, but a custom provider registered under a different namespace must use that namespace in the override key.

References