Most Puppeteer “font-cache” problems on Ubuntu are not caused by Puppeteer’s browser cache. Missing glyphs and unexpected fallback fonts usually mean that Fontconfig cannot see the required font files. Rebuild Fontconfig with fc-cache -f -v after confirming the fonts are installed. A missing Chrome executable, a sandbox error, a Docker library failure, or an unwritable cache directory requires a different fix.
This guide separates those failure stages, gives commands for desktop Ubuntu, CI, Docker, and read-only environments, and shows how to verify the result in the same Puppeteer job that produces your screenshots or PDFs.
First identify which “cache” is failing
Two unrelated caches are often confused:
| Component | What it stores | Typical symptom | Relevant action |
|---|---|---|---|
| Puppeteer browser cache | Downloaded browser executables. Puppeteer’s configuration documentation says the default location is ~/.cache/puppeteer from version 19.0.0. |
Could not find Chrome, an install that did not download a browser, or a packaged app that cannot locate its executable. |
Install or configure the browser separately; do not delete it as a routine font repair. |
| Fontconfig cache | Font metadata generated by scanning configured font directories. | Missing glyphs, a substituted typeface, boxes instead of characters, or a screenshot/PDF that differs from the desktop. | Install readable font files, then run fc-cache and test the actual render. |
Launch failures are a third category. No usable sandbox!, missing shared libraries, and unwritable user-data or XDG directories happen before page rendering and are not evidence of stale font metadata.
Use the symptom to choose the path
Missing glyphs or fallback text
Check the target script first. Latin, Cyrillic, Arabic, Chinese, Japanese, Korean, emoji, and specialist symbols can require different font families and packages. A cache rebuild cannot create a font file that is not installed.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
“Could not find Chrome” or a missing executable
This points to browser installation, a changed cache location, or a packaging step that omitted ~/.cache/puppeteer. It does not identify a font problem.
No usable sandbox! or an early browser crash
On Ubuntu 23.10 and newer, Puppeteer documents an AppArmor interaction that can stop downloaded Chrome for Testing from using user namespaces. Docker images can also lack required shared libraries. Resolve those launch conditions before investigating rendered text. Do not add --no-sandbox as a font workaround; Puppeteer’s troubleshooting guidance strongly discourages running without the browser sandbox.
Step 1: confirm the font files exist and are readable
Inspect the directories Fontconfig knows about:
fc-list | head -n 20
To search for a family by name:
fc-match "Noto Sans CJK JP"
fc-match "DejaVu Sans"
fc-match "Noto Color Emoji"
fc-match reports the font Fontconfig would select for a request. If it returns an unexpected family, or no usable result, install a package that covers the script you need. Package names and availability vary by Ubuntu release, so select the package for that release and language rather than assuming one universal package. Puppeteer’s Linux and Docker guidance specifically notes that extra Chinese, Japanese, and Korean font files may be necessary.
For a per-user font, place the file in a user font directory, such as ~/.local/share/fonts, and ensure the account running Puppeteer can read it. For system-wide use, install it in an appropriate system font directory with administrator permissions. Avoid copying fonts into a directory that is not included in the runtime container or that the service account cannot access.
Step 2: rebuild Fontconfig’s metadata
Run the force-and-verbose form first:
fc-cache -f -v
Ubuntu’s Jammy manual describes fc-cache as scanning font directories and building font-information cache files for applications using Fontconfig. The -f option forces regeneration and -v prints status so you can see which directories were scanned.
If the normal rebuild does not remove stale entries, use the more disruptive erase-and-rescan operation:
fc-cache -r -v
Here -r erases existing cache files before rescanning. Use it when the diagnostic points to corrupted or obsolete metadata, not as a first response to every rendering difference. Check the command status:
fc-cache -f -v
status=$?
printf 'fc-cache exit status: %sn' "$status"
test "$status" -eq 0
After rebuilding, run fc-match again and then render through Puppeteer. Fontconfig results in an interactive shell are not sufficient if your service runs as another user, inside a container, or with a different HOME.
Rank #3
Step 3: verify the fix in Puppeteer
Use a small, repeatable Node.js script that requests the exact family and characters your production page uses:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setContent(`
<style>
body { font-family: "Noto Sans", sans-serif; font-size: 32px; }
</style>
<div>Latin — العربية — 中文 — 日本語 — 한국어 — 😀</div>
`, { waitUntil: 'networkidle0' });
await page.screenshot({ path: 'font-check.png', fullPage: true });
const selected = await page.evaluate(() => {
const el = document.querySelector('div');
return { requested: getComputedStyle(el).fontFamily, text: el.textContent };
});
console.log(selected);
} finally {
await browser.close();
}
})();
Inspect the image or PDF, not just the CSS value: browsers can report the requested family while falling back for characters that family does not contain. Compare the same script under the same Unix account and container image used by your application.
Keep browser installation separate
Puppeteer normally downloads a compatible Chrome for Testing during installation. If a package manager or security policy blocked the postinstall script, install the browser explicitly:
npx puppeteer browsers install
Alternatively, allow the package’s postinstall step according to your package manager’s policy, then reinstall Puppeteer. If you intentionally manage Chrome yourself, configure Puppeteer to use that executable and document the path; changing the Fontconfig cache will not make an absent executable appear.
Rank #4
When packaging an application, remember that the browser cache under ~/.cache/puppeteer belongs to the user and environment that installed it. A build-stage download can be invisible to a different runtime user unless you copy it or configure a shared location with suitable permissions.
Ubuntu, CI, Docker, and read-only environments
Desktop Ubuntu
- Install the required typeface packages for every script your pages contain.
- Run
fc-cache -f -vas the account that will run Puppeteer, or ensure system caches and font directories are readable by that account. - Close and recreate long-running browser processes after changing fonts; a process may retain font state from before the installation.
Continuous integration
- Install fonts in the image or runner before the Puppeteer test starts.
- Rebuild the cache during image creation and verify it at runtime with
fc-match. - Pin the same Ubuntu, browser, and Puppeteer versions when comparing screenshots; rolling changes can alter fallback selection.
Docker
Puppeteer’s Linux troubleshooting documentation notes that Docker needs the browser’s shared libraries and suitable font coverage. A minimal image can therefore fail in two independent ways: the browser may not launch, or it may launch with missing glyphs. Install the libraries and fonts required by your chosen Ubuntu base image, run fc-cache -f -v after fonts are added, and test as the non-root runtime user.
Read-only containers
The browser may need writable XDG configuration/cache locations and a writable user-data directory. If the filesystem is read-only, provide writable mounted paths and set the relevant environment variables or Puppeteer launch options. A permission error in those paths is not repaired by erasing Fontconfig caches.
Troubleshooting by error and cause
| What you see | Likely cause | What to do |
|---|---|---|
| Boxes, tofu, or missing characters | The required glyph is absent from installed fonts, or Fontconfig metadata is stale. | Install a font covering the script, confirm it with fc-match, run fc-cache -f -v, and rerun the Puppeteer render. |
| Unexpected fallback family | The requested family lacks particular glyphs, is not readable, or is unavailable to the runtime user. | Check family selection and file permissions; compare fc-match under the service account. |
Could not find Chrome |
Puppeteer’s browser download was skipped, misplaced, or inaccessible. | Run npx puppeteer browsers install, allow the postinstall script, or configure a managed executable. |
No usable sandbox! |
Ubuntu AppArmor/user-namespace interaction or an incompatible launch environment. | Follow Puppeteer’s Ubuntu sandbox guidance and correct the environment; do not treat it as a font-cache issue or casually disable the sandbox. |
| Browser starts locally but fails in Docker | Missing shared libraries, permissions, or writable user-data/XDG paths. | Install image dependencies, provide writable paths, and test with the same runtime user. |
fc-cache reports permission errors |
The command cannot read a font directory or write its cache location. | Fix ownership/permissions or run it in the intended user context; do not delete unrelated Puppeteer files. |
Performance, reliability, and cost considerations
Font-cache rebuilding is normally an installation or image-build step, not something to run before every screenshot. Repeating it in a request handler adds filesystem work and can create race conditions when several workers update the same cache. Bake fonts and the rebuilt cache into a container image, or run a controlled initialization step before workers start.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsBest Value
For deterministic visual tests, keep the font files, Ubuntu release, Puppeteer version, and Chrome version stable. A cache hit in one environment does not guarantee identical rendering in another because fallback depends on the installed families and browser text shaping. Record the runtime user and the output of fc-match when diagnosing a regression.
Or skip the browser setup
If your goal is a reliable website screenshot or PDF rather than maintaining Chromium on Ubuntu, ScreenshotNeo provides an HTTP screenshot API and MCP server. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status.
One call is enough to try it (see the ScreenshotNeo documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. It supports full-page and element captures, lazy-image loading, device and viewport settings, dark mode, retina scale, PDF controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameters used by other screenshot APIs are accepted to ease migration.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteThe Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to start without a card.
Frequently Asked Questions
Should I delete ~/.cache/puppeteer to fix missing glyphs?
No. That directory stores downloaded browser binaries by default from Puppeteer 19.0.0; missing glyphs generally require font installation and a Fontconfig rebuild.
Will fc-cache -r -v install a missing font?
No. It erases and rescans metadata. Install a font package that contains the required script first.
Why does the CSS family look correct but characters still fall back?
A family can lack individual glyphs. Use fc-match and inspect the rendered output for the specific characters.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




