Skip to main content

Publish & Promote

Purpose: For platform engineers, shows the image promotion pipeline from dev → staging → production.

Task Summary

This guide walks through publishing a newly built image to Harbor and promoting it across environment-scoped projects until it reaches production.

Prerequisites

  • Harbor account with push access to the target project
  • docker CLI or skopeo installed
  • cosign installed for signature verification
  • Harbor projects created: platform-dev, platform-staging, platform-production

Steps

1. Push the Image to the Dev Project

After a successful CI build, push the image to the dev project:

# Tag for Harbor dev project
docker tag myservice:1.4.2-a1b2c3d \
harbor.opencenter.example.com/platform-dev/myservice:1.4.2-a1b2c3d

# Push
docker push harbor.opencenter.example.com/platform-dev/myservice:1.4.2-a1b2c3d

Harbor's Trivy scanner runs automatically on push. Check the scan result in the Harbor UI under Projects > platform-dev > myservice > 1.4.2-a1b2c3d > Vulnerabilities.

2. Sign the Image

Sign the image after it passes the vulnerability scan:

cosign sign --key cosign.key \
harbor.opencenter.example.com/platform-dev/myservice:1.4.2-a1b2c3d

3. Promote to Staging

Promotion copies the image (including its signature and SBOM) between Harbor projects. Use the Harbor replication API or skopeo:

# Using skopeo (copies manifest, signature, and SBOM)
skopeo copy \
--src-creds admin:${HARBOR_PASSWORD} \
--dest-creds admin:${HARBOR_PASSWORD} \
docker://harbor.opencenter.example.com/platform-dev/myservice:1.4.2-a1b2c3d \
docker://harbor.opencenter.example.com/platform-staging/myservice:1.4.2-a1b2c3d

Harbor re-scans the image on arrival in the staging project. If the staging project has a stricter vulnerability threshold (e.g., block HIGH), the image may be quarantined.

4. Validate in Staging

Deploy the staging-tagged image to the staging cluster. FluxCD ImagePolicy can automate this:

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: myservice
namespace: flux-system
spec:
imageRepositoryRef:
name: myservice-staging
policy:
semver:
range: ">=1.4.0 <2.0.0"

Run integration tests against the staging deployment before promoting further.

5. Promote to Production

After staging validation, copy to the production project:

skopeo copy \
--src-creds admin:${HARBOR_PASSWORD} \
--dest-creds admin:${HARBOR_PASSWORD} \
docker://harbor.opencenter.example.com/platform-staging/myservice:1.4.2-a1b2c3d \
docker://harbor.opencenter.example.com/platform-production/myservice:1.4.2-a1b2c3d

Update the cluster overlay to reference the production image:

# applications/overlays/k8s-production/services/myservice/kustomization.yaml
images:
- name: myservice
newName: harbor.opencenter.example.com/platform-production/myservice
newTag: "1.4.2-a1b2c3d"

Verification

Confirm the image exists in each project with the correct digest:

# Check digest matches across all three projects
skopeo inspect docker://harbor.opencenter.example.com/platform-dev/myservice:1.4.2-a1b2c3d | jq .Digest
skopeo inspect docker://harbor.opencenter.example.com/platform-staging/myservice:1.4.2-a1b2c3d | jq .Digest
skopeo inspect docker://harbor.opencenter.example.com/platform-production/myservice:1.4.2-a1b2c3d | jq .Digest

All three digests must be identical — promotion is a copy, not a rebuild.

Troubleshooting

SymptomCauseFix
Push rejected with 403Missing project permissionsGrant push role in Harbor project settings
Image quarantined after promotionDestination project has stricter CVE policyFix vulnerabilities or adjust threshold
Signature missing after copyskopeo version < 1.14 doesn't copy cosign artifactsUpgrade skopeo or copy signature separately with cosign copy

Further Reading