oo-workers ships as a Docker image, and the image was big: around 3.5 GB. Version 1.28.6 cut it to about 1.4 GB without dropping a feature. Here is where the weight was.
Where the weight was#
The bulk was Playwright. To run browser checks, the image bundled Playwright's full browser set: Chromium, Firefox, and WebKit. But the runner only ever launches headless Chromium. So we install just the Chromium headless shell and skip the other two browsers entirely. That, plus shipping production-only dependencies (dropping the test and build toolchain) and running as a non-root user, took the master and QA-agent images from ~3.49 GB to ~1.43 GB, and the light agent from 363 MB to 226 MB.
The Bun and Node detail#
One wrinkle. The engine runs on Bun, but Playwright's test runner spawns its worker processes with node, and Bun's node-compatibility shim does not carry Playwright's worker IPC. So the browser images install real Node.js alongside Bun, and the code calls node node_modules/.bin/playwright directly rather than npx, which is not in the Bun base image. Bun runs the app, Node runs Playwright's workers, and they coexist in the same image.
The floor#
There is a floor. Even stripped to the headless shell, Chromium pulls in a chain of system libraries (graphics, fonts, and the rest), so about 1.43 GB is as small as the browser image gets without giving up browser checks. The light agent, which carries no browser at all, sits at 226 MB.
The trade-off#
Only the Chromium headless shell ships. A browser-check script that targets Firefox or WebKit, or asks for a headed browser, will fail, and the dashboard offers no browser picker. For unattended monitoring that runs headless from a server, that is the right cut: pay for the one browser you use, not the two you do not.
The change was validated against the deployed images, not just the build: the full UI end-to-end suite and the multi-region harness both ran on the slim agent images, with a real browser check reaching a passing state.