GitOpsHQ Docs
Deployment

HQ Variables

Complete guide to scoped variable management, precedence resolution, and policy-safe variable rollout.

What Are HQ Variables

HQ Variables are scoped key-value pairs injected into Helm values during delivery generation. They let you manage configuration that varies across repositories, tenants, environments, and services — without duplicating values files or maintaining per-tenant chart forks.

When the delivery generator renders manifests, every {{hq.var.KEY}} placeholder in your values files is resolved against the effective variable map for that specific tenant-environment-service combination. You can also use explicit scope syntax — {{hq.var.repo.KEY}}, {{hq.var.tenant.KEY}}, {{hq.var.env.KEY}}, {{hq.var.svc.KEY}} — to pin resolution to a specific scope.

Key Concept

Variables decouple what changes (configuration values) from where it changes (Helm charts / Kustomize overlays). A single values file can serve hundreds of tenant-environments because runtime differences live in HQ Variables.

Variable Scopes

HQ Variables support four hierarchical scopes, from broadest to most specific:

ScopeScope IDApplies ToTypical Use
Repository_repoAll tenants and environments in the repositoryFeature flags, shared defaults, base image registry
EnvironmentenvAll tenants within a specific environmentDatabase URLs, log levels, resource limits
TenanttenantA specific tenant across all environmentsCustomer-specific branding, license keys, data regions
ServicesvcA specific service in a specific tenant-environmentPer-service replica count, resource overrides, sidecar config

Scope Hierarchy Diagram

Loading diagram…

Precedence Resolution

When the same variable key exists at multiple scopes, the most specific scope wins. The resolution order is:

Service > Tenant > Environment > Repository

Concrete Example

Consider a variable called REPLICA_COUNT defined at multiple scopes for a repository with two tenants (acme and globex) across staging and production environments:

ScopeContextValue
Repository2
Environmentproduction5
Tenantacme3
Serviceacme / production / api-gateway8

Resolved values:

TenantEnvironmentServiceEffective REPLICA_COUNTWinning Scope
acmestagingapi-gateway3Tenant
acmeproductionapi-gateway8Service
acmeproductionworker3Tenant (tenant > environment in precedence)
globexstagingapi-gateway2Repository
globexproductionapi-gateway5Environment

Precedence Matters

Tenant scope overrides environment scope. If tenant acme sets REPLICA_COUNT=3, it wins over the production environment's REPLICA_COUNT=5 for all of acme's services — unless a service-scope override exists.

The Scope Resolver panel in the UI traces exactly which scope provides the effective value for any given context, so you never have to guess.

Managing Variables

UI Workflow

The Variables panel is accessible at /projects/:slug/variables. It provides a unified interface for all variable operations.

Select scope — Use the scope selector to choose the level (Repository, Environment, Tenant, or Service) and the specific context (e.g., which environment, which tenant).

Create or edit — Click "Add Variable" to create a new key-value pair, or click an existing variable's edit icon. Each variable is a simple key-value string pair.

Preview impact — Before saving, the impact preview panel shows which tenant-environments will be affected by this change and the before/after values.

Save — Confirm the change. If the target environment has a release policy, the variable change will be queued as a pending change in the next release.

Quick-Insert from Values Editor

When editing Helm values in the workload configuration panel, the HQ Variable quick-insert button lets you reference an existing variable or create a new one inline. The editor inserts the {{hq.var.KEY}} placeholder at the cursor position. For explicit scope pinning, use {{hq.var.repo.KEY}}, {{hq.var.env.KEY}}, {{hq.var.tenant.KEY}}, or {{hq.var.svc.KEY}}.

Bulk Upsert via API

For CI/CD automation, use the bulk upsert endpoint to create or update multiple variables in a single request:

curl -X POST "https://api.gitopshq.com/api/v2/projects/{slug}/variables/bulk-upsert" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "updates": [
      { "key": "LOG_LEVEL", "value": "debug", "scope": "environment", "scopeId": "env-staging-id" },
      { "key": "FEATURE_NEW_UI", "value": "true", "scope": "environment", "scopeId": "env-staging-id" },
      { "key": "DB_HOST", "value": "db.staging.internal", "scope": "environment", "scopeId": "env-staging-id" }
    ]
  }'

Using Variables in Helm Values

Variables are referenced in values files using the {{hq.var.KEY}} placeholder syntax. The delivery generator resolves these at generation time using scope precedence. For explicit scope pinning, use {{hq.var.repo.KEY}}, {{hq.var.env.KEY}}, {{hq.var.tenant.KEY}}, or {{hq.var.svc.KEY}}.

