Chapter 20Lesson 03~165 minutes

Review Apps, Dynamic Environments, Ephemeral Test Stacks, Route Maps, and Environment Cleanup: Configuration, Design Choices, and Tradeoffs

Choose per-MR versus shared previews, manual versus automatic cleanup, naming and DNS strategies, route maps, credential scope, and lifecycle controls using explicit trust, cost, and audit boundaries.

Design tradeoffsDNSCredentialsLifecycleCost

Learning objectives

  • Choose per-MR full stacks versus shared previews based on isolation, feedback quality, contention, cost, and cleanup complexity.
  • Choose manual stop, branch/MR lifecycle stop, and auto_stop_in as complementary controls rather than interchangeable guarantees.
  • Choose dynamic DNS, provider-generated URLs, or a local simulation without allowing raw branch names to become unsafe infrastructure identifiers.
  • Use route maps when source-to-preview navigation adds value and use generic environment URLs when one landing page is enough.
  • Scope credentials to review environments and untrusted contexts narrowly while keeping production credentials inaccessible to ordinary review-app jobs.

1. Design starts with isolation, trust, and cleanup—not YAML aesthetics

The best review-app architecture depends on what reviewers need to observe and what the preview can safely access. A full per-MR stack maximizes isolation but costs more and multiplies cleanup work. A shared preview reduces resource count but introduces contention and can blur “which revision am I viewing?” The YAML is downstream of those operational choices.

For every option, define source identity, resource ownership, credential scope, maximum lifetime, cleanup actor, cleanup evidence, and what happens when the normal stop path fails. In production evidence, record CI_PIPELINE_SOURCE and the exact CI_COMMIT_SHA alongside MR/environment identity so design decisions are tied to the pipeline and revision actually evaluated.

2. Per-MR full stack versus shared preview

Choice Strengths Costs/risks Observable evidence
Per-MR full stack Strong isolation; exact MR-to-resource mapping; parallel review. Higher compute/storage/DNS cost; more orphan opportunities. MR IID → env/resource IDs; per-review cost/TTL; independent health.
Shared preview Lower baseline cost; simpler provider footprint. Contention; overwrite races; harder provenance; one MR can disturb another. Current owner/SHA; serialization queue; handoff history.
Hybrid Dedicated app layer with shared read-only dependencies. Trust boundaries become more complex. Per-MR app ID plus shared dependency versions/permissions.

3. Automatic versus manual stop: layer controls instead of choosing one

Manual stop is useful when a reviewer still needs a preview. MR/branch lifecycle stop is useful when the development object closes. auto_stop_in catches stale previews that nobody explicitly closes. Provider TTLs or reconciliation jobs catch cases where the CI stop job never reaches the provider. These controls solve different failure modes.

A mature design uses at least two independent cleanup mechanisms for chargeable or security-sensitive resources: an event-driven stop path and a time-based/reconciliation backstop. Neither should use broad deletion criteria.

4. Naming: environment identity, URL slug, and provider ID may differ

Keep three names explicit:

  • Environment: review/mr-101 for human GitLab history.
  • GitLab slug: observed CI_ENVIRONMENT_SLUG, limited by GitLab rules and useful for URL metadata.
  • Provider ID: a provider-validated identifier such as rvw-101-a1b2c3, recorded in a manifest/tag set.

Do not derive destructive selectors from URLs. Do not use only a truncated slug as proof of uniqueness. Include immutable labels/tags such as project ID, MR IID, and source SHA where the provider supports them.

5. Dynamic DNS versus local/provider-generated URLs

Approach When it fits Security/operations note
Wildcard DNS *.review.example.test Many short-lived previews behind one ingress/load balancer. Certificate, routing, tenant isolation, and hostname collision become platform concerns.
Provider-generated URL Managed preview platforms or PaaS. Record returned URL and resource ID; do not infer identity from URL alone.
Local simulation / .invalid Training, CI logic, contract tests. No external reachability; proves lifecycle logic safely.
Fixed shared URL One serialized shared preview. Must expose current SHA/owner visibly to avoid reviewer confusion.

6. Route maps versus one generic URL

Use route maps when changed files have a stable public-page correspondence—documentation, static sites, page-based web apps. Keep mappings small and ordered from specific to general because the first source match wins. Test regexes against representative paths; an over-broad early rule can shadow later mappings.

A generic URL is often better for APIs, stateful workflows, SPAs where deep-link mapping is unreliable, or previews that require login/setup. Route maps are a developer-experience enhancement, not a correctness gate.

