Module 03

Stages & Promotions

Checkpoints in your delivery pipeline and the recipes for deploying changes

"You have Freight. A sealed package with image v1.5.0 and commit abc123. It's sitting there, waiting.

But where does it go? How does it get there? What happens when it arrives?

That's the job of Stages and Promotions."

What is a Stage?

A Checkpoint in Your Pipeline

A Stage represents an environment (dev, staging, prod) or any logical checkpoint where Freight is deployed and verified.

Requesting Freight

Direct from Warehouse

The Stage requests Freight directly from a Warehouse. Typically used for the first Stage (e.g., dev).

requestedFreight:
- origin:
    kind: Warehouse
    name: my-warehouse
  sources:
    direct: true

From Upstream Stages

The Stage only accepts Freight that has been verified in an upstream Stage. Used for staging, prod.

requestedFreight:
- origin:
    kind: Warehouse
    name: my-warehouse
  sources:
    stages:
    - dev

Why Upstream Stages Matter

Warehouse creates Freight
direct
dev — promotes and verifies Freight
only verified Freight can proceed
staging — promotes and verifies
only verified Freight can proceed
prod — promotes with approval

This ensures no Freight reaches prod without passing through dev and staging first.

Promotion Templates

The Recipe for Deploying

A Promotion Template is a list of steps that Kargo executes when promoting Freight to a Stage. Each step is a built-in function.

Quiz 1 — Stages Basics

1. How does a staging Stage ensure it only receives tested Freight?

2. What is a Promotion Template?

3. Which Stage typically requests Freight directly from a Warehouse?

The Git Update Workflow

Most promotions follow this pattern: clone → update → commit → push

1 git-clone — Clone the GitOps repo
2 git-update — Update image tags, chart versions in files
3 git-commit — Commit with a descriptive message
4 git-push — Push to the remote branch
5 argocd-update — (Optional) Trigger ArgoCD sync

Step 1: git-clone

promotionTemplate:
  steps:
  - uses: git-clone
    config:
      repoURL: https://github.com/my-org/k8s-config.git
      checkout:
      - branch: main
        path: ./src
    # Output: working directory at ./src

Clones the repository to a temporary working directory. The path defines where subsequent steps can find the files.

Step 2: git-update

  - uses: git-update
    config:
      path: ./src
      updates:
      # Update image tag in values.yaml
      - file: apps/my-web-app/values.yaml
        key: image.tag
        value: ${{ freight.images["myacr.azurecr.io/my-web-app"].tag }}
      # Update chart version
      - file: apps/my-web-app/Chart.yaml
        key: version
        value: ${{ freight.charts["my-app"].version }}

Uses expressions to inject Freight values into your YAML files.

Freight Expressions

Accessing Freight Data in Steps

These expressions are evaluated at promotion time using the specific Freight being promoted.

Step 3: git-commit

  - uses: git-commit
    as: commit
    config:
      path: ./src
      messageFromSteps:
      - git-update   # auto-generates commit message from changes
      # OR custom message:
      # message: "chore: promote my-web-app to ${{ ctx.stage }}"

The as: commit alias lets later steps reference the commit output (e.g., the commit SHA).

Step 4: git-push

  - uses: git-push
    config:
      path: ./src
      # Push to the same branch (main)
      # OR push to an environment-specific branch:
      targetBranch: env/dev

Two Push Strategies

Step 5: argocd-update

  - uses: argocd-update
    config:
      apps:
      - name: my-web-app-dev
        sources:
        - repoURL: https://github.com/my-org/k8s-config.git
          # Update the target revision to the commit we pushed
          desiredCommitFromStep: commit

Optionally tells ArgoCD to update the Application's target revision to the exact commit Kargo just pushed. This triggers an immediate sync rather than waiting for ArgoCD polling.

Complete Stage Definition

apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
  name: dev
  namespace: my-web-app
spec:
  requestedFreight:
  - origin:
      kind: Warehouse
      name: my-warehouse
    sources:
      direct: true
  promotionTemplate:
    steps:
    - uses: git-clone
      config:
        repoURL: https://github.com/my-org/k8s-config.git
        checkout:
        - branch: main
          path: ./src
    - uses: git-update
      config:
        path: ./src
        updates:
        - file: apps/my-web-app/values.yaml
          key: image.tag
          value: ${{ freight.images["myacr.azurecr.io/my-web-app"].tag }}
    - uses: git-commit
      as: commit
      config:
        path: ./src
        messageFromSteps:
        - git-update
    - uses: git-push
      config:
        path: ./src

Quiz 2 — Promotion Steps

1. What is the correct order of Git promotion steps?

2. How do you reference an image tag from Freight in a promotion step?

3. What does the argocd-update step do?

Helm Values Updates

# values.yaml before promotion:
image:
  repository: myacr.azurecr.io/my-web-app
  tag: "1.4.0"     # ← Kargo updates this

replicaCount: 3

# values.yaml after promotion:
image:
  repository: myacr.azurecr.io/my-web-app
  tag: "1.5.0"     # ← Updated by git-update step

