TL;DR: This tutorial guides you step-by-step to build a GitLab pipeline that builds Docker images for multiple architectures and publishes them to AWS ECR. You'll get a working example, a reusable template, and a troubleshooting checklist. Skip to the template at the end if you're in a hurry.
Most DevOps guides are too abstract or skip real-world issues. Here, you'll build a working pipeline, see every command, and learn how to avoid common pitfalls.
Building and publishing Docker images for multiple architectures (x86, ARM) is essential for modern cloud and edge deployments—but it's easy to get stuck on CI/CD, authentication, or manifest issues.
Today you'll build a real pipeline that works, with all the gotchas explained.
In 30 minutes, you'll create:
- ✅ A working GitLab pipeline that builds and pushes multi-arch Docker images to AWS ECR
- ✅ A template you can reuse for any Docker project
- ✅ A troubleshooting checklist for common CI/CD and ECR errors
- A working GitLab pipeline that builds and pushes multi-arch Docker images to AWS ECR
- A template you can reuse for any Docker project
- A troubleshooting checklist for common CI/CD and ECR errors
⏱️ Time: 30 minutes
📊 Difficulty: Intermediate
🔧 Prerequisites: GitLab and AWS accounts, Docker, AWS CLI, basic CI/CD knowledge
Before you dive in, here's what you'll build and how all the pieces fit together.
flowchart LR
A["📝 GitLab Repo"] --> B["⚙️ GitLab CI/CD Pipeline"]
B --> C["🐳 Docker Buildx (Multi-Arch)"]
C --> D["☁️ AWS ECR (Container Registry)"]
D --> E["🚀 Deploy Anywhere (Cloud, Edge, ARM, x86)"]
style A fill:#e3f2fd,stroke:#90caf9,color:#1a237e
style B fill:#fce4ec,stroke:#f8bbd0,color:#4a148c
style C fill:#e8f5e9,stroke:#a5d6a7,color:#1b5e20
style D fill:#fffde7,stroke:#ffe082,color:#f57c00
style E fill:#e0f7fa,stroke:#80deea,color:#006064
This diagram shows the flow: code → CI/CD → multi-arch build → ECR → deploy anywhere.
- Automates multi-architecture Docker builds (x86, ARM)
- Pushes images to AWS ECR securely
- Works with any GitLab project (template provided)
- Handles authentication, manifest lists, and common CI/CD pitfalls
By the end, you'll have a robust, production-ready pipeline you can adapt for any project.
Before you start, let's make sure you have everything you need for a smooth experience.
- GitLab account (with permissions to create projects and CI/CD pipelines)
- AWS account (with permissions to create ECR repositories and IAM users)
- Docker (with Buildx support)
- AWS CLI (v2 recommended)
- GitLab Runner (if using self-hosted runners)
Open your terminal and run:
docker --version
aws --version
git --versionIf you see version numbers, you're good to go. If not, install the missing tools:
- Create an IAM user with programmatic access.
- Attach the following policy (minimum required for ECR push):
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
], "Resource": "*" },
{ "Effect": "Allow", "Action": ["ecr:CreateRepository"], "Resource": "*" }
]
}- Save your AWS Access Key ID and Secret Access Key for use in GitLab CI/CD variables.
aws ecr create-repository --repository-name my-multiarch-app- Adding a user to the
dockergroup or running privileged CI jobs grants root-level access. Use with caution and restrict permissions where possible.
- You can run
docker --version,aws --version, andgit --versionwithout errors - You have AWS credentials and an ECR repository ready
You're now ready to start building your pipeline!
A clear project structure makes your pipeline easier to build, test, and maintain.
my-multiarch-app/
├── .gitlab-ci.yml # GitLab pipeline definition
├── Dockerfile # Docker build instructions
├── README.md # Project overview
└── scripts/
└── entrypoint.sh # (Optional) Custom scripts for your app
- .gitlab-ci.yml: Defines your CI/CD pipeline, jobs, and stages
- Dockerfile: Instructions for building your multi-arch Docker image
- scripts/: (Optional) Store helper scripts for build or runtime
- Your repo contains a
.gitlab-ci.ymland aDockerfileat the root - Any custom scripts are in a
scripts/directory
With this structure, you're ready to start building your pipeline logic!
Let's break down how GitLab CI/CD works and set up a minimal pipeline to get you started.
This file defines your pipeline: stages, jobs, and the order they run. It lives at the root of your repo.
stages:
- build
build-image:
stage: build
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker info
script:
- docker build -t my-multiarch-app .- Shared runners: Provided by GitLab.com, easy to use, but may have restrictions
- Custom runners: You control the environment, required for advanced features (like privileged mode or custom Docker)
- Pin Docker image versions (e.g.,
docker:24.0.5) for stability - Use the
overlay2storage driver for better performance - Avoid plaintext secrets—use GitLab CI/CD variables
- You have a
.gitlab-ci.ymlwith a working build job - The pipeline runs and builds your Docker image without errors
You're now ready to add multi-architecture support!
Modern apps need to run on both x86 and ARM. Here's how to automate multi-arch builds in your pipeline.
- Cloud providers and edge devices use different CPU architectures
- One image for all platforms = less maintenance, more reach
Add a setup step in your pipeline to enable Buildx:
before_script:
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- docker buildx create --use
- docker buildx inspect --bootstrapExample job in .gitlab-ci.yml:
build-multiarch:
stage: build
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- docker buildx create --use
- docker buildx inspect --bootstrap
script:
- docker buildx build --platform linux/amd64,linux/arm64 -t $IMAGE_TAG --push .After pushing, check your image manifest:
docker buildx imagetools inspect $IMAGE_TAGYou should see both linux/amd64 and linux/arm64 listed.
- QEMU errors: Make sure the QEMU setup step runs before Buildx
- Missing platforms: Check your
--platformflag and Buildx version - Build fails on one arch: Test your Dockerfile on both platforms locally if possible
- Your pipeline builds and pushes a multi-arch image to your registry
docker buildx imagetools inspectshows both architectures
You're now ready to push your images to AWS ECR!
Now that you can build multi-arch images, let's push them to AWS ECR for secure, scalable storage and deployment.
If you haven't already:
aws ecr create-repository --repository-name my-multiarch-appAdd this to your pipeline before pushing:
aws ecr get-login-password --region <your-region> | \
docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.<your-region>.amazonaws.comStore your AWS credentials as GitLab CI/CD variables for security.
docker tag my-multiarch-app:latest <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/my-multiarch-app:latest
docker push <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/my-multiarch-app:latestFor multi-arch, Buildx can push directly:
docker buildx build --platform linux/amd64,linux/arm64 -t <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/my-multiarch-app:latest --push .- Go to the AWS Console → ECR → your repository
- You should see your image and both architectures in the manifest
- Auth errors: Double-check AWS credentials and region
- Push fails: Ensure your IAM user has ECR permissions
- Missing manifest: Confirm you used
--pushwith Buildx and the correct tag
- Your image appears in ECR and is multi-arch
- You can pull and run it from any supported platform
You're now ready to automate the entire process in your pipeline!
Let's put it all together—a full GitLab CI/CD pipeline that builds and pushes multi-arch Docker images to AWS ECR.
stages:
- build
- push
variables:
IMAGE_NAME: my-multiarch-app
IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
ECR_REGISTRY: <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com
ECR_REPO: $ECR_REGISTRY/$IMAGE_NAME
before_script:
- apk add --no-cache curl jq
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- docker buildx create --use
- docker buildx inspect --bootstrap
build-multiarch:
stage: build
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
script:
- docker buildx build --platform linux/amd64,linux/arm64 -t $ECR_REPO:$CI_COMMIT_SHORT_SHA --push .
push-to-ecr:
stage: push
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
script:
- aws ecr get-login-password --region <your-region> | docker login --username AWS --password-stdin $ECR_REGISTRY
- docker pull $ECR_REPO:$CI_COMMIT_SHORT_SHA
- docker tag $ECR_REPO:$CI_COMMIT_SHORT_SHA $ECR_REPO:latest
- docker push $ECR_REPO:latest
only:
- main- Stages: Separate build and push for clarity and control
- Variables: Centralize image names, tags, and registry info
- before_script: Prepares QEMU and Buildx for multi-arch builds
- build-multiarch: Builds and pushes the multi-arch image to ECR with a commit-specific tag
- push-to-ecr: Authenticates to ECR, tags the image as
latest, and pushes it (only onmainbranch)
- Store AWS credentials as GitLab CI/CD variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_DEFAULT_REGION) - Never hardcode secrets in your pipeline or repo
- Use layer caching and registry mirrors for faster builds (optional)
- Pin Docker image versions for reproducibility
- Use least-privilege IAM policies for ECR
- Your pipeline runs end-to-end, builds, and pushes multi-arch images to ECR
- The
latesttag is updated only on the main branch
You now have a production-ready, multi-arch Docker pipeline!
You've built and pushed your images—now let's make sure they work as expected on all platforms.
- Go to the AWS Console → ECR → your repository
- Confirm your image is present and lists both
amd64andarm64architectures in the manifest
docker pull <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/my-multiarch-app:latest
docker run --rm <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/my-multiarch-app:latestdocker pull <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/my-multiarch-app:latest
docker run --rm <aws_account_id>.dkr.ecr.<your-region>.amazonaws.com/my-multiarch-app:latestYou should see your app start up successfully on both platforms.
- Image not found: Double-check your ECR repo and image tag
- Wrong architecture: Make sure you used
--platformand pushed a manifest list - Run errors: Test your Dockerfile locally for both platforms using emulation if needed
- You can pull and run your image on both x86 and ARM
- The app works as expected on both platforms
You're now ready to apply best practices and secure your pipeline!
A great pipeline is not just functional—it's secure, efficient, and cost-effective. Here's how to level up your workflow.
- Never hardcode secrets in your repo or pipeline
- Use GitLab CI/CD variables for AWS credentials and other secrets
- Limit variable scope to only the jobs that need them
- Enable Docker layer caching for faster builds (if your runner supports it)
- Use registry mirrors to avoid Docker Hub rate limits
- Pin Docker image versions for reproducibility
- Monitor GitLab CI/CD build minutes (especially on shared runners)
- Clean up unused ECR images to save storage costs
- Use minimal base images to reduce build and storage size
- Use least-privilege IAM policies for AWS ECR
- Restrict who can trigger pipelines and push to protected branches
- Be cautious with privileged mode and Docker group membership
- Audit pipeline logs and access regularly
- No secrets are exposed in your repo or logs
- Pipeline runs are fast and cost-effective
- IAM and pipeline permissions follow least-privilege principles
You're now ready to wrap up and apply this template to your own projects!
Congratulations! You've built a robust, production-ready GitLab pipeline for multi-architecture Docker images and AWS ECR.
- Automated multi-arch Docker builds with Buildx and QEMU
- Securely pushed images to AWS ECR
- Validated your images on multiple platforms
- Applied best practices for security, cost, and performance
- Use this template for your next project
- Share your pipeline with your team
- Explore advanced topics: automated tests, deployment to Kubernetes, GitOps
Copy and adapt the provided .gitlab-ci.yml and project structure for any Docker project needing multi-arch support and cloud delivery.
💡 Remember: The best pipelines are simple, secure, and empower you to deliver anywhere. Keep iterating and share your learnings!