2026年11月12日
2026年11月12日
Install Headless Chrome on an Ubuntu VPS: Screenshots, PDFs, Puppeteer
This article is a localized edition of the Japanese original. Read the Japanese original
Google Chrome can run in "headless mode," which means it runs without showing any window. That makes it useful even on a VPS with no desktop environment: you can take screenshots of web pages, generate PDFs, grab the HTML of pages that are rendered with JavaScript, and automate browsing with Puppeteer.
This article uses an Ubuntu 24.04 LTS VPS (x86_64 / amd64) as the example and walks through installing the stable version of Google Chrome and using it from the command line and from Puppeteer. Versions and other details are as of November 2026.
Prerequisites
- The CPU is x86_64 (amd64). The Linux
.debof Chrome that Google distributes is built for amd64. These steps won't work on an ARM VPS, so check the official site for what is available for your architecture. - Work as a regular user with sudo. When Chrome runs as root it can't use its sandbox (the mechanism that isolates pages), and it refuses to start as is (see below).
- Leave some memory headroom. Chrome uses memory for every page it opens. On a VPS with little memory, limit how many pages you open at the same time or set up swap.
Step 1: Install Google Chrome
Download the .deb package that Google distributes and install it with apt. When you pass a file path to apt install, the libraries Chrome needs are installed along with it.
cd /tmp
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt update
sudo apt install -y ./google-chrome-stable_current_amd64.deb
# Check the version
google-chrome --version
Installing this package also registers Google's repository with apt. From then on, sudo apt update && sudo apt upgrade updates Chrome too. Chrome gets updates often, so keep it up to date on servers as well.
If the key isn't registered properly and you get a NO_PUBKEY error, register the signing key using the method in Google's instructions (the Linux Software Repositories page).
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo tee /etc/apt/trusted.gpg.d/google.asc >/dev/null
sudo apt update
Step 2: Install Japanese fonts
If the server has no Japanese fonts, Japanese text in screenshots and PDFs shows up as empty boxes (often called "tofu"). Install the Noto CJK fonts, which cover Chinese, Japanese and Korean. If you also want emoji to render, install the emoji font too.
sudo apt install -y fonts-noto-cjk fonts-noto-color-emoji
# Check that the fonts are recognized
fc-list :lang=ja | head
Step 3: Take screenshots and make PDFs from the command line
Start Chrome with --headless and it runs without opening a window. You set the output file by adding it to the flag with =.
# Screenshot (rendered at 1280 wide x 800 high)
google-chrome --headless --window-size=1280,800 \
--screenshot=/tmp/example.png https://example.com/
# PDF
google-chrome --headless --print-to-pdf=/tmp/example.pdf https://example.com/
# Print the HTML after JavaScript has run to standard output
google-chrome --headless --dump-dom https://example.com/ > /tmp/example.html
ls -lh /tmp/example.*
Since Chrome 132, the previous headless mode (the so-called old headless) has been removed from the main Chrome binary and is distributed as a separate program called chrome-headless-shell. In current Chrome, --headless alone gives you the new headless mode, so you don't need the --headless=new flag that older articles use.
Step 4: Use it from Puppeteer
To interact with pages or wait for a page to finish loading before taking a screenshot, use Puppeteer, a Node.js library. Puppeteer comes in two packages:
| Package | What it does |
|---|---|
puppeteer |
Downloads a tested build of Chrome (Chrome for Testing) when you install it |
puppeteer-core |
Doesn't download a browser. You specify where the Chrome you want to use is located |
Since you installed Chrome in Step 1, this article uses puppeteer-core. As of November 2026, puppeteer-core (the 25.x line) requires Node.js 22.12 or later. If Node.js isn't installed, install an LTS release (24.x here) from the NodeSource repository.
curl -fsSL https://deb.nodesource.com/setup_24.x -o /tmp/nodesource_setup.sh
sudo bash /tmp/nodesource_setup.sh
sudo apt install -y nodejs
node --version
Create a working folder and install puppeteer-core.
mkdir -p ~/capture && cd ~/capture
npm init -y
npm install puppeteer-core
Save the following as capture.mjs.
import puppeteer from 'puppeteer-core';
const url = process.argv[2] ?? 'https://example.com/';
const browser = await puppeteer.launch({
executablePath: '/usr/bin/google-chrome', // the Chrome installed in Step 1
headless: true, // the default: new headless mode
});
try {
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 });
await page.screenshot({ path: 'shot.png', fullPage: true });
await page.pdf({ path: 'page.pdf', format: 'A4', printBackground: true });
console.log('saved: shot.png, page.pdf');
} finally {
await browser.close();
}
Run it.
node capture.mjs https://example.com/
ls -lh shot.png page.pdf
Puppeteer's headless option takes one of three values: true (the default, new headless mode), 'shell' (uses chrome-headless-shell), and false (shows a window). The headless: 'new' setting found in older articles no longer appears in the current documentation, so use true.
If Chrome won’t start
You see "Running as root without --no-sandbox is not supported"
This error appears when you start Chrome as root. The usual fix is to run it again as a regular user. If a web app calls Chrome, create a dedicated user and run it as that user too.
sudo useradd -m -s /bin/bash chrome-runner
sudo -u chrome-runner google-chrome --headless --screenshot=/tmp/test.png https://example.com/
Add --no-sandbox only when you really have to run as root. Puppeteer's official documentation also says running without a sandbox is strongly discouraged. With the sandbox off, don't open pages whose content you can't trust.
Shared libraries are missing
If you installed the .deb with apt, its dependent libraries are normally installed as well. If Chrome still won't start, find out which libraries are missing.
ldd /opt/google/chrome/chrome | grep "not found"
Install the packages that contain the listed libraries with apt install.
Chrome crashes inside a Docker container
In a Docker container, shared memory (/dev/shm) is only 64MB by default, and tabs can crash on large pages. Either enlarge it when you start the container, for example with --shm-size=1g, or add --disable-dev-shm-usage to Chrome's launch options so it uses /tmp instead of shared memory.
const browser = await puppeteer.launch({
executablePath: '/usr/bin/google-chrome',
args: ['--disable-dev-shm-usage'],
});
On a regular VPS that isn't a container, /dev/shm is sized according to physical memory, so you normally don't need this option. You can check its size with df -h /dev/shm.
Japanese text shows up as boxes
Check with fc-list :lang=ja that the fonts from Step 2 are installed. After installing fonts, quit any running Chrome processes and take the screenshot again.
Things to watch in operation
- Keep Chrome updated. Headless Chrome is the same Chrome inside, and it receives the same security fixes. Run
apt upgraderegularly. - Always close the browser. If your script ends without calling
browser.close(), Chrome processes stay behind and keep using memory. Close it intryandfinallyas in the example above. You can find leftover processes withpgrep -a chrome. - Respect the terms of the sites you access. If you use this for scraping, check the target site's terms of use and robots.txt, and don't send large numbers of requests in short intervals.
Summary
- On an Ubuntu VPS, installing Google's
.debwithsudo apt install ./file-namealso sets up the dependent libraries and the repository. - Install
fonts-noto-cjkto render Japanese text. - Adding
--screenshot,--print-to-pdfor--dump-domtogoogle-chrome --headlesslets you take screenshots and make PDFs with a single command. - To use Chrome from Puppeteer, install
puppeteer-coreand setexecutablePathto/usr/bin/google-chrome. Usetrueforheadless. - Run Chrome as a regular user, and use
--no-sandboxonly when you must run it as root.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)