Terraform Provider
Manage Nexus application configuration as code with the official Nexus Terraform provider
Availability depends on your school's enabled tools, provider setup, and account permissions. These settings are managed in the web workspace. A class policy may restrict a feature described here. Some options require a separately licensed feature. The presence of a guide does not unlock that feature.
The Nexus Terraform provider manages the configuration inside a running Nexus deployment: LLM providers, connectors and their credentials, document sets, agents, actions, MCP servers, user groups, API keys, and workspace settings.
Everything you would otherwise click through in the Admin Panel becomes a version-controlled .tf file that you can
review, diff, and apply from CI.
onyx-dot-app/onyx on the Terraform Registry#
The registry holds the full reference: every resource, every attribute, and the import syntax. This page covers what the provider is for and how to get started.
Two different Terraform surfaces#
Nexus has two separate Terraform offerings. They solve different problems and are used at different times.
| Terraform modules | Terraform provider (this page) | |
|---|---|---|
| Manages | The infrastructure Nexus runs on | The configuration inside Nexus |
| Creates | VPC, EKS, RDS, ElastiCache, S3, OpenSearch, WAF | LLM providers, connectors, document sets, agents, groups |
| Distributed as | Modules in the onyx repo you copy and adapt | A published provider you declare in required_providers |
| Talks to | AWS | The Nexus admin API of a deployment that is already running |
| Used | Before Nexus exists | After Nexus is reachable |
The two compose. Provision infrastructure with the modules, install Nexus with the Helm chart, then configure the deployment with this provider. You can run them from the same root module, but they are independent. the provider works against any reachable Nexus deployment, including a managed deployment, regardless of how it was deployed.
Requirements#
Terraform 1.5 or later. Write-only secret arguments. the recommended way to keep credentials out of state. need 1.11 or later, and an older CLI rejects a configuration that uses one.
A reachable Nexus deployment. The provider is an API client; it does not install or upgrade Nexus.
An API key in the
Admingroup. An API key's access comes from its group membership, so a key with no group cannot reach the admin endpoints the provider uses.
Get an API key#
Create a key in the Admin Panel under API Keys. see Service Accounts , and give it admin access. An unrestricted Personal Access Token created by an admin also works.
API keys authenticate the same way whatever the deployment's human AUTH_TYPE is (basic, OIDC, SAML, or cloud),
and credentials must belong to the workspace that owns the resource.
This first key is a chicken-and-egg problem: it has to exist before Terraform can run. Either leave it unmanaged, or
terraform importit afterwards. On import itsapi_keyattribute stays null, because Nexus only ever returns the key material at creation.
For a scripted setup,
examples/bootstrap/mint_api_key.sh
runs the whole sequence: register, log in, resolve the Admin group, mint the key.
The script requires an admin email and password rather than defaulting them. On a deployment with no users it registers that account, and the first user to register becomes an admin. so a defaulted password would quietly create a known-password administrator on any reachable deployment.
Configure the provider#
terraform {
required_providers {
onyx = {
source = "onyx-dot-app/onyx"
version = "~> 0.2"
}
}
}
provider "onyx" {
endpoint = "https://onyx.example.com" # or ONYX_SERVER_URL
api_key = var.onyx_api_key # or ONYX_API_KEY
}Nexus server origin, for example https://school.narb.cc or http://localhost:3000. Also read from ONYX_SERVER_URL.
An API key (on_...) in the seeded Admin group, or an unrestricted personal access token (onyx_pat_...).
Also read from ONYX_API_KEY.
Path prefix the API is served under. The default matches the web proxy.
Set it to "" when you point the provider directly at the backend, for example http://localhost:8080.
Also read from ONYX_API_PREFIX.
Supply
api_keyfrom theONYX_API_KEYenvironment variable rather than a.tffile. Provider configuration is the one place Terraform never writes to state, so an environment variable keeps the key out of both your repository and your state file.
What you can manage#
Each entry links to its full schema on the Terraform Registry.
Resources#
| Resource | Manages |
|---|---|
onyx_llm_provider | An LLM provider and the model list it exposes |
onyx_llm_provider_default | The deployment default and vision models. a singleton |
onyx_embedding_provider | Cloud embedding provider credentials |
onyx_credential | Connector credentials |
onyx_connector | Connector definitions and their sync schedule |
onyx_cc_pair | The connector-credential pair that starts indexing and carries access control |
onyx_document_set | Document sets built from connector-credential pairs |
onyx_persona | Agents, including their prompts, document sets, and actions |
onyx_custom_tool | Custom actions defined from an OpenAPI schema |
onyx_mcp_server | MCP servers Nexus connects to |
onyx_user_group | User groups: roster, managers, and permission grants |
onyx_api_key | API keys |
onyx_settings | Workspace settings. a singleton, partially managed |
Data sources#
| Data source | Reads |
|---|---|
onyx_llm_providers | Configured LLM providers and the current defaults |
onyx_embedding_providers | Configured embedding providers |
onyx_connectors | Configured connectors |
onyx_settings | Current workspace settings, including the license tier |
Every resource supports terraform import,
so you can bring a deployment configured by hand under Terraform without recreating anything.
The import id is on each resource's registry page.
A worked example#
This wires a chat model to an indexed site and an agent that answers from it. It is the shape most configurations take: a provider, a credential, a connector, the pair that links them, a document set, and an agent.
resource "onyx_llm_provider" "openai" {
name = "openai"
provider_type = "openai"
# Write-only: the key never reaches the state file. See below.
api_key_wo = var.openai_api_key
api_key_wo_version = 1
# The complete set of enabled models: anything omitted is removed on apply.
model_configurations = [
{ name = "gpt-5" },
{ name = "gpt-5-mini" },
]
}
# Referencing the provider id also orders destroys correctly: the default is
# released before the provider holding it is deleted.
resource "onyx_llm_provider_default" "this" {
provider_id = onyx_llm_provider.openai.id
model_name = "gpt-5"
}
# The web connector reads public pages, so its credential holds no secret.
resource "onyx_credential" "web" {
source = "web"
name = "public-web"
credential_json = jsonencode({})
}
resource "onyx_connector" "docs" {
name = "docs-site"
source = "web"
input_type = "load_state"
# Re-index once a day.
refresh_freq = 24 * 60 * 60
connector_specific_config = jsonencode({
base_url = var.docs_base_url
web_connector_type = "recursive"
})
}
# The pair is the object that indexes. It also carries the access control for
# the documents it produces.
resource "onyx_cc_pair" "docs" {
name = "docs-site"
connector_id = onyx_connector.docs.id
credential_id = onyx_credential.web.id
access_type = "public"
}
resource "onyx_document_set" "docs" {
name = "docs"
description = "Public product documentation"
cc_pair_ids = [onyx_cc_pair.docs.id]
}
resource "onyx_persona" "docs" {
name = "Docs"
description = "Answers product questions from the documentation"
system_prompt = <<-EOT
You answer questions from the product documentation.
If the documentation does not cover the question, say so.
EOT
document_set_ids = [onyx_document_set.docs.id]
}A complete,
runnable version of this configuration lives in
examples/bootstrap/.
Keeping secrets out of state#
Every secret the provider accepts comes in two forms. The plain attribute is stored in Terraform state,
where anyone who can read the state file can read the secret.
The _wo twin is a write-only
argument:
Terraform strips the value from both the plan and the state, so it exists only in your configuration.
Prefer the twin. Set one or the other, never both.
| Resource | Stored attribute | Write-only twin |
|---|---|---|
onyx_llm_provider | api_key, custom_config | api_key_wo, custom_config_wo |
onyx_embedding_provider | api_key | api_key_wo |
onyx_credential | credential_json | credential_json_wo |
onyx_mcp_server | api_token, admin_credentials | api_token_wo, admin_credentials_wo |
onyx_custom_tool | custom_headers | custom_headers_wo |
Rotating a write-only secret#
A value Terraform never stores is a value it cannot diff, so changing api_key_wo on its own plans nothing at all.
Each twin has a _wo_version counter for exactly this: raise it,
and the resulting diff makes the next apply send the current secret.
resource "onyx_llm_provider" "openai" {
name = "openai"
provider_type = "openai"
api_key_wo = var.openai_api_key
api_key_wo_version = 2 # was 1 — bump after rotating the key
model_configurations = [{ name = "gpt-5-mini" }]
}Do not derive the counter from the secret.
md5(var.token)and similar. Unlike the secret, the counter is kept in state.
Two attributes cannot have a write-only twin. onyx_api_key.api_key is minted by Nexus rather than supplied by you,
so Terraform can only hand it back through state. treat the state file as holding it.
onyx_mcp_server.auth_template_headers is computed by Nexus,
and Terraform does not allow an argument to be both computed and write-only.
Regardless of which form you use, encrypt your Terraform state and restrict who can read it. Several resources hold credentials for systems well outside Nexus.
Enterprise Edition#
Most of the provider works on Community Edition. These parts need Enterprise Edition:
onyx_user_group. the routes live in the Enterprise application and answer 404 on Community Edition.usersandgroupsononyx_document_setandonyx_persona. Community Edition rejects a private set or agent.
Enterprise Edition Feature
These features require an Enterprise plan. View plans or contact sales to learn more.
Things the API cannot express#
A few behaviours come from the Nexus API rather than the provider, and are worth knowing before you rely on them:
Secret drift is invisible. The API masks secrets on read, so rotating one in the Admin Panel does not show up in
terraform plan. The configured value is authoritative and is re-asserted on the next apply.onyx_settingsandonyx_llm_provider_defaultdo not really delete. Nexus has no reset API for either, sodestroyremoves them from state with a warning and leaves the live values alone.model_configurationsis the list of record. A model left out of it is removed from the provider server-side.Deleting an agent leaves a tombstone. The name stays taken, and a later create under that name revives the same agent id rather than making a new one.
Deleting a custom action detaches it from every agent that uses it, including agents Terraform does not manage, with no error and no warning.
The provider README carries the full list, and each resource's registry page repeats the ones that apply to it.
Source and releases#
Source:
terraform-provider-onyx/in the Nexus repoRelease mirror: onyx-dot-app/terraform-provider-onyx
Pin the provider version in required_providers and let Terraform's lock file pick up the checksums,
the same as any other provider. The registry page always shows the current release.