Private Provider Registry

Overview

The private provider registry lets you host custom, forked, or internally approved Terraform and OpenTofu providers in your Scalr account. Configurations consume them with the same required_providers syntax used for the public registry, but the source address points at your Scalr host instead. This gives you a central place to control which provider binaries are available, who can publish them, and which signing key vouches for each release.

The registry implements the OpenTofu provider registry protocol, so any Terraform or OpenTofu CLI version that speaks the standard protocol can resolve and download providers from Scalr without extra plugins.

When to Use the Private Registry

Use a private provider when:

  • You maintain an internal fork of a public provider and want to pin workspaces to your build.
  • You distribute a provider that you wrote in-house and do not want to publish to the public Terraform registry.
  • You need an audit trail of who published which provider version, and which GPG key signed it.
  • You want to make a curated set of providers available to consumers without giving them direct access to a binary store or VCS repo.

If you only need to control which public providers are pulled into runs, the provider configurations feature already covers that. The private registry is for hosting provider binaries themselves.

Concepts

The registry has three resources.

Providers

A provider is a named, account-scoped record that holds metadata for a single provider distribution. It has:

  • Name — the short name consumers will type after terraform-provider-. The name is lowercased, must match the Terraform provider naming rules, and must be unique within the account. It cannot be changed after creation.
  • Description — free-text description shown in the UI. This is the only attribute that can be edited after creation.
  • Namespace — every provider is published under the providers namespace. Scalr currently auto-assigns this and does not support custom namespaces.

A provider on its own does nothing until you publish at least one version under it.

Provider Versions

A provider version is a specific release of a provider. Each version has:

  • A semantic version string (for example, 1.4.2). Each version must be unique within its parent provider.
  • A reference to the GPG key that signed the version's checksum file.
  • One or more provider packages, each of which is a compiled binary for a specific operating system and CPU architecture.
  • A status: pending while you are uploading the artifacts, uploaded once Scalr has verified them, or errored if verification failed.

Supported operating systems are linux, darwin, windows, freebsd, openbsd, and solaris. Supported architectures are amd64, 386, arm, and arm64. Each (os, arch) combination you ship is uploaded as its own zip archive inside the release tarball. You do not need to ship every combination, only the platforms you care about.

GPG Keys

A GPG key is a public key in ASCII-armored form that Scalr stores at the account scope and uses to verify the signature on a provider version's checksum file. The key has:

  • A name — letters, numbers, dashes, underscores, and spaces. Unique within the account.
  • A description — optional free text.
  • A fingerprint — a text-encoded representation of your GPG public key (starting with -----BEGIN PGP PUBLIC KEY BLOCK-----). Two keys with the same fingerprint cannot coexist in the same account.

A key cannot be deleted while any provider version still references it. Generate the key on the workstation or CI host that will sign provider releases. Scalr only ever stores the public half.

Setting Up the Registry

The end-to-end flow for a first-time setup is:

  1. Generate a GPG signing key and upload its public half to Scalr.
  2. Create a provider record (name and description).
  3. Publish a version: create the version metadata, then upload the release tarball to the URL Scalr returns.
  4. Reference the provider from a Terraform or OpenTofu configuration.

The first three steps require account-scoped permissions. The fourth requires only providers:read, which is what most workspace consumers will have.

Managing GPG Keys

Open the Registry → GPG Keys page at the account scope. The page lists every key currently registered, the user who uploaded it, the fingerprint Scalr extracted, and the number of provider versions that depend on it.

Generating a Key

If you do not already have a signing key, generate one with GPG. Pick a key name and an email address you control, and choose a passphrase you will keep available to your release tooling.

gpg --full-generate-key

GitHub's generating a new GPG key walkthrough covers the prompts in detail. Export the public half of the key in ASCII-armored form — this is what you upload to Scalr.

Export the public half of the key in ASCII-armored form; this is what you upload to Scalr.

gpg --armor --export <KEY_ID>

