BLOG
Playwright Remove Browsers: Clean Up and Go Remote
Learn how to remove Playwright browsers from local and CI machines, reclaim disk space, and switch to hosted Chromium over CDP for production runs.
# Playwright Remove Browsers: Clean Up and Go Remote
If you need to playwright remove browsers from a machine, the command is npx playwright uninstall. That deletes the browser binaries Playwright downloaded into its cache directory. It does not touch your node_modules, your test files, or your playwright.config.ts. The reason most teams run it is disk pressure: a full Playwright install pulls Chromium, Firefox, and WebKit plus their headless shells, and that adds up fast across CI runners and developer laptops.
This guide covers what playwright uninstall actually removes, where the files live on each OS, how to verify the cleanup, and when removing local browsers is the wrong move. If your goal is to stop managing browser binaries entirely, the better answer is usually to keep Playwright as your automation library and point it at a hosted Chromium session over CDP. That is the model Remote Browser is built around, and it is worth understanding before you delete anything.
What playwright uninstall actually does
Playwright stores browsers outside your project. The default locations are:
- Linux:
~/.cache/ms-playwright - macOS:
~/Library/Caches/ms-playwright - Windows:
%USERPROFILE%\AppData\Local\ms-playwright
npx playwright uninstall removes the contents of that cache directory. It is a blunt instrument: it removes every browser revision Playwright has downloaded, not just the ones your current project uses. If you have multiple projects pinned to different Playwright versions, they all lose their binaries and will re-download on the next playwright install.
A few things it does not do:
- It does not remove the
playwrightor@playwright/testnpm packages. - It does not remove system dependencies installed by
playwright install-deps. - It does not remove browser profiles, downloaded files, or anything your tests wrote to disk.
- It does not remove the
chromium_headless_shellbinary if you installed it separately in some setups — check the cache directory manually.
To remove a single browser instead of all of them, pass a name:
npx playwright uninstall chromium
npx playwright uninstall firefox
npx playwright uninstall webkitTo see what is actually on disk before you delete it:
du -sh ~/.cache/ms-playwright/* # Linux
du -sh ~/Library/Caches/ms-playwright/* # macOSOn a typical Linux CI image, a full three-browser install lands somewhere in the 1–2 GB range depending on version and whether headless shells are included. Chromium alone is usually the largest single item.
Removing browsers in CI versus on a dev machine
The trade-off is different in each environment, and conflating them is where teams get into trouble.
| Environment | Removing browsers | Keeping browsers |
|---|---|---|
| Local dev laptop | Frees 1–2 GB; next run re-downloads (~1–3 min on good network) | Instant test runs, works offline |
| Ephemeral CI runner | Sensible if the image is rebuilt per job and you cache the download | Standard approach; cache ~/.cache/ms-playwright between runs |
| Long-lived CI runner | Risky — you may delete binaries other jobs depend on | Fine, but watch disk growth across Playwright upgrades |
| Container image | Only if you install browsers at runtime, which is slow and flaky | Bake them into the image layer |
| Production automation | Usually the wrong question — see the remote runtime section | N/A if you connect to a hosted browser |
The CI case is where most playwright uninstall searches originate. A runner fills its disk after a few weeks of Playwright version bumps, each of which downloads a new browser revision without removing the old one. Playwright keeps multiple revisions side by side so that in-flight jobs do not break mid-upgrade. That is correct behavior, but it means the cache grows monotonically unless something prunes it.
Two practical options:
- Cache with a version key. Key your CI cache on the Playwright version so a new version produces a fresh cache and the old one is evicted by the cache backend's own retention rules.
- Uninstall then reinstall. Run
npx playwright uninstallfollowed bynpx playwright install --with-depsat the start of a job. This guarantees a clean state at the cost of a full download every run.
Option 1 is almost always faster. Option 2 is what you reach for when you suspect a corrupted or partially-downloaded browser is causing flaky failures.
Verifying the removal
After running uninstall, confirm the cache is empty:
ls -la ~/.cache/ms-playwrightYou should see either an empty directory or no directory at all. If files remain, they are likely from a different Playwright version's cache path, or from a system-level install. Check PLAYWRIGHT_BROWSERS_PATH if it is set in your environment — when that variable points somewhere custom, playwright uninstall targets that location instead of the default cache.
One common gotcha: if you installed browsers with PLAYWRIGHT_BROWSERS_PATH=0, they live inside node_modules/playwright-core/.local-browsers instead of the user cache. playwright uninstall will not remove those. You have to delete them manually or remove node_modules entirely.
When removing browsers is the wrong fix
If you are removing Playwright browsers because your CI is slow, disk-constrained, or flaky, deletion treats a symptom. The underlying costs of local browser management do not go away:
- Download time. Every fresh runner pays the install cost unless you cache effectively.
- Version drift. Playwright pins browser revisions; upgrading the library forces a re-download, and your tests may behave differently on the new revision.
- System dependencies.
--with-depspulls a long list of shared libraries. On minimal container images this is a frequent source of failures. - Resource contention. Running many headless Chromium instances on one host competes for CPU and memory, and the failure mode is usually a timeout rather than a clean error.
- No shared session state. Local browsers are per-process. Coordinating profiles, cookies, and proxies across workers means building that infrastructure yourself.
None of these are reasons to avoid Playwright. They are reasons to stop running the browser on the same machine as the code that drives it.
The alternative: keep Playwright, remove the local browser dependency
Playwright can connect to a browser that is already running elsewhere. chromium.connectOverCDP() takes a WebSocket endpoint and returns a Browser object you drive exactly like a locally launched one. The browser binary lives on the remote host; your code just speaks CDP to it.
This is the model Remote Browser provides: a hosted Chromium session with a CDP endpoint, a live viewer for debugging, persistent profiles, configurable proxy and browser settings, and session isolation between runs. You keep writing Playwright. You stop installing browsers.
import { chromium, Browser, BrowserContext, Page } from 'playwright';
async function runTask(cdpUrl: string): Promise<void> {
// Connect to a hosted Chromium session over CDP.
// No local browser binary is required on this machine.
const browser: Browser = await chromium.connectOverCDP(cdpUrl);
try {
// A remote session may already have a default context.
const context: BrowserContext =
browser.contexts()[0] ?? (await browser.newContext());
const page: Page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.getByRole('link', { name: 'More information' }).click();
await page.waitForLoadState('networkidle');
const title = await page.title();
console.log('Remote page title:', title);
await page.close();
} finally {
// Closing the connection does not necessarily terminate the remote session.
// Session lifetime is controlled by the runtime, not the client.
await browser.close();
}
}
runTask(process.env.REMOTE_BROWSER_CDP_URL!);Two details matter in production:
- `browser.close()` semantics differ. With
connectOverCDP, closing the client connection may leave the remote session running. If you want the session torn down, do it through the runtime's API rather than assuming the disconnect ends it. - Context reuse. A remote session often arrives with a default context already present. Calling
newContext()unconditionally can produce a second context and confusing state. Checkbrowser.contexts()first.
If you are using Playwright's own server mode instead of a CDP endpoint, chromium.connect() with a wsEndpoint is the equivalent call. Both paths let you delete local browsers without changing your test code beyond the connection line.
What you give up, and what you gain
Removing local browsers and connecting to a hosted runtime is a real architectural change. Be honest about both sides.
You give up:
- Offline test runs. If the network is down, you cannot reach the remote browser.
- Full control over the exact browser build. You depend on the runtime's Chromium version.
- Zero-latency local debugging. Every action is a network round trip, though CDP is efficient enough that this rarely dominates.
You gain:
- No browser install step in CI, no
--with-depsfailures, no cache management. - Consistent browser version across every environment, so a test that passes locally passes in CI for the same reason.
- Session isolation handled by the runtime rather than by your process management.
- Persistent profiles and configurable proxy settings without building that layer yourself.
- A live viewer for inspecting what the agent or test actually saw, which is hard to replicate with a headless local browser.
For teams running browser-use style agents rather than test suites, the calculus tilts further toward remote. Agents run long, open many pages, and need profiles that survive across tasks. Managing that on local Chromium means writing a session manager. See Remote Browser for AI agents for how that layer is structured.
A migration path that does not break your suite
You do not have to choose between local and remote. A staged migration works well:
- Keep local browsers for unit-level tests. Fast, offline, no network dependency.
- Add a remote connection for integration and end-to-end runs. Point a second Playwright project at the CDP endpoint.
- Move CI to remote first. CI is where install cost and flakiness hurt most, and it is the easiest place to accept a network dependency.
- Remove local browsers from CI images once remote runs are stable. This is the step where
playwright uninstallactually belongs — as a cleanup after migration, not as a fix before it. - Keep a local install on developer machines unless disk pressure forces otherwise. The feedback loop is worth the space.
If you are still deciding whether a hosted runtime fits, Remote Browser online covers the connection model end to end, and remote web browser walks through the runtime characteristics that matter for automation workloads. For driving sessions interactively during development, remote control browser explains how the live viewer fits into the workflow.
Practical checklist
Before you run playwright uninstall, confirm:
- You know which cache path is in use (
PLAYWRIGHT_BROWSERS_PATHmay override the default). - No other project on the machine depends on the same cache.
- You have a plan for the next install — cached CI layer, container image, or a remote endpoint.
- You have checked whether browsers were installed into
node_modulesrather than the user cache.
After you remove them, if the next step is a hosted runtime:
- Get a CDP endpoint from your runtime provider.
- Switch
chromium.launch()tochromium.connectOverCDP(). - Verify context handling — check
browser.contexts()before creating a new one. - Confirm session teardown behavior with your provider rather than assuming
browser.close()ends the session. - Move CI first, then developer machines.
Current session limits, browser-hour metering, and plan details live on the pricing page. Connection options, CDP endpoints, and profile configuration are documented in the documentation.
Summary
npx playwright uninstall removes Playwright's downloaded browser binaries from the cache directory. It is the right command when you need disk space back or want a clean reinstall. It is the wrong command if you are trying to solve CI slowness, flakiness, or resource contention — those problems come from running browsers next to your code, not from the binaries existing.
The durable fix is to keep Playwright as your automation library and connect it to a hosted Chromium session over CDP. You delete the install step, the version drift, and the dependency management, and you keep the API you already know. For the official details on the connection method, see the Playwright `connectOverCDP` documentation.