Registry Publishing
Publishing your module to container registries makes it available for deployment across clusters. This guide covers registry options and publishing workflows.
Container Registries
Docker Hub
bash
# Login
docker login
# Tag image
docker tag my-module:v1.0.0 myorg/my-module:v1.0.0
# Push
docker push myorg/my-module:v1.0.0GitHub Container Registry (ghcr.io)
bash
# Login with PAT
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Tag
docker tag my-module:v1.0.0 ghcr.io/myorg/my-module:v1.0.0
# Push
docker push ghcr.io/myorg/my-module:v1.0.0Google Container Registry
bash
# Configure auth
gcloud auth configure-docker
# Tag
docker tag my-module:v1.0.0 gcr.io/my-project/my-module:v1.0.0
# Push
docker push gcr.io/my-project/my-module:v1.0.0Amazon ECR
bash
# Get login token
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
# Tag
docker tag my-module:v1.0.0 123456789.dkr.ecr.us-east-1.amazonaws.com/my-module:v1.0.0
# Push
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/my-module:v1.0.0Azure Container Registry
bash
# Login
az acr login --name myregistry
# Tag
docker tag my-module:v1.0.0 myregistry.azurecr.io/my-module:v1.0.0
# Push
docker push myregistry.azurecr.io/my-module:v1.0.0Helm Chart Registries
OCI-based Registry
bash
# Login
helm registry login ghcr.io -u USERNAME -p $GITHUB_TOKEN
# Package chart
helm package ./charts/my-module
# Push
helm push my-module-1.0.0.tgz oci://ghcr.io/myorg/chartsChartMuseum
bash
# Add repo
helm repo add myrepo https://charts.example.com
# Push (requires chartmuseum-push plugin)
helm cm-push ./charts/my-module myrepoGitHub Pages
bash
# Package chart
helm package ./charts/my-module -d ./docs
# Generate index
helm repo index ./docs --url https://myorg.github.io/charts
# Commit and push to gh-pages branchAutomated Publishing
GitHub Actions - Container
yaml
name: Publish Container
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=maxGitHub Actions - Helm Chart
yaml
name: Publish Helm Chart
on:
push:
tags:
- 'v*'
jobs:
publish-chart:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
- name: Login to GHCR
run: |
echo ${{ secrets.GITHUB_TOKEN }} | \
helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Update Chart version
run: |
sed -i "s/^version:.*/version: ${{ steps.version.outputs.VERSION }}/" charts/my-module/Chart.yaml
sed -i "s/^appVersion:.*/appVersion: \"${{ steps.version.outputs.VERSION }}\"/" charts/my-module/Chart.yaml
- name: Package chart
run: helm package ./charts/my-module
- name: Push chart
run: |
helm push my-module-${{ steps.version.outputs.VERSION }}.tgz \
oci://ghcr.io/${{ github.repository_owner }}/chartsGitLab CI
yaml
stages:
- build
- publish
variables:
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_NAME: $CI_REGISTRY_IMAGE
build:
stage: build
image: docker:24-dind
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
publish:
stage: publish
image: docker:24-dind
services:
- docker:dind
only:
- tags
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker pull $IMAGE_NAME:$CI_COMMIT_SHA
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:$CI_COMMIT_TAG
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
- docker push $IMAGE_NAME:$CI_COMMIT_TAG
- docker push $IMAGE_NAME:latestImage Signing
Cosign
bash
# Install cosign
brew install cosign
# Generate key pair
cosign generate-key-pair
# Sign image
cosign sign --key cosign.key ghcr.io/myorg/my-module:v1.0.0
# Verify signature
cosign verify --key cosign.pub ghcr.io/myorg/my-module:v1.0.0Keyless Signing with GitHub Actions
yaml
- name: Sign image
uses: sigstore/cosign-installer@v3
- name: Sign with OIDC
run: cosign sign --yes ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.version }}
env:
COSIGN_EXPERIMENTAL: 1Security Scanning
Trivy
yaml
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.version }}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'Snyk
yaml
- name: Run Snyk
uses: snyk/actions/docker@master
with:
image: ghcr.io/${{ github.repository }}:${{ steps.meta.outputs.version }}
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}Image Tags
Tagging Strategy
| Tag Pattern | Example | Use Case |
|---|---|---|
| Semantic version | v1.2.3 | Production releases |
| Major.Minor | v1.2 | Latest patch in minor |
| Major only | v1 | Latest in major |
latest | latest | Most recent stable |
| SHA | sha-abc1234 | Exact commit |
| Branch | main | Development |
Implementation
yaml
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=sha,prefix=sha-Private Registries
Kubernetes Image Pull Secret
bash
# Create secret
kubectl create secret docker-registry regcred \
--docker-server=ghcr.io \
--docker-username=myuser \
--docker-password=$GITHUB_TOKEN \
--docker-email=me@example.com \
-n tinysystemsIn Helm Values
yaml
# values.yaml
imagePullSecrets:
- name: regcred
image:
repository: ghcr.io/myorg/my-module
tag: "v1.0.0"
pullPolicy: IfNotPresentService Account
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-module
namespace: tinysystems
imagePullSecrets:
- name: regcredMulti-Architecture Images
Building with Buildx
bash
# Create builder
docker buildx create --name multiarch --use
# Build and push multi-arch
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/myorg/my-module:v1.0.0 \
--push .Inspect Manifest
bash
# View platforms
docker manifest inspect ghcr.io/myorg/my-module:v1.0.0Release Checklist
Before publishing a release:
Version bump
- Update
Chart.yamlversion and appVersion - Update any version references in code
- Update
Testing
- All tests pass
- Integration tests complete
- Image builds successfully
Documentation
- CHANGELOG updated
- README current
- API docs generated
Security
- Vulnerability scan passes
- No secrets in image
- Base image updated
Tagging
bashgit tag -a v1.0.0 -m "Release v1.0.0" git push origin v1.0.0
Next Steps
- Versioning - Version strategy
- Helm Charts - Chart packaging
- Building Modules - Build process