Claude-skill-registry github-actions-container-build
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
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/github-actions-container-build" ~/.claude/skills/majiayu000-claude-skill-registry-github-actions-container-build && rm -rf "$T"
skills/data/github-actions-container-build/SKILL.mdGitHub 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
(free standard ARM64 runners, 10-50x faster)github-actions-workflow-matrix-build.yml - 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
(free QEMU emulation, slower but works on free tier)github-actions-workflow-qemu.yml
1. Push-by-Digest (2025 Best Practice - Default)
Matrix builds use push-by-digest pattern:
- Images pushed by digest without intermediate
tags:amd64/:arm64 - Only tiny digest files (~70 bytes) transfer as artifacts
- Registry stays clean (no tag clutter)
- Same debug experience with
flag--platform
# 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
for ARM64 emulationdocker/setup-qemu-action - 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
(notpodman manifest push --all
)podman push - Format: Use OCI (default) for modern registries; use
only for Quay.io or cross-registry (see references for details)--format v2s2 - Network: Use
flag for builds to avoid container networking SSL issues on GitHub Actions ubuntu-24.04 (see Troubleshooting section)--network=host
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
flag--platform
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:
or SSL certificate verification failures duringUNKNOWN_CERTIFICATE_VERIFICATION_ERROR
,bun install
,npm install
, etc. inside containerspip install - 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
flag to--network=host
:podman buildpodman 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
workaround reduces network isolation during build but is acceptable for CI/CD use cases.--network=host
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
not/usr/bin//usr/local/bin/ - Run
after installationpodman system migrate
Wrong architecture pulled:
- Always use
flag when pulling--platform - Use
when building for compatibility--format docker