Playwright in Docker: The Browser Path Gotcha That'll Waste Your Afternoon
You build your screenshot service. It works perfectly on your machine. You deploy it to Docker and get:
browserType.launch: Executable doesn't exist at
/home/rails/.cache/ms-playwright/chromium_headless_shell-1208/...
Classic.
The Problem
Playwright installs browser binaries to a user-specific cache directory. By default, that's ~/.cache/ms-playwright/ on Linux.
In Docker, you typically:
- Install Playwright as
rootduring the build - Run the app as a non-root user (like
rails,node, orappuser)
The browsers get installed to /root/.cache/ms-playwright/, but your app looks in /home/youruser/.cache/ms-playwright/. Empty directory. Crash.
The Fix
Set PLAYWRIGHT_BROWSERS_PATH to a shared location:
ENV PLAYWRIGHT_BROWSERS_PATH="/opt/playwright-browsers"
RUN npm install -g playwright@latest && \
npx playwright install --with-deps chromium && \
chmod -R o+rx /opt/playwright-browsers
The key pieces:
PLAYWRIGHT_BROWSERS_PATH— tells Playwright where to install AND where to lookchmod -R o+rx— makes the directory readable by all users- Set the ENV before the install so it takes effect
Why --with-deps Matters
On Debian/Ubuntu-based images, Chromium needs system libraries. --with-deps installs them automatically — about 20 libraries including libnss3, libatk, libgbm, and more. Without it, you'll get a different error about missing shared libraries.
Quick Sanity Check
SSH into your container and verify:
echo $PLAYWRIGHT_BROWSERS_PATH
ls -la /opt/playwright-browsers/
su - appuser -c "npx playwright install --dry-run"
Performance Tip
First render after a cold container start will be slow (~2-5s) because Chromium needs to spin up. Subsequent renders reuse the warm process. If you need consistent sub-second times, keep a warm browser instance pool.
I hit this exact bug building Rendly. Wasted 30 minutes before the obvious hit me. Now you don't have to.
Ready to get started?
Try Rendly free — 100 renders/month, no credit card required.
Sign up for free →