✦ For everyone, free.

Practical knowledge for real and everyday life

Home

19.1.4.5 Push Pipeline Publishing

A focused guide to Push Pipeline Publishing, connecting core concepts with practical Docker and container operations.

Pipeline publishing refers to automating the process of building, tagging, and pushing Docker images as part of a CI/CD pipeline. Instead of manually running docker build and docker push from a developer workstation, the pipeline executes these steps automatically on every relevant code event — such as a commit to a specific branch, a merged pull request, or a tagged release. The result is a reproducible, auditable, and consistent publishing workflow that does not depend on any individual's local environment.

The Core Pipeline Sequence

A Docker image publishing pipeline typically follows this sequence:

Checkout Build Test Tag Push

Each stage is a shell command or a pipeline job that runs in a clean, isolated runner environment.

GitHub Actions Example

GitHub Actions is one of the most widely used CI platforms for Docker publishing. A workflow that builds and pushes on every push to main:

name: Build and Push Docker Image

on:
  push:
    branches:
      - main

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source
        uses: actions/checkout@v4

      - name: Log in to Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Tag as latest
        run: docker tag myapp:${{ github.sha }} mydockerhubuser/myapp:latest

      - name: Push versioned tag
        run: docker push mydockerhubuser/myapp:${{ github.sha }}

      - name: Push latest tag
        run: docker push mydockerhubuser/myapp:latest

Secrets (DOCKER_USERNAME, DOCKER_PASSWORD) are stored in the repository's GitHub Secrets settings and injected as environment variables at runtime. They never appear in logs.

GitLab CI Example

GitLab CI provides built-in variables for its integrated registry:

stages:
  - build
  - push

build-image:
  stage: build
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .

push-image:
  stage: push
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main

CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, and CI_REGISTRY_IMAGE are all automatically populated by GitLab for every pipeline run.

Jenkins Pipeline Example (Declarative)

pipeline {
    agent any
    environment {
        REGISTRY = 'myregistry.example.com'
        IMAGE = "${REGISTRY}/myapp"
        TAG = "${GIT_COMMIT}"
    }
    stages {
        stage('Build') {
            steps {
                sh "docker build -t ${IMAGE}:${TAG} ."
            }
        }
        stage('Push') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'registry-creds', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
                    sh "echo $PASS | docker login ${REGISTRY} -u $USER --password-stdin"
                    sh "docker push ${IMAGE}:${TAG}"
                    sh "docker tag ${IMAGE}:${TAG} ${IMAGE}:latest"
                    sh "docker push ${IMAGE}:latest"
                }
            }
        }
    }
}

Tag Strategy in Pipelines

Pipelines typically generate multiple tags per build to serve different consumers:

TagPurpose
$GIT_SHAExact traceability — immutable identifier
$SEMVER (e.g. 1.4.2)Release versioning for production deployments
$BRANCH (e.g. main)Latest build on a specific branch
latestMost recent stable release
$DATE (e.g. 20240615)Chronological rollback reference

A pipeline that generates all of these:

VERSION="1.4.2"
GIT_SHA=$(git rev-parse --short HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
DATE=$(date +%Y%m%d)
REGISTRY="myregistry.example.com/myapp"

docker build -t $REGISTRY:$VERSION .

docker tag $REGISTRY:$VERSION $REGISTRY:$GIT_SHA
docker tag $REGISTRY:$VERSION $REGISTRY:$BRANCH
docker tag $REGISTRY:$VERSION $REGISTRY:$DATE
docker tag $REGISTRY:$VERSION $REGISTRY:latest

docker push $REGISTRY:$VERSION
docker push $REGISTRY:$GIT_SHA
docker push $REGISTRY:$BRANCH
docker push $REGISTRY:$DATE
docker push $REGISTRY:latest

Conditional Publishing

Not every pipeline run should publish an image. Common conditions:

  • Push only on the main or release/* branches.
  • Push only when tests pass.
  • Push only when a version tag is created (e.g., v* pattern).

GitHub Actions conditional example:

- name: Push image
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  run: docker push mydockerhubuser/myapp:latest

Multi-Platform Builds in Pipelines

Pipelines can produce images for multiple CPU architectures using docker buildx:

docker buildx create --use
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag myregistry.example.com/myapp:1.0.0 \
  --push \
  .

The --push flag in buildx pushes directly to the registry as part of the build, bypassing the local image store.

Caching Layers Between Runs

Pipeline runners are stateless, so Docker build cache is lost between runs by default. Cache can be preserved using registry-backed caching:

docker buildx build \
  --cache-from type=registry,ref=myregistry.example.com/myapp:cache \
  --cache-to type=registry,ref=myregistry.example.com/myapp:cache,mode=max \
  --tag myregistry.example.com/myapp:1.0.0 \
  --push \
  .

This significantly reduces build times for images with stable base layers.

Security Considerations

  • Store registry credentials as encrypted secrets, never in code or plain environment variables.
  • Use short-lived tokens (e.g., AWS ECR tokens expire after 12 hours) and refresh them before each pipeline run.
  • Scope credentials to only the permissions needed (push to a specific repository, not all repositories).
  • Scan images before publishing using tools integrated into the pipeline step between build and push.