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:
| Scope | Scope ID | Applies To | Typical Use |
|---|---|---|---|
| Repository | _repo | All tenants and environments in the repository | Feature flags, shared defaults, base image registry |
| Environment | env | All tenants within a specific environment | Database URLs, log levels, resource limits |
| Tenant | tenant | A specific tenant across all environments | Customer-specific branding, license keys, data regions |
| Service | svc | A specific service in a specific tenant-environment | Per-service replica count, resource overrides, sidecar config |
Scope Hierarchy Diagram
Precedence Resolution
When the same variable key exists at multiple scopes, the most specific scope wins. The resolution order is:
Service > Tenant > Environment > RepositoryConcrete 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:
| Scope | Context | Value |
|---|---|---|
| Repository | — | 2 |
| Environment | production | 5 |
| Tenant | acme | 3 |
| Service | acme / production / api-gateway | 8 |
Resolved values:
| Tenant | Environment | Service | Effective REPLICA_COUNT | Winning Scope |
|---|---|---|---|---|
acme | staging | api-gateway | 3 | Tenant |
acme | production | api-gateway | 8 | Service |
acme | production | worker | 3 | Tenant (tenant > environment in precedence) |
globex | staging | api-gateway | 2 | Repository |
globex | production | api-gateway | 5 | Environment |
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.
Variable Impact Preview
Before changing a variable, the impact preview shows the full blast radius of the change:
| Preview Column | Description |
|---|---|
| Tenant-Environment | Which tenant-environments will receive the new value |
| Service | Which services reference this variable in their values |
| Current Value | The currently resolved value (considering all scopes) |
| New Value | What the resolved value will be after the change |
| Scope Source | Which 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
FeatureValuesGovernanceis 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:
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v2/projects/{slug}/variables | List variables for a project (supports scope query params) |
POST | /api/v2/projects/{slug}/variables | Create a variable from request body |
POST | /api/v2/projects/{slug}/variables/bulk-upsert | Apply multiple variable updates (updates[]) |
POST | /api/v2/projects/{slug}/variables/preview | Dry-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=Z | Resolve 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 Code | Message | Meaning |
|---|---|---|
400 | invalid request body | Request payload does not match the expected schema |
400 | updates must contain at least one item | Bulk preview/upsert request has an empty update list |
400 | invalid key format | Variable key does not match ^[A-Za-z_][A-Za-z0-9_]*$ |
400 | invalid scope type | Scope must be one of the supported values (repo, tenant, env, svc) |
404 | project not found | The specified project slug does not exist or you lack access |
500 | failed to upsert variables | Server-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.