Build multi-architecture container images in GitHub Actions. Matrix builds (public repos with native ARM64), QEMU emulation (private repos), or ARM64 larger runners (Team/Enterprise). Uses Podman rootless builds with push-by-digest pattern
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
npx agent-skills-cli listSkill Instructions
name: github-actions-container-build description: Build multi-architecture container images in GitHub Actions. Matrix builds (public repos with native ARM64), QEMU emulation (private repos), or ARM64 larger runners (Team/Enterprise). Uses Podman rootless builds with push-by-digest pattern
GitHub Actions Container Build
Build multi-architecture container images in GitHub Actions using Podman and native ARM64 runners.
Core Principles
Choose Your Workflow
CRITICAL: Ask these questions before generating any workflow.
Question 1: Is your GitHub repository public?
- Yes → Use
github-actions-workflow-matrix-build.yml(free standard ARM64 runners, 10-50x faster) - No → Go to Question 2
Question 2: Do you have GitHub Team/Enterprise + willing to pay for ARM64 builds?
- Yes → Use ARM64 larger runners (custom setup required, paid per minute)
- No → Use
github-actions-workflow-qemu.yml(free QEMU emulation, slower but works on free tier)
1. Push-by-Digest (2025 Best Practice - Default)
Matrix builds use push-by-digest pattern:
- Images pushed by digest without intermediate
:amd64/:arm64tags - Only tiny digest files (~70 bytes) transfer as artifacts
- Registry stays clean (no tag clutter)
- Same debug experience with
--platformflag
# Build job
- name: Push by digest
run: |
podman push \
--digestfile /tmp/digest \
localhost/build:${{ matrix.arch }} \
docker://${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Merge job
- name: Create manifest from digests
run: |
podman manifest create "$IMAGE:latest"
podman manifest add "$IMAGE:latest" "docker://$IMAGE@${AMD64_DIGEST}"
podman manifest add "$IMAGE:latest" "docker://$IMAGE@${ARM64_DIGEST}"
podman manifest push --all "$IMAGE:latest" "docker://$IMAGE:latest"
Debug specific architecture:
podman pull --platform linux/arm64 ghcr.io/OWNER/REPO:latest
2. Matrix Builds (Public Repos)
For public repositories - use GitHub-hosted standard ARM64 runners:
- 10-50x faster builds (native vs. emulation)
- Better reliability and accuracy
- Lower CI costs
- Completely free for public repos
- Not available for private repos
strategy:
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm # Standard ARM64 runner (public repos only)
3. QEMU Builds (Private Repos - Free Tier)
For private repositories on free tier - use QEMU emulation:
- Works on GitHub Free plan
- Slower (10-50x) than native ARM64 runners
- Uses
docker/setup-qemu-actionfor ARM64 emulation - Single-job pattern with
--platform linux/amd64,linux/arm64
runs-on: ubuntu-latest
steps:
- uses: docker/setup-qemu-action@v3
- run: podman build --platform linux/amd64,linux/arm64 --manifest ...
4. Podman Over Docker
Use Podman for container builds:
- Rootless by default (better security)
- No daemon required
- Native multi-arch manifest support
- OCI compliant
- Must use
podman manifest push --all(notpodman push) - Format: Use OCI (default) for modern registries; use
--format v2s2only for Quay.io or cross-registry (see references for details) - Network: Use
--network=hostflag for builds to avoid container networking SSL issues on GitHub Actions ubuntu-24.04 (see Troubleshooting section)
5. podman-static for Heredoc Support
Ubuntu 24.04's bundled podman (4.9.3) uses buildah 1.33.7 which doesn't support heredoc syntax. Install podman-static for full BuildKit compatibility:
- name: Install podman-static
run: |
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then
PODMAN_ARCH="amd64"
else
PODMAN_ARCH="arm64"
fi
curl -fsSL -o /tmp/podman-linux-${PODMAN_ARCH}.tar.gz \
https://github.com/mgoltzsche/podman-static/releases/latest/download/podman-linux-${PODMAN_ARCH}.tar.gz
cd /tmp && tar -xzf podman-linux-${PODMAN_ARCH}.tar.gz
sudo cp -f podman-linux-${PODMAN_ARCH}/usr/local/bin/* /usr/bin/
podman system migrate
Important: Install to /usr/bin/ (not /usr/local/bin/) to avoid AppArmor issues.
Quick Start
For Public Repos (Matrix Build)
-
Copy workflow template:
cp assets/github-actions-workflow-matrix-build.yml .github/workflows/build.yml -
Customize Containerfile path:
-f ./Containerfile.python-uv # or your Containerfile -
Add your Containerfile (see secure-container-build plugin for templates)
For Private Repos (QEMU)
-
Copy workflow template:
cp assets/github-actions-workflow-qemu.yml .github/workflows/build.yml -
Follow steps 2-3 from above.
Workflow Structure
Matrix Build Workflow (Push-by-Digest)
- Build job (matrix): Build and push images by digest on native runners
- Merge job: Download digests, create and push multi-arch manifest
QEMU Workflow
- Single job: Build multi-arch manifest directly with
--platformflag
Multi-arch Build Approaches
| Approach | Artifact Size | Registry Overhead | Best For |
|---|---|---|---|
| Push-by-digest (default) | ~70 bytes | 1x | Production |
| Architecture tags | None | 2x (tags + manifest) | Debugging |
| OCI artifacts | Full images | 3x | Maximum privacy |
See references/github-actions-best-practices.md for detailed comparison.
ARM64 Larger Runners (Private Repos with Team/Enterprise)
For private repositories with GitHub Team or Enterprise Cloud plans:
Standard ARM64 runners (ubuntu-24.04-arm) don't work in private repos. Instead, create ARM64 larger runners:
Setup steps:
- Go to Organization Settings → Actions → Runners → New runner
- Select "Larger runners"
- Choose "Ubuntu 24.04 by Arm Limited" partner image
- Name your runner (e.g.,
my-org-arm64-runner) - Configure size (e.g., 4-core, 16GB RAM)
Update workflow to use custom runner:
strategy:
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: my-org-arm64-runner # Your custom ARM64 larger runner name
Cost:
- Billed per minute (not included in free minutes)
- ~37% cheaper than x64 larger runners
- Ref: Actions runner pricing
Registry Configuration
GitHub Container Registry (GHCR) - Default
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
- name: Login to GHCR
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | podman login "${{ env.REGISTRY }}" \
-u "${{ github.actor }}" \
--password-stdin
Docker Hub (Optional)
- name: Login to Docker Hub
run: |
echo "${{ secrets.DOCKERHUB_TOKEN }}" | podman login docker.io \
-u "${{ secrets.DOCKERHUB_USERNAME }}" \
--password-stdin
# Push to Docker Hub
podman manifest push --all "$IMAGE:latest" \
"docker://docker.io/${{ secrets.DOCKERHUB_USERNAME }}/app:latest"
Debugging Multi-arch Images
# Pull specific architecture
podman pull --platform linux/arm64 ghcr.io/OWNER/REPO:latest
# Inspect manifest
podman manifest inspect ghcr.io/OWNER/REPO:latest
# Verify architectures
podman manifest inspect ghcr.io/OWNER/REPO:latest | jq '.manifests[].platform'
Reference Documentation
For detailed information, see references/github-actions-best-practices.md.
Containerfile Templates
For Containerfile templates and security best practices, see the secure-container-build plugin which provides:
- Production-ready templates for Python/uv, Bun, Node.js/pnpm, Golang, and Rust
- Wolfi runtime images with non-root users
- Multi-stage build patterns
- Allocator optimization for Rust
Troubleshooting
Common Issues
Container networking SSL errors (ubuntu-24.04 runners):
-
Symptom:
UNKNOWN_CERTIFICATE_VERIFICATION_ERRORor SSL certificate verification failures duringbun install,npm install,pip install, etc. inside containers -
Cause: GitHub Actions ubuntu-24.04 runner image 20251208.163.1+ has container networking configuration changes that break SSL/TLS connections from inside containers
-
Solution: Add
--network=hostflag topodman build:podman build \ --network=host \ --format docker \ --platform linux/${{ matrix.arch }} \ -f ./Containerfile \ . -
Verification: Test repository at https://github.com/pigfoot/test-bun-ssl-issue
-
GitHub Issue: https://github.com/actions/runner-images/issues/13422
-
Note: This is a known issue with ubuntu-24.04 runners. The
--network=hostworkaround reduces network isolation during build but is acceptable for CI/CD use cases.
Authentication failed:
- Ensure GITHUB_TOKEN has package write permission
- Check registry URL and credentials
Manifest add failed:
- Verify architecture-specific images exist in registry
- Check digest format is correct (
sha256:...)
ARM64 runner not available:
- Standard ARM64 runners only work for public repos
- For private repos, use QEMU or larger runners
podman-static installation fails:
- Verify correct architecture detection
- Check GitHub releases for podman-static availability
AppArmor issues:
- Install binaries to
/usr/bin/not/usr/local/bin/ - Run
podman system migrateafter installation
Wrong architecture pulled:
- Always use
--platformflag when pulling - Use
--format dockerwhen building for compatibility
More by pigfoot
View allSmart commit creation with conventional commits, emoji, and GPG signing. Use when user says "commit" or requests committing changes. Handles staged file detection, suggests splits for multi-concern changes, and applies proper commit format.
Build secure container images with Wolfi runtime, non-root users, and multi-stage builds. Templates for Python/uv, Bun, Node.js/pnpm, Golang (static/CGO), and Rust (glibc/musl) with allocator optimization
Use when users request image generation, AI art creation, image editing with Gemini models, need help crafting prompts, or want brand-styled imagery. Handles both direct generation and interactive prompt design.
Confluence doc management. Use for Confluence URLs (/wiki/x/... short URLs), reading/uploading/downloading/searching/creating/updating pages, Markdown→ADF conversion, and syncing docs to Confluence.
