Chapter 19Lesson 02~195 minutes

Environments, Deployments, Environment Tiers, URLs, Deployment History, and Operational Traceability: Guided Hands-On Workflow and Core Operations

Build once, deploy a verified artifact to a disposable simulated target, create a GitLab environment/deployment record, update it once, and independently verify target state.

Hands-onBuild onceDigestDeployment historyHealth verification

Learning objectives

  • Create a disposable Free-tier-compatible pipeline that builds one synthetic artifact and deploys it without rebuilding.
  • Declare a stable environment name, explicit testing deployment tier, and non-routable training URL.
  • Capture source/ref/SHA, pipeline/job IDs, artifact digest, environment metadata, and simulated target receipt before and after an update.
  • Use an environment verification job without creating a second deployment record simply to inspect health.
  • Clean up only the disposable environment/project evidence and leave unrelated environments/deployments untouched.

1. Disposable scenario: one verified artifact, one stable training environment

The mandatory path needs only a disposable GitLab project with a runner and basic POSIX shell tools. It does not create cloud resources, publish packages, or use credentials. The target is a faithful filesystem snapshot simulation stored as deployment evidence. The point is to learn GitLab environment/deployment semantics and provenance before introducing a real provider.

Use a branch such as ch19/environments. The stable GitLab environment is training/ch19, tier testing, URL https://training.invalid/ch19.

2. Preflight: prove project/source/runner state before mutating anything

Record the project path, branch, current SHA, expected runner/executor, and the fact that no real target exists. Use only a throwaway project or branch you are authorized to modify.

git status --short
git rev-parse HEAD
git branch --show-current
printf 'expected environment=%s
' 'training/ch19' 

If the project already has an environment named training/ch19, inspect its history first. Do not overwrite someone else’s lab evidence. Use a project-specific disposable suffix instead while keeping the name stable across this lab.

3. Create deterministic synthetic source and the build-once contract

mkdir -p src ci
cat > src/app.txt <<'EOF'
GitLab CI/CD Chapter 19 training payload
health=ok
EOF
cat > ci/build.sh <<'EOF'
#!/bin/sh
set -eu
mkdir -p dist
cp src/app.txt dist/app.txt
printf 'source_sha=%s
' "${CI_COMMIT_SHA:-local}" > dist/build-metadata.txt
sha256sum dist/app.txt > dist/app.sha256
EOF
chmod +x ci/build.sh

The deployment job will consume dist/app.txt and dist/app.sha256; it will not rerun the build. This makes the provenance rule observable.

4. Pipeline: build → deploy → verify without manufacturing extra deployment history

stages: [build, deploy, verify]

build:release:
  stage: build
  image: alpine:3.22
  script:
    - apk add --no-cache coreutils
    - ./ci/build.sh
    - cat dist/build-metadata.txt
    - cat dist/app.sha256
  artifacts:
    expire_in: 1 week
    paths:
      - dist/app.txt
      - dist/app.sha256
      - dist/build-metadata.txt

deploy:training:
  stage: deploy
  image: alpine:3.22
  needs:
    - job: build:release
      artifacts: true
  resource_group: ch19-training-environment
  script:
    - apk add --no-cache coreutils
    - sha256sum -c dist/app.sha256
    - mkdir -p simulated-target/current evidence
    - cp dist/app.txt simulated-target/current/app.txt
    - cp dist/app.sha256 simulated-target/current/app.sha256
    - printf 'source=%s
sha=%s
pipeline=%s
job=%s
environment=%s
'         "$CI_PIPELINE_SOURCE" "$CI_COMMIT_SHA" "$CI_PIPELINE_ID" "$CI_JOB_ID" "$CI_ENVIRONMENT_NAME"         > evidence/deployment-receipt.txt
    - sha256sum simulated-target/current/app.txt | tee evidence/target-observed.sha256
  environment:
    name: training/ch19
    deployment_tier: testing
    url: https://training.invalid/ch19
  artifacts:
    expire_in: 1 week
    paths:
      - simulated-target/current/
      - evidence/

verify:training:
  stage: verify
  image: alpine:3.22
  needs:
    - job: deploy:training
      artifacts: true
  script:
    - apk add --no-cache coreutils
    - sha256sum -c simulated-target/current/app.sha256
    - diff -u simulated-target/current/app.sha256 evidence/target-observed.sha256 || true
    - grep '^health=ok$' simulated-target/current/app.txt
    - printf 'verified_sha=%s pipeline=%s job=%s
' "$CI_COMMIT_SHA" "$CI_PIPELINE_ID" "$CI_JOB_ID"
  environment:
    name: training/ch19
    action: verify

State changes: build:release creates artifact evidence; deploy:training consumes the exact artifact and creates a GitLab deployment for training/ch19; verify:training consumes target evidence and verifies it but does not pretend another deployment happened.

5. First deployment: inspect before and after

  1. Commit and push the lab configuration.
  2. Record CI_PIPELINE_SOURCE, ref, CI_COMMIT_SHA, pipeline ID, build/deploy/verify job IDs.
  3. Download or inspect dist/app.sha256; record the artifact digest.
  4. Open Operate → Environments → training/ch19. Record the environment ID/state/tier/URL and the deployment ID/status/actor/SHA.
  5. Inspect evidence/deployment-receipt.txt and evidence/target-observed.sha256.
  6. Confirm the verify job is successful and did not create a second ordinary deployment event.

