Using the mirror
Three ways in, depending on whether you are on a workstation, inside CI, or building an
air-gapped bundle. All of them are plain HTTPS GET against
https://mirror.fadminops.space/v1/providers/.
1. Workstation
The CLI configuration file lives at ~/.terraformrc on Linux and macOS,
%APPDATA%\terraform.rc on Windows. A network_mirror block replaces the default
installation method for the prefixes you list.
provider_installation {
network_mirror {
url = "https://mirror.fadminops.space/v1/providers/"
include = ["registry.terraform.io/hashicorp/*"]
}
direct {
exclude = ["registry.terraform.io/hashicorp/*"]
}
}
Confirm it took effect — the log line names the source for every provider it resolves:
$ TF_LOG=trace terraform init 2>&1 | grep -i "provider mirror"
... using provider mirror at https://mirror.fadminops.space/v1/providers/
Lock files are portable. .terraform.lock.hcl records upstream
checksums, not mirror URLs, so a lock file produced against the mirror verifies against
registry.terraform.io and the other way round. Do not regenerate locks just because you switched.
2. CI
Ship the same block as a file and point the CLI at it with TF_CLI_CONFIG_FILE. Nothing else
in the pipeline changes.
# .gitlab-ci.yml / any runner
before_script:
- printf '%s\n' \
'provider_installation {' \
' network_mirror { url = "https://mirror.fadminops.space/v1/providers/" }' \
'}' > /tmp/tf.rc
- export TF_CLI_CONFIG_FILE=/tmp/tf.rc
Runners on our own estate resolve the mirror over the internal network; from anywhere else it is the same public endpoint, so a pipeline behaves identically on a laptop and on a runner.
3. Air-gapped bundle
For environments with no egress at all, mirror to a directory and copy the directory in. This produces the
filesystem layout terraform expects under filesystem_mirror:
$ terraform providers mirror -platform=linux_amd64 ./providers
$ tar czf providers.tgz ./providers
# on the target host: extract, then
provider_installation {
filesystem_mirror { path = "/opt/terraform/providers" }
}
Protocol
The mirror implements the provider network mirror protocol. Two document types, both JSON, both cacheable. There is no index of the whole namespace by design — you ask for a provider you already know the address of.
| Request | Returns |
|---|---|
| GET /v1/providers/<host>/<ns>/<type>/index.json | Available versions |
| GET /v1/providers/<host>/<ns>/<type>/<version>.json | Archives per platform, with hashes |
Worked example:
$ curl -s https://mirror.fadminops.space/v1/providers/registry.terraform.io/hashicorp/random/index.json
{
"versions": {
"3.7.2": {},
"3.7.1": {},
"3.6.3": {}
}
}
and one level down, the archives for a single version:
$ curl -s .../hashicorp/random/3.7.2.json
{
"archives": {
"linux_amd64": {
"url": "terraform-provider-random_3.7.2_linux_amd64.zip",
"hashes": ["h1:cFGCdxTlsrTeVhIcTHJ1nzdT1zpBGwOO9uL9dgvMYRc="]
}
}
}
Archive URLs are relative to the document that names them, so the mirror can be re-hosted behind another path or another name without rewriting any JSON.
Verifying what you got
The h1: hashes are upstream values copied verbatim; the mirror never recomputes or re-signs
anything. To check a download by hand:
$ terraform providers lock -platform=linux_amd64 $ grep -A3 'hashicorp/random' .terraform.lock.hcl
If a version is missing. The mirror only serves what the last sync
copied. A brand-new release can be up to six hours behind upstream — see Status for
the current window. Pin to a version that exists, or fall back to direct for that one prefix.