Managing Environments
Most organizations deploy to several "environments": infrastructures for distinct deployment stages or purposes, isolated from one another in some way — often physically, and by ownership and permissions.
Typical dimensions of isolation are:
- Deployment stage: development, staging, production
- Region: us-east, us-west, europe
- Kubernetes cluster, account, or tenant
An environment is a dimension, not an entity
ConfigHub has no Environment entity. Environments are a dimension of the component model: a component is a logical piece of software, and each of its deployments is a variant — one complete copy of the component's configuration, adapted to one deployment context.
A useful mental model is a matrix, with components on one axis and deployment contexts on the other. Each populated cell is a deployment variant.
| Component | Base | Dev | Staging | Prod (us) | Prod (eu) |
|---|---|---|---|---|---|
| web-app | ✓ | ✓ | ✓ | ✓ | ✓ |
| inference-engine | ✓ | ✓ | ✓ | ||
| docs-site | ✓ | ✓ |
"The dev environment" is a column of that matrix — every component's dev variant — not a single container. So an environment is something you select, with a label, rather than something you create.
Each variant is a Space
A variant is implemented as a Space, and the set of Units in it is the totality of that variant's configuration. In ConfigHub's two-level authorization model, that is also the isolation boundary: each variant's configuration lives in its own Space and is released to its own Target.
The recommended Space slug is {{.Labels.Component}}-{{.Labels.Variant}}, and the well-known Space labels name the axes:
| Label | What it names |
|---|---|
Component |
the logical software — every variant of it shares this value |
Variant |
this variant within the component |
Stage |
the delivery stage: Base, Development, Staging, Production |
Environment |
the environment class, where it differs from the stage |
Region |
us-east2, europe-west1, … |
Layer, Owner |
the platform layer and the owning team |
cub variant create sets them as it clones, so you rarely set them by hand:
cub variant create prod-use2 web-base \
--stage Production --environment Prod --region us-east2 \
--target platform-prod/cluster-use2 \
--namespace web-prod
That creates the Space web-prod-use2, labelled Component=web, Variant=prod-use2, Stage=Production, Environment=Prod, Region=us-east2, cloned from web-base and linked to it so it can be promoted later. See creating and managing variants.
Because the implementation is Spaces and labels, everything that operates on those works across environments — filters, bulk operations, triggers, views, and permissions:
# Every variant of one component
cub space list --where "Labels.Component = 'web'"
# One environment, across every component
cub space list --where "Labels.Environment = 'Prod'"
# One environment in one region
cub space list --where "Labels.Environment = 'Prod' AND Labels.Region = 'us-east2'"
Shared entities per environment
Targets, workers, views, and filters are not part of any component's configuration, so they do not belong in a variant Space — a variant Space gets cloned, and its contents with it. Give each environment a Space of its own for them:
cub space create --label Environment=Development platform-dev
cub space create --label Environment=Prod platform-prod
A Space's release Target must already exist when the Space is created, so the Target generally cannot live in the Space it releases. A per-environment platform Space is where it goes.
The same applies to a ChangeWorkflow definition: it governs a component's rollout across environments, so it lives in a Space of its own rather than in a base.
Policy per environment
Triggers enforce constraint-based policies on configuration data, and they are registered at the Space level — either by creating them in the Space, or by naming which Triggers apply to it with a where filter. That is how a production environment gets stricter rules than a development one: encryption required, no privileged containers, backups on, cost ceilings enforced.
Point each environment's Spaces at that environment's platform Space:
platformSpaceID="$(cub space get platform-prod -o jq='.Space.SpaceID')"
cub space create app-prod --label Environment=Prod --where-trigger "SpaceID = '$platformSpaceID'"
cub variant create copies the upstream Space's WhereTrigger and TriggerFilterID to the clone, so a variant created from a base inherits its Trigger selection; override it afterwards where an environment's policy differs. A Space's Trigger list is cached when the selector is set, so a Trigger added to the platform Space later is not picked up by Spaces already pointing at it until they re-list:
cub space update --refresh-triggers app-prod
See validating configuration and enforcing policies.
Moving a change between environments
Environments are usually not independent: a change is authored once and then travels — base to dev, dev to staging, staging to each production region. That path follows the upstream/downstream relationships cub variant create records.
To move everything the upstream has, promote the variant:
cub variant promote web-prod-use2
To move one named change through that path — gated on each environment actually running it before the next one may take it — declare the stages as a ChangeWorkflow and name the change with a ChangeOrder. See tracking and promoting changes.
Further reading
- Components and variants — the model this guide applies
- Creating and managing variants — the commands
- Tracking and promoting changes — moving a change environment to environment
- Manage sets of units together — selecting across environments