Expected observation: one environment identity, one deployment for the first run, one target snapshot whose observed digest equals the build artifact digest.

6. Update once without changing the environment identity

Make a controlled payload change:

printf '
version=2
' >> src/app.txt
git add src/app.txt
git commit -m 'ch19: update synthetic payload'
git push

Record the second pipeline ID/SHA and new artifact digest. The environment name remains training/ch19, so GitLab appends another deployment to the same history rather than creating a separate environment.

Expected evidence: deployment 2 has the second source SHA, the target snapshot shows the second digest, and deployment 1 remains visible as rollback/audit context.

7. Read deployment history as a provenance chain

Evidence Run 1 Run 2 Why it matters
Source SHA SHA-A SHA-B Names repository revision.
Artifact digest digest-A digest-B Names actual bytes.
Pipeline/job IDs P-A/J-A P-B/J-B Names execution evidence.
Environment training/ch19 training/ch19 Stable target identity.
Deployment ID D-A D-B Names GitLab deployment events.
Target observed digest digest-A digest-B Proves simulated external state after each event.

8. Prove the URL is metadata, not health

The lab URL intentionally uses .invalid. The deployment can be successful even though the URL is not reachable. That is educational: deployment history and target health cannot be collapsed.

In a real system, replace the filesystem read-back with a provider/API or HTTP health/version endpoint and compare a version/digest returned by the target with the expected artifact identity. Treat HTTP 200 without version identity as weak evidence.

9. Optional non-secret environment-scope demonstration

Create a harmless project variable such as TRAINING_LABEL=chapter19 scoped only to training/ch19. Print only the non-secret value from the deployment or verify job to prove environment-scoped availability. Do not use environment-scoped variables in rules or include decisions; pipeline validation can occur before those variables are available.

This is a scoping demonstration, not a secrets lab. Chapter 7 already established that sensitive data needs stronger lifecycle controls.

10. Challenge: choose the correct layer

The deploy job is green, GitLab shows deployment 2 as successful, but the target receipt still reports digest-A. Which layer should you change first?

Do not add retry to YAML. Preserve the deployment/job IDs and inspect the deployment script/provider/target path. The mismatch proves the external-state transition failed or wrote somewhere else; runner retry is not evidence.

11. Cleanup / rollback for the disposable lab

  1. Close/delete only the throwaway branch/project you created.
  2. If you keep the project for the checkpoint, retain the environment history and let the one-week artifacts expire naturally.
  3. If you stop/delete the disposable environment, first record the final deployment IDs and target evidence; note that stopping/deleting GitLab metadata is not proof of external deletion.
  4. Do not delete shared environments, project-wide variables, unrelated artifacts, or deployment history to make the lab look clean.

12. What you proved

You built one artifact per source revision, transferred it to deployment without rebuilding, created repeated deployment events under one stable environment, and used a separate verify action to inspect target evidence. The next lesson decides how to design these identities and lifecycle controls for larger systems.

Knowledge check

Why does deploy:training use needs:artifacts from build:release?

Why does verify:training use environment:action: verify?

What should stay stable across the two lab pipeline runs?

Why is the .invalid URL useful in the training lab?

What is the first diagnostic clue if the target digest remains old after a green deploy job?

Next lesson

Configuration, design choices, and tradeoffs

Choose environment identity, tiers, actions, URL/health semantics, environment-scoped variables, artifact promotion, and rollback retention.

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. Environment/deployment keywords, environment actions, deployment-tier variable support, auto-stop behavior, rollback UI behavior, environment-scoped variables, and deployment safety are version-sensitive. Re-check the GitLab and Runner versions used by production delivery before applying the exact examples.

  • Environments — environment identity/state, tiers, URLs, stop/auto-stop behavior, environment-scoped variables, and operational views.
  • Deployments — deployment history, deployment refs, retry/rollback behavior, and auditability.
  • Deployment safety — deployment serialization, outdated-job prevention, protected environments, and rollback considerations.
  • CI/CD YAML syntax reference — environment, environment:name, url, action, auto_stop_in, deployment_tier, and related job semantics.
  • Deployments API — deployment IDs, statuses, SHA/ref, user, environment, and history queries.
  • Environments API — environment metadata/state/tier management when API access is appropriate.
  • CI/CD variables — environment scope behavior and precedence boundaries.

Current assumptions used in this chapter: environments and deployment history are available on Free, Premium, and Ultimate across GitLab.com, Self-Managed, and Dedicated. Environment states include available, stopping, and stopped. environment:action currently supports start (default, creates a deployment after job start), prepare, verify, access, and stop; the latter four model lifecycle/access work rather than ordinary deployment creation. Deployment tiers are production, staging, testing, development, and other; CI/CD-variable support for deployment_tier was added in GitLab 18.5. auto_stop_in uses natural-language durations; stop processing is background work and is not an exact real-time timer. The mandatory labs use synthetic artifacts, a stable training/ch19 environment, alpine:3.22, a reserved .invalid URL, and filesystem/artifact snapshots as a faithful target simulation—no cloud account, protected production environment, or real credential 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.