Stay in the loop
The best CI provider for fast Docker builds
Most CI providers can build a Docker image. Getting that build to finish quickly is where the differences start. If building an image for production takes 10 minutes, a developer can wait for it to finish and continue with their task. If it takes an hour and a half, they have to go do something else and remember to come back.
A lot of that time comes down to where the build runs, what happens to its cache between jobs, and whether you're building for Intel, Arm, or both. Depot Container Builds gives you persistent BuildKit caching and native Intel and Arm builders from your existing CI provider. You can speed up the Docker build without moving the rest of your pipeline or maintaining the builders yourself.
As Depot integrates with CI providers, we spend a lot of time working with their Docker build setups. Here's how each handles caching and multi-platform builds, where the extra work comes from, and how we handle the build in Depot.
Where Docker builds spend their time
You don't have to build the image on the machine running your CI job. A GitHub Actions job can run Docker on its runner, or it can send the same build to a remote BuildKit builder. That gives you a way to change the slow part without migrating the whole pipeline.
These are the parts we'd look at first:
| Part of the build | What to check |
|---|---|
| Builder | CPU, memory, disk throughput, and whether the builder survives between jobs. |
| Cache | Which layers and cache mounts survive, how much storage is available, and how long cache import and export take. |
| Architecture | Whether each target uses native hardware, emulation, or cross-compilation. |
| Data transfer | Time to send the build context, pull base images, and push or download the finished image. |
| Pipeline | Queue time, runner startup, tool setup, and work repeated across jobs. |
Caching is a good example. You can save layers to a registry and pull them into the next job, but now you're spending time moving the cache over the network. Keeping it on a persistent builder avoids that transfer. You still need enough storage to keep the layers your builds use.
For multi-platform builds, pay attention to where each architecture gets built. An Intel machine can build an Arm image through QEMU, but compiling under emulation can be much slower than compiling on an Arm machine. Native builders avoid that cost. Cross-compilation is another option when your toolchain supports it.
Comparison of CI providers for Docker builds
GitHub Actions
GitHub Actions supports Docker Buildx and BuildKit. For builds on ephemeral runners, Docker supports both the GitHub Actions cache backend and registry cache exports.
GitHub's repository cache allowance defaults to 10 GB. It isn't a hard ceiling: administrators can increase it, with additional storage billed separately. Once you hit the configured limit, entries can be evicted to make room. So a build that had a useful cache yesterday may have to rebuild those layers today. Increasing the allowance can help with that, but it won't remove the time spent uploading and downloading the cache.
For multi-platform images, you can use QEMU on one runner, distribute builds across native runners, or use a remote builder. Docker documents multi-platform GitHub Actions workflows that split builds across runners and combine the results into one image manifest. You don't have to self-host runners to do this.
CircleCI
CircleCI supports Docker builds with its machine executor or with a Docker executor and setup_remote_docker. Its Docker layer caching feature works with both configurations and consumes additional credits.
If you're using Buildx, give the builder a name so CircleCI can retain its volumes across jobs. There's also a timing detail worth knowing: cache uploads happen asynchronously. A job that runs next isn't guaranteed to see the cache from the job that just finished.
CircleCI also documents limitations when combining Docker layer caching with QEMU. If you're building for multiple architectures, check that combination before relying on it. The caching feature can save repeated work, but it costs extra and still needs some configuration.
Google Cloud Build
Google's build optimization guide describes pulling a previous image and passing it to docker build --cache-from to reuse layers. That requires an available image in a registry and an explicit cache setup in the build configuration.
The tradeoff is the same as any registry cache: you save work rebuilding layers, but first you have to download them. How much that helps depends on the image size, where the registry lives, and which layers changed. Registry storage and transfer can add to the bill too.
For multi-platform builds, you'll also need to configure how each architecture gets built. Check which worker architectures are available before choosing native remote builders, cross-compilation, or emulation.
Bitbucket Pipelines
Bitbucket Pipelines has changed since we first wrote this post. Runtime v3 supports Docker Buildx, BuildKit, and multi-platform builds. The older restrictions on those features no longer describe the current runtime.
You'll need to configure a BuildKit cache backend, such as a registry cache. That's separate from Pipelines' general-purpose caches, so their size limits don't tell you how much BuildKit cache you can use. Atlassian's Docker commands documentation covers the runtime differences.
Having Buildx available lets you configure multi-platform builds. It doesn't mean those builds automatically run on native hardware for every architecture.
GitLab CI/CD
GitLab supports several Docker build configurations, including Docker-in-Docker and standalone rootless BuildKit. Rootless BuildKit doesn't require a Docker daemon, and GitLab documents importing and exporting cache through its container registry.
If you host your own runners, you can keep the builder and its cache around between jobs. You're also taking on the work of managing disk space, cleaning up old layers, and isolating jobs. Giving a job access to the host's Docker socket or using privileged Docker-in-Docker has different security implications from rootless BuildKit. That choice needs to account for what code runs in the job and which credentials it can access.
Jenkins
Jenkins' Docker Pipeline integration supports building images from Dockerfiles. You operate the agents and can configure local or remote builders, including BuildKit.
That gives you control over the machines and cache, but it also makes them your responsibility. Someone has to handle upgrades, disk cleanup, credentials, job isolation, and capacity when several builds need to run at once. As with any self-hosted setup, the time spent operating it is part of the cost.
Buildkite
Buildkite offers both hosted and self-hosted agents, so you can choose who operates the machines that run your jobs.
On the Enterprise plan, its hosted Linux agents use remote Docker builders by default. Buildx sends the build to a separate machine, so you don't have to build the image on the agent running the job. The builder keeps image layers in its local cache for 30 minutes after each build. Persistent container cache volumes are a separate feature you need to enable.
If you self-host the agents, the builder and cache setup are yours to manage. Buildkite's hosted option is worth including in a comparison, rather than assuming you have to operate all of that infrastructure yourself.
Use Depot for the Docker build without moving your CI
The CI provider doesn't have to decide how fast your Docker build runs. With Depot Container Builds, you send the build to our BuildKit builders. We keep the layer cache on the builder between runs, and Intel and Arm builds run on their native hardware.
That cache isn't tied to your CI provider either. Run depot build from your laptop with the same Depot project and you can reuse the layers CI already built. Your next CI build can use the layers from your local build too.
After installing and authenticating the Depot CLI, replace the Docker build command in your pipeline:
depot build \
--project <your-project-id> \
--platform linux/amd64,linux/arm64 \
--tag registry.example.com/team/app:latest \
--push .Replace the project ID and image tag, and configure authentication for your destination registry. Your Dockerfile and build context stay in the repository. Depot builds the image remotely and pushes it to the registry; to load an image into the local Docker engine, set --platform to your local architecture and replace --push with --load.
Your CI job no longer needs to download and upload the layer cache on every run. There's still data to move, including the build context, base images, packages, and the finished image. The difference is that the next build starts with its cache already on the builder.
If you want to move the whole workflow, we also have Depot CI. It runs your GitHub Actions YAML with our own orchestration, and you can use Depot Container Builds from those workflows. Our managed GitHub Actions runners are a separate option: GitHub still schedules the jobs, and we run them. For the Docker build alone, you can keep your current CI provider.
Compare configurations with your own Dockerfile
The useful comparison is how these setups handle your Dockerfile. Keep the commit, build arguments, target platforms, and output destination the same, then try four cases:
- A cold build: no reusable build cache.
- An unchanged rebuild: shows cache lookup and transfer overhead.
- A source-only change: shows whether dependency layers remain reusable.
- A dependency change: shows what happens when an expensive layer needs rebuilding.
Run each case a few times and look at the whole job, including setup, cache transfers, and pushing the image. Compare the typical time and the slow runs, then add up the runner, builder, cache, and storage costs. Saving time in docker build doesn't help as much if you're spending it downloading the cache instead.
If Docker builds are what's slowing you down, you don't need to start with a CI migration. Try Depot with the pipeline you already have and run that comparison.
Build Docker images with persistent caching and native Intel and Arm builders. Use Depot from your existing CI workflow or run the workflow on Depot CI.