7. Credential model: review/* is a separate trust domain

Review jobs should receive only what the preview needs. For test services, use synthetic accounts and short-lived scoped identity. For project variables, environment scope review/* can narrow availability, but do not use environment-scoped values to control rules or includes during pipeline compilation. Protected production credentials should remain unavailable to ordinary unprotected review refs.

For fork MRs, treat code as untrusted. Do not bypass parent-project protections merely to make a review app convenient. If a trusted maintainer deliberately runs a parent-context pipeline, review exactly which variables/runners become available and make the deployment target disposable.

8. Cost and capacity: ephemeral resources need budgets and cardinality bounds

Cost is a correctness constraint when unbounded review creation can exhaust quotas and prevent production work. Define maximum concurrent reviews, resource class, TTL, idle behavior, database/storage policy, and stale-resource reconciliation. Measure queue time, creation latency, active count, age distribution, failed-cleanup count, and spend by review owner—not only pipeline duration.

Do not solve cost leaks with a nightly “delete everything matching review-*” script. Reconcile manifests/tags against open MRs and exact ownership labels, then delete only resources that are provably stale and inside the dedicated review boundary.

9. Environment actions and lifecycle semantics

Action/control GitLab meaning Design implication
start Default deployment action; creates/updates deployment record. Use for actual review deployment.
stop Marks stop lifecycle via stop job. Teardown must remove external resource and verify.
prepare / access / verify Lifecycle work that can interact with environment without ordinary new deployment intent. Useful for prep/verification; understand auto-stop reset behavior.
auto_stop_in Schedules environment stop after a duration. Backstop; not exact provider TTL.
Stale cleanup Maintainer/Owner operation to stop old non-protected environments. Administrative reconciliation, not a substitute for per-resource teardown.

10. Decision table: choose an architecture from evidence requirements

Scenario Recommended pattern Prerequisites / trust Evidence to require
Docs site; many MRs Per-MR static review + route map + 1–2 day auto-stop. Disposable hosting; no production secrets. MR/SHA, env/slug, URL, mapped paths, cleanup proof.
Stateful app; expensive DB Per-MR app + shared read-only test data or isolated lightweight schema. Strong tenant isolation; bounded DB role. Resource IDs, schema owner, TTL, DB cleanup check.
Small team; expensive stack Serialized shared preview. resource_group + visible current SHA. Queue, owner, current SHA, previous handoff.
Untrusted forks Build/test only or manual trusted deployment to disposable target. No protected prod variables/runners. Triggering user/context, variables policy, target isolation.
Training/course Local guarded provider + fake URL metadata. No credentials. Manifest, hashes, bounded inventory, cleanup proof.

11. Compatibility and offering notes

The mandatory review-app/environment path is Free-compatible. Protected environments and deployment approvals belong to later Chapter 21 and higher tiers; do not make them a hidden prerequisite here. Environment-scoped project variables are available in the project settings, while some group-level environment-scope capabilities are tiered—verify the exact scope/tier in your instance.

GitLab versions also evolve around environment actions and auto-stop behavior. This chapter records assumptions as of 2026-09-12; production platform code should include version tests or upgrade notes rather than silently depending on UI behavior.

12. Worked choice: 80 docs MRs, cheap static hosting, no secrets

Choice: per-MR static review environment named by MR IID, wildcard or provider URL, route map for docs paths, manual stop button plus auto_stop_in: 2 days, and a provider reconciler keyed by project/MR labels.

Why: static previews are cheap, isolation improves review correctness, route maps reduce navigation friction, and no secret-bearing backend is needed. The bounded TTL/reconciler contains orphan risk. Evidence is MR IID + source SHA + environment name/slug + resource ID + content digest + stop job/operation ID + final absence query.

13. Design summary

  • Choose isolation and cleanup guarantees before syntax.
  • Use multiple cleanup layers for chargeable/security-sensitive resources.
  • Keep environment name, GitLab slug, and provider resource ID distinct and recorded.
  • Route maps optimize navigation, not deployment correctness.
  • Review credentials belong to a low-trust, non-production boundary.
  • Bound active-review cardinality, TTL, and resource class; reconcile exact ownership labels.

Knowledge check

When is a shared preview reasonable?

Why use more than one cleanup mechanism?

Should CI_ENVIRONMENT_SLUG be your universal cloud resource ID?

What is the main value of route maps?

Why is broad stale-prefix deletion a bad cost-control strategy?

Next lesson

Diagnostics, failure modes, security, and performance

Preserve evidence and diagnose orphans, unsafe names, credential exposure, unreachable stop jobs, stale URLs, and cost leaks without blind deletion.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-12. Review-app, environment lifecycle, variable, protected-resource, route-map, and cleanup semantics are version-sensitive. Re-check the GitLab version used by your organization before copying exact lifecycle behavior into production.

  • Review apps — dynamic review environments, merge-request workflows, stop behavior, and route maps.
  • Environments — static/dynamic environments, environment states, on_stop, auto_stop_in, stale cleanup, deletion, and environment-scoped variables.
  • CI/CD YAML syntax reference — environment:name, url, on_stop, action, auto_stop_in, resource_group, and rules.
  • Predefined CI/CD variables — CI_COMMIT_REF_SLUG, CI_ENVIRONMENT_NAME, CI_ENVIRONMENT_SLUG, CI_ENVIRONMENT_URL, and merge-request variables.
  • CI/CD variables — protection, masking/hidden behavior, fork/MR exposure, and environment scope.
  • Environments API — environment metadata, stop, and stale-environment operations when API automation is appropriate.
  • Deployment safety — protected-resource and deployment-safety boundaries that also matter for review infrastructure.

Current assumptions used in this chapter: review apps and dynamic environments are available on Free, Premium, and Ultimate across GitLab.com, Self-Managed, and Dedicated. CI_COMMIT_REF_SLUG is normalized and shortened to 63 bytes; CI_ENVIRONMENT_SLUG is derived from environment:name, is truncated to 24 characters, and uppercase environment names can receive a random suffix. Route maps live in .gitlab/route-map.yml, are evaluated in declaration order, and the first matching source rule determines the public path; the merge-request widget can surface up to five mapped pages before filtering. environment:auto_stop_in accepts human-readable durations (and variables), but environment expiration is serviced by background work that runs approximately hourly, so it is not an exact timer. Deploy and stop jobs should have compatible rules/only/except; a stop job also has to be runnable when cleanup is needed. To trigger on_stop from the Environments UI, deploy and stop jobs must share a resource_group. Protected environments are Premium/Ultimate and are not required by the mandatory lab. The mandatory path uses fake .invalid URLs and guarded local filesystem resources under a temporary sandbox—no cloud account, Kubernetes cluster, production DNS, PAT, deploy token, or real secret is required.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.