This directory contains GitHub Actions workflows for automated Docker multi-arch builds.
Builds Docker images for multiple CPU architectures (ARM64, AMD64) and pushes them to Docker Hub.
Triggers:
- Manual (workflow_dispatch) - Full control via GitHub UI
- Push to
ci/dockerbranch - Auto-build and push - Git tags - Auto-build and push with tag
- Pull requests - Build only (no push)
Architecture:
The workflow consists of 4 stages:
- setup_matrix - Determines what to build based on inputs/triggers
- build_dependencies - Builds dependencies image (only when needed)
- build - Builds builder and runtime images on native runners (ARM64 + AMD64)
- create_manifest - Creates unified multi-arch manifest
Key Features:
- ✅ Native builds on ARM64 and AMD64 runners (fast, no emulation)
- ✅ Parallel builds on different architectures
- ✅ Smart dependency caching (rebuild only when
vcpkg.jsonchanges) - ✅ Multi-arch manifest (one tag works on both architectures)
- ✅ Flexible tagging (commit hash + optional custom tag + optional latest)
- ✅ Auto-push on branch/tag pushes
-
Go to Actions → Docker Build → Run workflow
-
Select parameters:
- Build linux/amd64 - Build for AMD64 (default: yes)
- Build linux/arm64 - Build for ARM64 (default: yes)
- Build dependencies image - Rebuild vcpkg dependencies (default: no)
- Push to Docker Hub - Push images to registry (default: no)
- Push additional custom tag - Optional tag (e.g.,
v1.0.0,staging) - Push 'latest' tag - Also tag as
latest(default: no) - Dependencies image tag - Which deps tag to use/create (default:
latest)
-
Click Run workflow
Example scenarios:
# Scenario 1: Build both architectures, don't push (testing)
build_amd64: true
build_arm64: true
push_to_registry: false
# Scenario 2: Build and push with version tag
build_amd64: true
build_arm64: true
push_to_registry: true
docker_push_tag: "v1.0.0"
docker_push_latest: true # Also tag as 'latest'
# Scenario 3: Rebuild dependencies (vcpkg.json changed)
build_dependencies: true
push_to_registry: true
docker_deps_tag: "latest"
# Scenario 4: Build only ARM64 for testing
build_amd64: false
build_arm64: true
push_to_registry: falsePush to ci/docker branch:
git push origin ci/docker- Builds both ARM64 and AMD64
- Automatically pushes to Docker Hub
- Tags:
qlean-mini:608f5cc(commit hash) - If
vcpkg.jsonchanged → rebuilds dependencies
Create git tag:
git tag v1.0.0
git push origin v1.0.0- Same as branch push
- Tags:
qlean-mini:608f5cc+qlean-mini:v1.0.0+qlean-mini:latest
Pull request:
git push origin feature-branch
# Create PR to ci/docker- Builds images but doesn't push
- Validates that the build works
The workflow uses GitHub-hosted free runners with native ARM64 support:
- AMD64:
ubuntu-24.04(native x64) - ARM64:
ubuntu-24.04-arm(native ARM64)
These are configured in .github/actions/docker-matrix/action.yml.
Benefits:
- ✅ Free - Both AMD64 and ARM64 runners are free for public repositories
- ✅ Native builds - No QEMU emulation, full speed on both architectures
- ✅ Parallel - Builds run simultaneously on separate runners
- ✅ Fast - Each architecture builds in ~20-30 minutes natively
Note: For private repositories, consider costs or use self-hosted runners.
Required secrets in GitHub repository settings:
DOCKER_USERNAME- Docker Hub usernameDOCKER_TOKEN- Docker Hub access token
Setting up secrets:
- Go to Settings → Secrets and variables → Actions
- Click New repository secret
- Add both secrets
The dependencies image (qlean-mini-dependencies:latest) contains:
- vcpkg packages
- Python venv
- Rust toolchain
- System dependencies
When to rebuild:
- ❌ Don't rebuild for normal code changes
- ✅ Rebuild when
vcpkg.jsonchanges - ✅ Rebuild when system dependencies change (
.ci/scripts/init.sh)
The workflow automatically detects vcpkg.json changes on push events.
For manual builds, check Build dependencies image in the UI.
Dependencies:
qdrvm/qlean-mini-dependencies:latest
qdrvm/qlean-mini-dependencies:v1 (custom tag)
Builder:
qdrvm/qlean-mini-builder:608f5cc (commit hash, always)
qdrvm/qlean-mini-builder:v1.0.0 (custom tag, optional)
Runtime:
qdrvm/qlean-mini:608f5cc (commit hash, always)
qdrvm/qlean-mini:v1.0.0 (custom tag, optional)
qdrvm/qlean-mini:latest (latest tag, optional)
1. Setup Matrix
outputs:
matrix: {"include":[{"platform":"linux/amd64","arch_suffix":"amd64","runs_on":"actions-runner-controller"},{"platform":"linux/arm64","arch_suffix":"arm64","runs_on":["self-hosted","qdrvm-arm64"]}]}
should_build_deps: false
should_push: true2. Build Dependencies (if needed)
# Job 1: ARM64 runner
DOCKER_PLATFORM=linux/arm64 make docker_build_dependencies
make docker_push_platform_dependencies # Push with -arm64 suffix
# Job 2: AMD64 runner (parallel)
DOCKER_PLATFORM=linux/amd64 make docker_build_dependencies
make docker_push_platform_dependencies # Push with -amd64 suffix3. Build Application
# Job 1: ARM64 runner
make docker_pull_dependencies # Pull deps from registry
DOCKER_PLATFORM=linux/arm64 make docker_build_builder
DOCKER_PLATFORM=linux/arm64 make docker_build_runtime
make docker_push_platform # Push with -arm64 suffix
# Job 2: AMD64 runner (parallel)
make docker_pull_dependencies
DOCKER_PLATFORM=linux/amd64 make docker_build_builder
DOCKER_PLATFORM=linux/amd64 make docker_build_runtime
make docker_push_platform # Push with -amd64 suffix4. Create Manifest
# On any runner (no build, no pull)
make docker_manifest_dependencies # If deps were built
make docker_manifest_create # Builder + runtimeResult: Single tag works on both architectures:
docker pull qdrvm/qlean-mini:608f5cc
# Docker automatically pulls correct architecture (ARM64 or AMD64)Error: "Dependencies image not found in registry"
Solution:
# Option 1: Run workflow with "Build dependencies image" = true
# Option 2: Build and push manually
make docker_build_dependencies DOCKER_PLATFORM=linux/arm64
make docker_push_platform_dependencies
make docker_build_dependencies DOCKER_PLATFORM=linux/amd64
make docker_push_platform_dependencies
make docker_manifest_dependenciesError: "Runner not found"
The workflow should work on all GitHub repositories with free ubuntu-24.04 runners. If you see this error, check that GitHub Actions are enabled for your repository.
Build timeout
Default timeout is 180 minutes (3 hours). If builds take longer, increase in workflow:
timeout-minutes: 240 # 4 hoursThe workflow uses standard Makefile commands, so you can test locally:
# Build locally
export DOCKER_PLATFORM=linux/arm64
make docker_build_all
# Push to test
export DOCKER_REGISTRY=qdrvm
export DOCKER_PUSH_TAG=true
export DOCKER_IMAGE_TAG=test-build
make docker_push- Use commit tags - Always pushed, provides full traceability
- Use custom tags for releases -
v1.0.0,staging, etc. - Use
latestsparingly - Only for stable releases - Rebuild dependencies rarely - Only when
vcpkg.jsonchanges - Test PRs before merging - Auto-builds verify changes work
Current configuration builds on ci/docker branch for testing.
After validation:
-
Update workflow triggers to
master:on: push: branches: - master # Change from ci/docker
-
Update PR target:
pull_request: branches: - master
-
Push workflows to master
-
Delete
ci/dockerbranch