Use gpg --list-keys --keyid-format=short to find the <KEY_ID>. The exported block starts with -----BEGIN PGP PUBLIC KEY BLOCK-----.

Uploading a Key

Click New GPG key on the GPG Keys page. Give the key a unique name, paste the ASCII-armored public key into the ASCII armor field, and optionally add a description. Scalr extracts the fingerprint from the key material and shows it on the resulting detail page.

If a key with the same fingerprint already exists in your account, the upload is rejected, and the existing key's name is included in the error so you can find it.

Replacing or Deleting a Key

You can edit the name and description of an existing key at any time. The ASCII armor and the fingerprint are immutable. To change the underlying key, upload a new one, migrate provider versions, then delete the old one.

A key cannot be deleted while it is referenced by a provider version. The delete action returns a validation error in that case and tells you the registry is still using the key. Delete or re-sign the dependent versions first.

Creating a Provider

Open Registry → Providers at the account scope and click New provider.

Fill in:

  • Name — This becomes the provider's identifier in source addresses.
  • Description — optional. Shown on the provider's detail page.

The name is validated against the standard Terraform provider naming rules and is automatically lowercased. After the provider is created, the only field you can edit is the description.

Publishing a Provider Version

Publishing is a two-step flow:

  1. Create the version record. This tells Scalr which provider you are publishing under, which version number you intend to release, and which GPG key the checksum signature will be made with.
  2. Upload the release tarball. Once the version is created, Scalr returns a one-time upload URL. You PUT a tar.gz archive to that URL containing the binaries, the SHA256SUMS file, and the detached GPG signature. Scalr unpacks the tarball, verifies it, and either marks the version uploaded or errored.

The publishing flow is API-first, so you can wire it into a CI pipeline, but the UI also surfaces the exact commands to run for a manual release.

Required Permission

Publishing a version requires provider-versions:create. Reading providers and versions requires providers:read. Deleting a version requires provider-versions:delete.

Create the Version

In the UI, open the provider, click Publish version, and pick the version string and the GPG key to sign with. The form shows the exact commands to run for the rest of the flow.

In the API, POST to /provider-versions with the parent provider, the GPG key, and the version string. The version must be valid semver and must not already exist on that provider.

POST /api/iacp/v3/provider-versions
{
  "data": {
    "type": "provider-versions",
    "attributes": {
      "version": "1.0.0"
    },
    "relationships": {
      "provider": {
        "data": { "id": "prv-xxxxxxxxxxxxxxxx", "type": "providers" }
      },
      "gpg-key": {
        "data": { "id": "gpg-xxxxxxxxxxxxxxxx", "type": "gpg-keys" }
      }
    }
  }
}

The response includes the new version's ID, its initial status of pending, and the upload URL you will use in step 2.

Build and Sign the Release

Build a separate binary for every (os, arch) combination you want to ship. The expected naming convention is terraform-provider-<name>_<version>_<os>_<arch>.zip

Each zip should contain only the compiled provider binary. For an acme provider being released as 1.0.0:

# macOS arm64
GOOS=darwin GOARCH=arm64 go build -o terraform-provider-acme
zip terraform-provider-acme_1.0.0_darwin_arm64.zip terraform-provider-acme

# Linux amd64
GOOS=linux GOARCH=amd64 go build -o terraform-provider-acme
zip terraform-provider-acme_1.0.0_linux_amd64.zip terraform-provider-acme

Generate a single SHA256SUMS file covering every zip in the release:

shasum -a 256 terraform-provider-acme_1.0.0_*.zip > terraform-provider-acme_1.0.0_SHA256SUMS

Sign the SHA256SUMS file with the same GPG key you registered in Scalr — Scalr will verify the detached signature with the public key it has on file:

gpg --detach-sign -u <KEY_ID> terraform-provider-acme_1.0.0_SHA256SUMS

This produces terraform-provider-acme_1.0.0_SHA256SUMS.sig.

Bundle and Upload