replicaCount: 3    # untouched

The git-update step surgically modifies only the specified keys.

Multi-Stage Pipeline

The real power: dev → staging → prod

dev Stage
• requestedFreight: direct from Warehouse
• autoPromotion: true
verified
staging Stage
• requestedFreight: from stages [dev]
• autoPromotion: false (manual trigger)
verified + approved
prod Stage
• requestedFreight: from stages [staging]
• autoPromotion: false (requires approval)

Staging Stage Definition

apiVersion: kargo.akuity.io/v1alpha1
kind: Stage
metadata:
  name: staging
  namespace: my-web-app
spec:
  requestedFreight:
  - origin:
      kind: Warehouse
      name: my-warehouse
    sources:
      stages:
      - dev        # Only accept Freight verified in dev
  promotionTemplate:
    steps:
    - uses: git-clone
      config:
        repoURL: https://github.com/my-org/k8s-config.git
        checkout:
        - branch: main
          path: ./src
    - uses: git-update
      config:
        path: ./src
        updates:
        - file: apps/my-web-app/staging-values.yaml
          key: image.tag
          value: ${{ freight.images["myacr.azurecr.io/my-web-app"].tag }}
    - uses: git-commit
      as: commit
      config:
        path: ./src
        messageFromSteps: [git-update]
    - uses: git-push
      config:
        path: ./src

Auto-Promotion vs Manual Promotion

Auto-Promotion

Freight is promoted automatically when it becomes available. No human intervention.

# In the Project spec:
promotionPolicies:
- stage: dev
  autoPromotionEnabled: true

Best for: dev, feature environments

Manual Promotion

Someone must explicitly trigger the promotion via UI or CLI.

# In the Project spec:
promotionPolicies:
- stage: prod
  autoPromotionEnabled: false

Best for: staging, production

Triggering a Manual Promotion

# Via CLI
kargo promote --project my-web-app \
  --stage staging \
  --freight jolly-penguin

# Via kubectl (create a Promotion resource)
apiVersion: kargo.akuity.io/v1alpha1
kind: Promotion
metadata:
  name: promote-staging-abc123
  namespace: my-web-app
spec:
  stage: staging
  freight: abc123def456   # Freight name or alias

# Via UI: drag Freight onto the Stage!

Promotion Status Tracking

# Check promotion status
kargo get promotions --project my-web-app

# NAME                      STAGE     FREIGHT          STATUS
# promote-staging-abc123    staging   jolly-penguin    Succeeded
# promote-dev-abc123        dev       jolly-penguin    Succeeded

# Detailed view
kargo get promotion promote-staging-abc123 \
  --project my-web-app -o yaml

Statuses: Pending Running Succeeded Failed Aborted

Approval Workflows

Gating Promotions with Approvals

For sensitive Stages (production), you can require Freight to be explicitly approved before promotion.

# Approve Freight for the prod Stage
kargo approve --project my-web-app \
  --stage prod \
  --freight jolly-penguin

Quiz 3 — Multi-Stage Pipelines

1. In a dev → staging → prod pipeline, what does staging's requestedFreight look like?

2. Where is auto-promotion configured?

3. How can you manually approve Freight for a Stage?

Pattern: Environment Branches

One Branch per Environment

Instead of one main branch, use separate branches for each environment:

# In the dev Stage's git-push step:
- uses: git-push
  config:
    path: ./src
    targetBranch: env/dev

Pattern: Directory per Environment

All Environments in One Branch

Use a single branch with directories per environment:

k8s-config/
  apps/my-web-app/
    dev/
      values.yaml       # image.tag: 1.5.0
    staging/
      values.yaml       # image.tag: 1.4.0
    prod/
      values.yaml       # image.tag: 1.3.0
    Chart.yaml

Each Stage's git-update step targets the corresponding directory.

Reusing Promotion Templates

Avoid Duplication

When dev, staging, and prod share similar promotion logic, you can use:

# Dynamic path using context
- uses: git-update
  config:
    path: ./src
    updates:
    - file: apps/my-web-app/${{ ctx.stage }}/values.yaml
      key: image.tag
      value: ${{ freight.images["myacr.azurecr.io/my-web-app"].tag }}

Multiple Freight Origins

A Stage can request Freight from multiple Warehouses. Each origin provides different artifact types.

spec:
  requestedFreight:
  - origin:
      kind: Warehouse
      name: app-images      # container images
    sources:
      direct: true
  - origin:
      kind: Warehouse
      name: config-repo     # Git config changes
    sources:
      direct: true

Useful when images and config are managed separately.

Quiz 4 — Patterns & Advanced Promotion

1. What does ${{ ctx.stage }} evaluate to in a promotion step?

2. In the environment branches pattern, what does each ArgoCD Application watch?

3. Can a Stage request Freight from multiple Warehouses?

Key Takeaways

Up Next

Module 04: Verification & Advanced Patterns

Trust but verify. AnalysisTemplates, parallel stages, rollbacks, and more.

Verification AnalysisTemplates Advanced Patterns
← Back