Skip to content

devops(docker): split browser layers and use zstd compression for faster pulls#40702

Open
KRRT7 wants to merge 6 commits intomicrosoft:mainfrom
KRRT7:docker-optimize-pull-time
Open

devops(docker): split browser layers and use zstd compression for faster pulls#40702
KRRT7 wants to merge 6 commits intomicrosoft:mainfrom
KRRT7:docker-optimize-pull-time

Conversation

@KRRT7
Copy link
Copy Markdown

@KRRT7 KRRT7 commented May 7, 2026

Summary

  • Split the single browser-install RUN into per-browser layers (chromium, firefox, webkit, ffmpeg) enabling Docker's parallel layer downloading
  • Switch publish pipeline from docker build + docker push to docker buildx build --push with zstd compression (level 19) for ~25% smaller compressed layers
  • Refactor tagging to build once per arch and create additional tags via docker buildx imagetools create (registry-side copy, no layer re-upload)
  • Add docker/setup-buildx-action to CI (required for buildx + zstd output)

Note on zstd compatibility

zstd-compressed layers require Docker 23.0+ (Feb 2023) on the client side. Older Docker versions will fail to pull. If this is a concern for your user base, the layer-splitting alone still provides a significant speedup via parallel downloads, and the zstd change could be dropped or deferred.

Fixes #40701

KRRT7 added 4 commits May 7, 2026 07:41
The zstd compression output requires a buildx builder with the
docker-container driver. Also removes the now-unused tag_and_push
function.
Build pushes one canonical tag per arch, then tag_and_push creates
additional tags via `docker buildx imagetools create` (registry-side
copy, no layer re-upload).
Use `install-deps` for all system dependencies in one shared layer,
then `install` (without --with-deps) per browser for binary-only
layers. This eliminates duplicate apt index fetches and makes browser
layers pure binary data with better zstd compression ratio.
@pavelfeldman
Copy link
Copy Markdown
Member

Thanks for looking into it. I'm sure we have customers with older docker clients. But I'm ok with the layering change. Could you provide the numbers? Time w/ and w/o layers next to each other.

KRRT7 added 2 commits May 7, 2026 12:44
… compatibility

zstd-compressed layers require Docker 23.0+ (Feb 2023). Reverting to
standard gzip keeps the parallel-layer speedup without breaking pulls
for users on older Docker versions.
One-off workflow_dispatch to measure cold pull times for monolithic
vs split-layer Docker images on a GitHub Actions runner.
@KRRT7
Copy link
Copy Markdown
Author

KRRT7 commented May 7, 2026

Dropped the zstd change (latest commit), so no Docker version concerns anymore — this is purely the layer split.

Layer structure (arm64, compressed):

Before (1 browser layer) After (4 browser layers)
ubuntu base 28 MB 28 MB
Node.js + system deps 97 MB 97 MB
playwright-core COPY 3 MB 3 MB
apt browser deps 229 MB
chromium 321 MB
firefox 104 MB
webkit 105 MB
ffmpeg 2 MB
all browsers + deps 754 MB

Largest single layer drops from 754 MB → 321 MB.

Benchmarks (Azure VM, D16s_v5, amd64, GHCR):

Scenario Monolithic Split Delta
Per-connection throttled (3 MB/s per conn) 437s 270s 38% faster
Total-bandwidth throttled (25 Mbps shared) 304s 304s Same
Unthrottled (full speed) 17s 17s Same

The split helps when the bottleneck is per-connection throughput (registry/CDN rate limits per stream — Docker downloads each layer on a separate connection). When the user's own pipe is the bottleneck, it's neutral — never worse.

For CI runners with high total bandwidth but CDN-limited per-stream rates, this cuts the download portion significantly. On fast unthrottled connections, decompression dominates and both are equivalent.

@@ -0,0 +1,106 @@
name: "benchmark: docker pull time"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

uses: docker/setup-qemu-action@v4
with:
platforms: arm64
- name: Set up Docker Buildx
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

@@ -1,3 +1,4 @@
# syntax=docker/dockerfile:1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

npm i /tmp/playwright-core.tar.gz && \
npm exec --no -- playwright-core mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npm exec --no -- playwright-core install --with-deps && rm -rf /var/lib/apt/lists/* && \
npm exec --no -- playwright-core install-deps chromium chromium-headless-shell firefox webkit && \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not list browsers, install all deps.

rm -rf /var/lib/apt/lists/*

# 3. Install each browser binary in its own layer for parallel pulling.
RUN cd /ms-playwright-agent && \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you could benefit from splitting these too? chromium is your largest layer

@@ -1,3 +1,4 @@
# syntax=docker/dockerfile:1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dtop

chmod -R 777 /ms-playwright/webkit-*

# 4. Install ffmpeg and clean up.
RUN cd /ms-playwright-agent && \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First installed browser will do it, this layer will be empty.

./build.sh "--${ARCH}" "${FLAVOR}" playwright:localbuild

for ((i = 0; i < ${#TAGS[@]}; i++)) do
local CANONICAL_TAG="playwright.azurecr.io/public/${MCR_IMAGE_NAME}:${TAGS[0]}-${ARCH}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Reduce Docker image size to improve container pull time

2 participants