Wrap the zips, the SHA256SUMS file, and the .sig file into a single tar.gz:

tar -czf release.tar.gz terraform-provider-acme_1.0.0_*

Upload the tarball to the URL returned in step 1. After Scalr finishes verifying the contents, the version transitions from pending to uploaded. If verification fails, it transitions to errored and the error message tells you which check failed . For example, a mismatched checksum, an unsupported file in the tarball, or a signature that does not match the registered GPG key.

Tarball Contents

FileRequiredNotes
terraform-provider-<name>_<version>_<os>_<arch>.zipYes — at least oneOne zip per platform you support. The zip contains only the compiled binary.
terraform-provider-<name>_<version>_SHA256SUMSYesOne line per zip in the format <sha256> <filename>.
terraform-provider-<name>_<version>_SHA256SUMS.sigYesDetached GPG signature of the SHA256SUMS file, made with the GPG key referenced when the version was created.
terraform-registry-manifest.jsonOptionalStandard provider manifest. If present, its protocol_versions are recorded against the version. If absent, Scalr records a default protocol version (5.0).
README.mdOptionalCase-insensitive. Surfaced on the version detail page in the UI.
CHANGELOG.mdOptionalCase-insensitive. Currently uploaded but not surfaced.
docs/*.mdOptionalCurrently ignored. Reserved for future documentation rendering.

Any other top-level entry in the tarball causes verification to fail.

Consuming a Provider

Configurations consume a private provider with the same required_providers block they would use for any other provider. The source address has three parts: your Scalr host, the namespace providers, and the provider name.

terraform {
  required_providers {
    acme = {
      source  = "<account>.scalr.io/providers/acme"
      version = ">= 1.0.0"
    }
  }
}

resource "acme_widget" "example" {
  name = "demo"
}

Replace <account>.scalr.io with the hostname of the Scalr instance you log in to. Self-hosted installations use their own hostname.

When the workspace runs terraform init (or tofu init), the CLI calls Scalr's implementation of the provider registry protocol to discover available versions and download the right binary for the runner's operating system and architecture. The runner's identity is checked against providers:read on the account, and the provider binary is streamed from the registry only if that check passes.

The same source address works in CLI-driven, VCS-driven, and module-driven workspaces. There is no separate setup on the workspace itself.

Reporting

Private provider usage is recorded in the same provider usage data that backs the reports feature. You can see which workspaces are consuming which version of which provider, and use that data to deprecate older versions or trace adoption of a new release.

Permissions

The registry uses three permission groups: providers:* for the provider records, provider-versions:* for the versions, and gpg-keys:* for the signing keys. They are independent — for example, you can grant a release-automation service account provider-versions:create without giving it the ability to manage GPG keys or delete providers.

ActionRequired permission
List or read providersproviders:read
Create a providerproviders:create
Update a provider's descriptionproviders:update
Delete a providerproviders:delete
List or read provider versionsproviders:read
Publish a new version (create + upload)provider-versions:create
Delete a versionprovider-versions:delete
List or read GPG keysgpg-keys:read
Upload a new GPG keygpg-keys:create
Update a GPG key's name or descriptiongpg-keys:update
Delete a GPG keygpg-keys:delete

Workspace runs that resolve and download providers only need providers:read. The download URL Scalr hands back to the CLI is short-lived and scoped to that read permission, so a configuration cannot reuse a stale link to bypass access checks later.

All three permission groups can be added to custom roles in the same way as any other Scalr permission. See RBAC for the broader model.

Limitations

A few constraints are worth calling out:

  • Only the auto-generated providers namespace is available. Custom namespaces are not configurable.
  • A provider's name cannot be changed after creation. To rename, publish a new provider and migrate consumers over.
  • CHANGELOG.md and docs/*.md files inside the release tarball are accepted by the upload check but are not yet surfaced in the UI.
  • The registry follows the standard provider protocol, so any feature the public Terraform or OpenTofu registry does not expose is not available here either.