replicaCount: "{{hq.var.REPLICA_COUNT}}"

image:
  repository: "{{hq.var.IMAGE_REGISTRY}}/my-service"
  tag: "{{hq.var.IMAGE_TAG}}"

resources:
  limits:
    memory: "{{hq.var.MEMORY_LIMIT}}"
    cpu: "{{hq.var.CPU_LIMIT}}"

env:
  - name: DATABASE_URL
    value: "{{hq.var.env.DATABASE_URL}}"
  - name: LOG_LEVEL
    value: "{{hq.var.LOG_LEVEL}}"

Resolution Behavior

  • Variables are resolved at delivery generation time, not at Kubernetes apply time.
  • The delivery generator builds the effective variable map for the target tenant-environment-service, applies precedence rules, and substitutes all placeholders.
  • If a placeholder references a variable key that is not defined at any scope, the delivery generator emits a warning and leaves the placeholder unresolved. The release diff will show the raw placeholder, making it easy to spot.
Loading diagram…

Variable Impact Preview

Before changing a variable, the impact preview shows the full blast radius of the change:

Preview ColumnDescription
Tenant-EnvironmentWhich tenant-environments will receive the new value
ServiceWhich services reference this variable in their values
Current ValueThe currently resolved value (considering all scopes)
New ValueWhat the resolved value will be after the change
Scope SourceWhich scope currently provides the value and whether that changes

The preview also flags policy conflicts — if any affected tenant-environment has a freeze or requires approval for variable changes, the preview surfaces this before you save.

Review Before Broad Changes

A repository-scope variable change can fan out to every tenant-environment in the project. Always review the impact preview before modifying broad-scope variables.

Policy Integration

Variable changes participate in the same governance model as all other configuration changes:

  • OPA policy evaluation: When FeatureValuesGovernance is enabled, variable updates trigger OPA policy evaluation. Policy violations block the change and surface in the UI.
  • Release workflow: Variable changes appear as a change type in releases. If the target environment requires release approval, the variable change goes through the same approval flow.
  • Environment freeze: If an environment is frozen, variable changes targeting that environment are blocked until the freeze is lifted or a break-glass session is used.
  • Promotion: When promoting from staging to production, variable differences between environments are included in the promotion diff, making it clear what configuration shifts are happening.
  • Audit trail: Every variable create, update, and delete is logged with actor, timestamp, old value, and new value.

API Reference

The Variables API supports list/create plus path-based upsert and bulk preview/apply workflows:

MethodEndpointDescription
GET/api/v2/projects/{slug}/variablesList variables for a project (supports scope query params)
POST/api/v2/projects/{slug}/variablesCreate a variable from request body
POST/api/v2/projects/{slug}/variables/bulk-upsertApply multiple variable updates (updates[])
POST/api/v2/projects/{slug}/variables/previewDry-run variable updates and return impact
PUT/api/v2/projects/{slug}/variables/{scopeType}/{scopeId}/{key}Path-based single upsert (create/update/noop)
GET/api/v2/projects/{slug}/variables/resolve?tenant=X&env=Y&service=ZResolve effective variable map for a specific context

Filtering by Scope

# List all environment-scoped variables for staging
GET /api/v2/projects/{slug}/variables?scopeType=environment&scopeId={stagingEnvId}

# List all tenant-scoped variables for a specific tenant
GET /api/v2/projects/{slug}/variables?scopeType=tenant&scopeId={tenantId}

Error Reference

Error CodeMessageMeaning
400invalid request bodyRequest payload does not match the expected schema
400updates must contain at least one itemBulk preview/upsert request has an empty update list
400invalid key formatVariable key does not match ^[A-Za-z_][A-Za-z0-9_]*$
400invalid scope typeScope must be one of the supported values (repo, tenant, env, svc)
404project not foundThe specified project slug does not exist or you lack access
500failed to upsert variablesServer-side processing failure while applying updates

Best Practices

Use repository scope for shared defaults. Feature flags, base image registries, and common resource limits that apply to all tenants belong at the repository level. Override only where needed.

Use environment scope for env-specific config. Database URLs, external service endpoints, log levels, and resource profiles that differ between staging and production belong at the environment level.

Use tenant scope for customer-specific config. Branding, license keys, data regions, and customer-specific feature toggles belong at the tenant level.

Review impact preview before production changes. A single variable change can affect dozens of tenant-environments. The impact preview is your safety net — use it every time.

Adopt a naming convention. Keys must match ^[A-Za-z_][A-Za-z0-9_]*$. A consistent convention like DB_HOST, DB_PORT, DB_NAME with shared prefixes improves discoverability.

On this page