Docker Image Build and Deployment Workflow
This document describes the standardized Docker image build, versioning, and deployment workflow used in the argocd-eda project. This pattern can be followed by other projects for consistent container lifecycle management.
Overview
The argocd-eda project implements a comprehensive Docker image workflow that includes:
- Local Development: Manual build and push for rapid iteration
- Automated CI/CD: Building via GitHub Actions
- Semantic Versioning: Automatic version bumping
- Multi-architecture Support: AMD64/ARM64 builds
- Security Scanning: Trivy vulnerability scanning
- Registry Integration: GitHub Container Registry (GHCR)
- Image Factory: Centralized lifecycle management
- GitOps Deployment: Kargo promotion pipelines
Workflow Components
1. Local Development Workflow
For rapid development and testing, developers can build and push images locally before triggering automated CI/CD workflows.
Prerequisites
- Docker Authentication: Log in to GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin- Multi-platform Support: Install Docker Buildx for multi-architecture builds
docker buildx create --use --name multiarchLocal Build and Push Process
Local Testing (No Push):
# Build for current platform only (fast local testing)
docker build -t app-name:local .
# Run the application
docker run --rm -it -p 8000:8000 app-name:local
# Get shell access for debugging
docker run --rm -it --entrypoint /bin/bash app-name:localMulti-Architecture Build and Push:
# Build and push for multiple platforms to GHCR
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag ghcr.io/owner/app-name:$(git rev-parse --short HEAD)-dev \
--push .Common Build Tasks (Workspace-Shared)
Recommendation: Yes, create common build tasks in workspace-shared for consistency across projects.
File: libs/workspace-shared/Taskfile.yml
version: '3'
vars:
REGISTRY: ghcr.io
OWNER: '{{.OWNER | default "craigedmunds"}}'
tasks:
docker:build:local:
desc: Build Docker image for local testing (no push)
vars:
APP_NAME: '{{.APP_NAME}}'
DOCKERFILE: '{{.DOCKERFILE}}'
CONTEXT: '{{.CONTEXT}}'
cmds:
- docker build -f {{.DOCKERFILE}} -t {{.APP_NAME}}:local {{.CONTEXT}}
docker:run:
desc: Run Docker container from local image
vars:
APP_NAME: '{{.APP_NAME}}'
PORTS: '{{.PORTS}}'
ENV_VARS: '{{.ENV_VARS}}'
VOLUMES: '{{.VOLUMES}}'
cmds:
- docker run --rm -it {{.PORTS}} {{.ENV_VARS}} {{.VOLUMES}} {{.APP_NAME}}:local
docker:shell:
desc: Get shell access to Docker container
vars:
APP_NAME: '{{.APP_NAME}}'
SHELL: '{{.SHELL | default "/bin/bash"}}'
VOLUMES: '{{.VOLUMES}}'
cmds:
- docker run --rm -it {{.VOLUMES}} --entrypoint {{.SHELL}} {{.APP_NAME}}:local
docker:build:multiarch:
desc: Build and push multi-architecture Docker image
vars:
APP_NAME: '{{.APP_NAME}}'
DOCKERFILE: '{{.DOCKERFILE}}'
CONTEXT: '{{.CONTEXT}}'
TAG: '{{.TAG | default (printf "%s-dev" (exec "git" "rev-parse" "--short" "HEAD"))}}'
PLATFORMS: '{{.PLATFORMS | default "linux/amd64,linux/arm64"}}'
cmds:
- docker buildx build --platform {{.PLATFORMS}} -f {{.DOCKERFILE}} -t {{.REGISTRY}}/{{.OWNER}}/{{.APP_NAME}}:{{.TAG}} --push {{.CONTEXT}}
docker:scan:
desc: Scan Docker image for vulnerabilities with Trivy
vars:
APP_NAME: '{{.APP_NAME}}'
TAG: '{{.TAG | default (printf "%s-dev" (exec "git" "rev-parse" "--short" "HEAD"))}}'
cmds:
- trivy image {{.REGISTRY}}/{{.OWNER}}/{{.APP_NAME}}:{{.TAG}}Project-Specific Task Integration
Multiple Apps Per Repo - Include with Prefixes:
version: '3'
includes:
gateway:
taskfile: ../libs/workspace-shared/Taskfile.yml
vars:
APP_NAME: mobile-ai-gateway
DOCKERFILE: services/gateway/Dockerfile
CONTEXT: services/gateway
PORTS: "-p 8000:8000"
ENV_VARS: "-e ENV=development"
VOLUMES: "-v $(pwd)/config:/app/config"
worker:
taskfile: ../libs/workspace-shared/Taskfile.yml
vars:
APP_NAME: mobile-ai-worker
DOCKERFILE: services/worker/Dockerfile
CONTEXT: services/worker
ENV_VARS: "-e ENV=development"
VOLUMES: "-v $(pwd)/logs:/app/logs"
# Now you can call:
# task gateway:docker:build:local
# task gateway:docker:run
# task worker:docker:build:local
# task worker:docker:runDevelopment Workflow Integration
Local Development → GitHub Actions Pipeline:
- Local Development:
# Gateway service
task gateway:docker:build:local
task gateway:docker:run
task gateway:docker:shell
# Worker service
task worker:docker:build:local
task worker:docker:run
task worker:docker:shell
# When ready, build and push multi-arch for integration testing
task gateway:docker:build:multiarch
task worker:docker:build:multiarch- Trigger GitHub Actions:
# Push to feature branch triggers draft build
git push origin feature/new-feature
# Push to main triggers production build
git push origin main2. Reusable Docker Build Workflow
File: .github/workflows/_docker-build.yml
This is a reusable workflow that standardizes the Docker build process across all applications. It provides:
Key Features:
- Semantic Versioning: Automatic version bumping (major/minor/patch)
- Branch-based Tagging: Feature branches get
-draft-{branch}suffix - Multi-platform Builds: AMD64 and ARM64 support
- Security Scanning: Trivy vulnerability scanning
- Registry Integration: GitHub Container Registry (GHCR)
- Automated Releases: GitHub releases with image metadata
Version Management:
- File-based: Simple VERSION file (e.g.,
0.2.0) - Package.json-based: For Node.js applications
- Auto-detection: Commit message parsing for version bump type
[major]orbreaking change→ major bump[minor]orfeat:→ minor bump- Default → patch bump
Branch Strategy:
- Main branch: Production builds with
latesttag - Feature branches: Draft builds with branch suffix
- Version commits: Automatic version file updates on main
3. Application-Specific Workflows
Each application has its own workflow that calls the reusable build workflow:
Example: UV Service (uv.yml)
jobs:
build-and-push:
uses: ./.github/workflows/_docker-build.yml
with:
app_name: 'UV'
image_name: 'craigedmunds/uv'
dockerfile: 'apps/uv/Dockerfile'
context: 'apps/uv'
version_file: 'apps/uv/VERSION'
version_type: 'file'Example: Backstage (backstage.yml)
jobs:
build-and-push:
uses: ./.github/workflows/_docker-build.yml
with:
app_name: 'Backstage'
image_name: 'craigedmunds/backstage'
dockerfile: 'backstage/app/packages/backend/Dockerfile'
context: 'backstage/app'
version_file: 'backstage/app/package.json'
version_type: 'package-json'
pre_build_script: |
cd backstage/app
cp .yarnrc.ci.yml .yarnrc.yml4. Image Factory Integration
File: image-factory/images.yaml
The Image Factory provides centralized container lifecycle management:
Image Registration:
- name: backstage
registry: ghcr.io
repository: craigedmunds/backstage
source:
provider: github
repo: craigedmunds/argocd-eda
branch: main
dockerfile: backstage/app/packages/backend/Dockerfile
workflow: backstage.yml
rebuildDelay: 7d
autoRebuild: trueKey Features:
- Dependency Tracking: Monitors base image updates
- Automated Rebuilds: Triggers rebuilds when dependencies change
- State Management: Centralized image configuration
- Kargo Integration: GitOps deployment pipelines
Implementation Guide
Step 0: Set Up Local Development Environment
Option A: Colima (Lightweight - Recommended for macOS)
Colima is a lightweight alternative to Docker Desktop that uses minimal resources and provides excellent multi-architecture build support on macOS.
Installation:
# Install Colima via Homebrew
brew install colima docker docker-buildx
# Start Colima with optimized settings
colima start --cpu 4 --memory 4 --arch aarch64 --vm-type=vz --vz-rosettaConfiguration:
--cpu 4 --memory 4: Allocate 4 CPUs and 4GB RAM (adjust based on your needs)--arch aarch64: Use ARM64 architecture (native on Apple Silicon)--vm-type=vz: Use Apple’s Virtualization framework (faster than QEMU)--vz-rosetta: Enable Rosetta for x86_64 emulation (required for multi-arch builds)
Verify Setup:
# Check Colima status
colima status
# Verify Docker is working
docker ps
# Verify buildx is available
docker buildx lsMulti-Architecture Builds: Colima with Rosetta provides excellent multi-arch build performance:
# Build for both AMD64 and ARM64
docker buildx build --platform linux/amd64,linux/arm64 -t myimage:latest .Benefits:
- Lightweight: Uses significantly less resources than Docker Desktop
- Fast: Native Apple Virtualization framework
- Free: No licensing concerns
- Multi-arch: Excellent support for AMD64/ARM64 builds via Rosetta
Managing Colima:
# Stop Colima
colima stop
# Restart Colima
colima restart
# Delete Colima VM (clean slate)
colima deleteOption B: Docker Desktop (Full-Featured)
If you need Docker Desktop’s additional features:
Prerequisites:
- Install Docker Desktop: Download from docker.com
- Enable Buildx: Already included in Docker Desktop
Create Multi-Platform Builder:
docker buildx create --use --name multiarchCommon Setup Steps (Both Options)
- Configure GitHub Container Registry Access:
# Create GitHub Personal Access Token with packages:write scope
# Then authenticate
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin- Set Up Workspace-Shared Tasks:
# Create shared Taskfile if it doesn't exist
mkdir -p libs/workspace-shared
# Copy the common build tasks from the example aboveStep 1: Create Reusable Build Workflow
- Copy the reusable workflow: Use
_docker-build.ymlas a template - Customize parameters: Adjust registry, security scanning, etc.
- Configure secrets: Set up
GHCR_TOKENandSUBMODULE_PAT
Step 2: Create Application Workflows
For each application that needs Docker images:
- Create workflow file:
.github/workflows/{app-name}.yml - Configure triggers: Set up path-based triggers
- Call reusable workflow: Pass application-specific parameters
- Add testing: Include pre-build validation (kustomize tests, unit tests)
Workflow Template:
name: Build and Push {App Name}
on:
push:
branches: [main]
paths:
- 'path/to/app/**'
- '.github/workflows/{app-name}.yml'
- '.github/workflows/_docker-build.yml'
workflow_dispatch:
inputs:
version_bump:
description: 'Version bump type'
required: false
default: 'patch'
type: choice
options: [patch, minor, major]
jobs:
build-and-push:
uses: ./.github/workflows/_docker-build.yml
with:
app_name: '{App Name}'
image_name: '{registry}/{image-name}'
dockerfile: 'path/to/Dockerfile'
context: 'path/to/context'
version_file: 'path/to/VERSION'
version_type: 'file' # or 'package-json'
secrets: inheritStep 3: Set Up Version Management
Choose a versioning strategy:
Option A: File-based (Simple)
- Create
VERSIONfile with semantic version (e.g.,1.0.0) - Set
version_type: 'file'in workflow
Option B: Package.json-based (Node.js)
- Use existing
package.jsonversion field - Set
version_type: 'package-json'in workflow
Step 4: Configure Image Factory (Optional)
For advanced lifecycle management:
- Add image definition: Update
image-factory/images.yaml - Configure source: Link to GitHub workflow
- Set rebuild policy: Define dependency tracking and rebuild delays
- Deploy Image Factory: Use CDK8s infrastructure definitions
Step 5: Set Up GitOps Integration
For deployment automation:
- Kargo Stages: Define promotion stages (dev → staging → prod)
- Analysis Templates: Configure image analysis and testing
- Freight Management: Automate image promotion between environments
Best Practices
Local Development
- Local Testing Only: Build local images for testing without pushing to registry
- Multi-Arch for Registry: Only push multi-architecture builds to GHCR
- Consistent Tagging: Use git commit hash with
-devsuffix for development tags (abc123-dev) - Registry Authentication: Keep GHCR authentication current and secure
Security
- Trivy Scanning: Always include vulnerability scanning
- SARIF Upload: Integrate security results with GitHub Security tab
- Registry Security: Use GitHub Container Registry with proper permissions
Versioning
- Semantic Versioning: Follow semver principles
- Branch Naming: Use descriptive feature branch names
- Commit Messages: Include version bump hints in commit messages
Performance
- Build Caching: Use GitHub Actions cache for Docker layers
- Multi-stage Builds: Optimize Dockerfile for smaller images
- Parallel Builds: Leverage multi-architecture builds efficiently
Monitoring
- Build Notifications: Set up alerts for build failures
- Registry Monitoring: Track image sizes and vulnerability counts
- Deployment Tracking: Monitor image promotion through environments
Troubleshooting
Common Issues
Build Failures
- Check Dockerfile syntax and build context
- Verify all required files are included in context
- Review build logs for dependency issues
Version Conflicts
- Ensure version file format matches
version_type - Check for concurrent builds causing race conditions
- Verify git permissions for version commits
Registry Issues
- Confirm
GHCR_TOKENhas proper permissions - Check registry quotas and rate limits
- Verify image name format and registry URL
Debugging Commands
# Local development debugging
docker images | grep ghcr.io
docker buildx ls
docker buildx inspect multiarch
# Colima-specific debugging
colima status
colima list
docker context ls
# Check workflow status
gh workflow list
gh run list --workflow="{app-name}.yml"
# View build logs
gh run view {run-id} --log
# Test Docker build locally
docker build -t test-image -f path/to/Dockerfile path/to/context
# Test multi-arch build locally
docker buildx build --platform linux/amd64,linux/arm64 -t test-image .
# Check image in registry
docker pull ghcr.io/{owner}/{image}:{tag}
docker inspect ghcr.io/{owner}/{image}:{tag}
# Scan local image
trivy image ghcr.io/{owner}/{image}:{tag}Colima-Specific Issues
Colima Not Starting
# Check logs
colima logs
# Delete and recreate
colima delete
colima start --cpu 4 --memory 4 --arch aarch64 --vm-type=vz --vz-rosettaMulti-Arch Build Failures
- Ensure Rosetta is enabled:
--vz-rosettaflag when starting Colima - Verify buildx is using the correct builder:
docker buildx ls - Check available platforms:
docker buildx inspect --bootstrap
Docker Context Issues
# List contexts
docker context ls
# Switch to Colima context
docker context use colima
# Verify
docker psIntegration with Other Systems
Backstage Integration
- Images appear in Backstage catalog
- Build status and metadata visible in developer portal
- Links to GitHub releases and registry
Kargo Integration
- Automated promotion pipelines
- Image analysis and testing stages
- GitOps-based deployment workflows
Monitoring Integration
- Build metrics in observability dashboards
- Security scan results in security tools
- Deployment tracking across environments
Migration Guide
For Existing Projects
To adopt this workflow in existing projects:
- Assessment: Review current build processes and identify applications
- Local Setup: Configure Docker Buildx and GHCR authentication
- Shared Tasks: Create or update workspace-shared Taskfile with common build tasks
- Project Integration: Add project-specific tasks that use shared tasks
- GitHub Actions: Create workflows following the template above
- Testing: Validate local builds before setting up automated workflows
- Migration: Gradually move applications to new workflow
- Monitoring: Set up alerts and monitoring for new build process
Development Workflow Migration
Before: Manual docker commands
docker build -t my-app .
docker tag my-app ghcr.io/owner/my-app:latest
docker push ghcr.io/owner/my-app:latestAfter: Task-based workflow
# Local testing (no push)
task docker:build:my-app:local
# Multi-arch build and push to registry
task docker:build:my-appThis workflow provides a robust, scalable foundation for container lifecycle management that can be adapted to various project needs while maintaining consistency and best practices.