Debian

Download Web Pages as PDF on Linux with Percollate

Percollate prints every page at A5 unless you tell it otherwise. On a fresh Debian box it prints nothing at all until two things are fixed. The npm install fails with EACCES because Debian’s npm writes to /usr/local, and the first PDF fails because Puppeteer’s Chrome build is missing 13 shared libraries. Sort both and you can download web pages as PDF from the terminal with one command, minus the menus, sidebars and ads.

Original content from computingforgeeks.com - post 4177

The install uses Debian’s own Node.js with a per-user npm prefix, so nothing runs under sudo. After that come single pages, A4 output, bundles with a table of contents, EPUB and Markdown export, and the errors you will hit. Every command below was run on Debian 13 in October 2026 with percollate 4.3.0, as a normal user on a lab VM and again in clean debian:13 containers, where Chrome only started with --no-sandbox.

Install Node.js and npm from Debian

You do not need NodeSource or nvm for this. Percollate declares support for Node.js 14.17 and anything from 16 up, and Debian 13 ships Node.js 20, so the distro packages are new enough and they get security updates with the rest of the system. Before you start, the box needs:

  • Debian 13 with a normal user that has sudo
  • outbound HTTPS to the npm registry and Google’s Chrome download host
  • about 700 MB free in your home directory

Most of that disk figure is Chrome, which we measure further down. npm also keeps a download cache in ~/.npm that npm cache clean --force clears afterwards. A small VPS with a few hundred megabytes left will run out during the install. Install the two packages from the Debian archive.

sudo apt-get install -y nodejs npm

Check what you got. Any Node.js 16 or newer is fine for percollate, so the exact minor version does not matter here.

node --version; npm --version

Debian 13 reports Node.js 20 and npm 9.

v20.19.2
9.2.0

If another project on the same machine needs a newer Node.js, the Node.js install guide for Debian covers the upstream repository. Percollate itself does not care which one you run, as long as node on your PATH is 16 or newer.

Give npm a user prefix

Debian’s npm installs global packages into /usr/local/lib/node_modules, which only root can write, so a plain npm install -g percollate as your user stops with npm ERR! code EACCES. The old advice was to prefix it with sudo. Do not, because Puppeteer then downloads Chrome into root’s cache, your own user can’t launch it, and root can only run it with Chrome’s sandbox turned off. A prefix inside your home directory avoids all three problems.

Create the directory, point npm at it and put its bin folder on your PATH. The echo line covers every new shell, and the last export covers the one you are in now.

mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.bashrc
export PATH=$HOME/.npm-global/bin:$PATH

The npm config set line writes the prefix to ~/.npmrc, so it sticks across logins. If you use zsh, append the export line to ~/.zshrc instead.

Install percollate with npm

With the prefix in place the global install runs as your user and puts the percollate command in ~/.npm-global/bin. It also triggers Puppeteer’s post-install step, which downloads a matching Chrome build into ~/.cache/puppeteer, so expect the step to take about half a minute on a decent link.

npm install -g percollate

Our install finished in 29 seconds. The deprecation warning about Puppeteer is expected and harmless, and the error section at the end explains where it comes from.

npm WARN deprecated [email protected]: < 24.15.0 is no longer supported

added 354 packages in 29s

118 packages are looking for funding
  run `npm fund` for details

Confirm the shell finds the command in your prefix and that it runs. If an older copy installed with sudo is still around, command -v shows which one wins.

command -v percollate; percollate --version

The first line should point into your home directory and the second should print the current release:

/home/josphat/.npm-global/bin/percollate
4.3.0

If command -v prints nothing, the PATH export did not run in this shell, so run it again or open a new terminal. Then look at where the disk space went. Most of it goes to Chrome, not to percollate itself.

du -sh ~/.cache/puppeteer; ls ~/.cache/puppeteer/chrome; du -sh ~/.npm-global/lib/node_modules/percollate

The Chrome download is 472 MB against 106 MB for the package and its dependencies, and the folder name is the Chromium build that Puppeteer 19.11.1 downloads (Chrome 112):

472M	/home/josphat/.cache/puppeteer
linux-1108766
106M	/home/josphat/.npm-global/lib/node_modules/percollate

Because Chrome lives in the cache of whoever ran the install, install percollate as the user who will run it. Puppeteer’s install step saves the browser under the installing user’s ~/.cache/puppeteer, so a copy installed by root looks for Chrome in /root/.cache, which other users cannot read. On a shared machine each user needs their own install and their own 472 MB.

Add the shared libraries Chrome needs

On a clean Debian 13 install percollate fetches and cleans the page, then dies at the PDF step with libnss3.so: cannot open shared object file. Puppeteer’s Chrome is a plain binary, not a Debian package, so apt never pulled in its dependencies. Our lab VM did not show this error because libvirt and Mesa had already brought most of the libraries in, which is exactly why we repeated the install in an empty debian:13 container. Running ldd on the bundled binary there listed 13 missing libraries, and these Debian packages cover all of them:

sudo apt-get install -y libnss3 libnspr4 libatk1.0-0t64 libatk-bridge2.0-0t64 libcups2t64 libatspi2.0-0t64 libxdamage1 libxkbcommon0 libpango-1.0-0 libcairo2 libasound2t64

The t64 suffixes come from Debian’s 64-bit time_t transition. Ubuntu 24.04 and later went through the same rename, but we only ran this list on Debian 13. Check that nothing is still missing before you try a PDF.

ldd ~/.cache/puppeteer/chrome/linux-*/chrome-linux/chrome | grep -c "not found"

A count of zero means Chrome can load. Any other number means libraries are still missing, and dropping the -c prints their names.

0

A box that already runs a desktop or libvirt will often report zero before you install anything, so run the check first and skip the apt step if it passes.

Save a web page as PDF

The pdf command takes one or more URLs and an output path. Percollate fetches the page, strips it down to the article with Mozilla’s Readability, applies its print stylesheet and has Chrome render the result. We used a long Wikipedia article because it is a fair stress test, with tables, references and images.

percollate pdf https://en.wikipedia.org/wiki/Portable_Document_Format -o pdf-wiki.pdf

Each stage prints a check mark as it finishes, and the run took 15.4 seconds. If it stops after the Enhancing line with Failed to launch the browser process!, Chrome is the part that failed, and the error section covers why.

Fetching: https://en.wikipedia.org/wiki/Portable_Document_Format ✓
Enhancing web page: https://en.wikipedia.org/wiki/Portable_Document_Format ✓
Saved PDF: pdf-wiki.pdf

To see what you actually got, install poppler-utils, which provides pdfinfo.

sudo apt-get install -y poppler-utils

The terminal below renders the output of that run on our Debian 13 VM, with the file size and a trimmed set of PDF metadata fields.

percollate pdf command saving a Wikipedia page as an A5 PDF on Debian 13 with pdfinfo output

The page came out as a 63-page, 482 KB PDF, and the page size line is the default this guide opened with: 420 x 594.96 points is A5 portrait. That suits a phone or an e-reader, but it doubles the page count on paper. The producer line reads Skia/PDF m112 because Chrome 112 did the printing. The title comes from the page’s own title, which on Wikipedia is just “PDF”, so pass -t when you want something better.

Switch the page size to A4

Percollate has no page-size flag. Size, margins and fonts all go through CSS, either inline with --css or from a file with --style, and an @page rule is enough for paper output.

percollate pdf --css "@page { size: A4; margin: 15mm }" https://en.wikipedia.org/wiki/Rsync -o rsync-a4.pdf

Check the page size and count with pdfinfo, because a typo inside the quoted CSS fails silently and leaves you with A5.

pdfinfo rsync-a4.pdf | grep -E "Pages|Page size"

The Rsync article now fits on ten A4 pages.

Pages:           10
Page size:       594.96 x 841.92 pts (A4)

The same --css string takes any valid CSS, so "@page { size: A3 landscape } html { font-size: 18pt }" from percollate’s own help works as well. Keep repeated settings in a stylesheet and pass it with --style once you are past two or three rules, because a long quoted string is easy to break.

Bundle several pages into one PDF

Give percollate more than one URL and it builds a single PDF with a cover and a table of contents. We’d use this for a reading list or for docs that are split across pages. These are the flags that matter for a bundle:

  • -t sets the bundle title on the cover and in the metadata
  • -a sets the author
  • --toc adds a table of contents, on by default for several URLs
  • -w fetches the URLs one by one with a pause in seconds
  • --individual writes one file per URL instead

The title and author end up both on the cover page and in the PDF properties, so a file you send to someone else carries a sensible name in their reader. Build a two-article reading list with both set.

percollate pdf --toc -t "Linux reading list" -a "josphat" https://en.wikipedia.org/wiki/Rsync https://en.wikipedia.org/wiki/Tmux -o reading-list.pdf

The progress lines interleave because both pages are fetched at the same time. That is normal, and -w makes the fetches sequential if a site rate-limits you. The metadata confirms the title and author.

pdfinfo reading-list.pdf | grep -E "Title|Author|Pages"

Two articles plus the cover and contents came to 27 pages, still at A5.

Title:           Linux reading list
Author:          josphat
Pages:           27

When you want one file per page instead, add --individual and leave out -o. Percollate then names each file after the page title and writes it to the current directory.

percollate pdf --individual https://en.wikipedia.org/wiki/Rsync https://en.wikipedia.org/wiki/Tmux

Two separate files land in the directory.

-rw-rw-r-- 1 josphat josphat 244148 Oct 20 12:27 Rsync.pdf
-rw-rw-r-- 1 josphat josphat  98541 Oct 20 12:27 Tmux.pdf

Run --individual in its own directory, because the file names come from page titles you don’t control and a batch of URLs can scatter dozens of PDFs into whatever directory you’re in.

Export EPUB, HTML or Markdown

The same cleaned page can go out in three other formats, and the command only changes its first word. EPUB is the one we’d pick for an e-reader, since the text reflows instead of sitting on a fixed A5 page.

percollate epub https://en.wikipedia.org/wiki/Rsync -o rsync.epub
percollate html https://en.wikipedia.org/wiki/Rsync -o rsync.html
percollate md https://en.wikipedia.org/wiki/Rsync -o rsync.md

Each run produced a file of a very different size from the same Rsync article.

CommandOutput fileSizeBest for
percollate pdfRsync.pdf (A5)238 KBprinting, archiving a fixed layout
percollate epubrsync.epub80 KBe-readers and phones
percollate htmlrsync.html94 KBa single offline file you can open in any browser
percollate mdrsync.md42 KBnotes apps, grep, version control

Markdown is the smallest and the easiest to search, and the file starts with the title, the byline and the original publish date that Readability pulled out of the page.

head -5 rsync.md

Markdown keeps that metadata as plain text at the top.

# Rsync

By Contributors to Wikimedia projects

Published Nov 12, 2002, 11:50 AM

If you already have EPUB files and need them as PDF, our guide to converting EPUB to PDF on the command line covers that direction.

Pipe HTML in on stdin

Percollate can read HTML from stdin when you pass - as the URL. That is the route for pages you have to fetch yourself, for example with cookies or custom headers in curl. Pass the original address with -u so relative links and images still resolve.

curl -s https://example.com | percollate pdf -o example.pdf -u https://example.com -

Check that the title survived the trip through the pipe.

pdfinfo example.pdf | grep -E "Title|Pages"

Percollate read the title from the piped HTML and produced a one-page PDF.

Title:           Example Domain
Pages:           1

Percollate is a reading tool, so it throws away layout on purpose. When you need a pixel-exact copy of a page with its own CSS, use headless Chrome’s print to PDF instead, and for HTML you generate yourself, Puppeteer from a Node.js script gives you full control over the render. Our comparison of wkhtmltopdf alternatives weighs those options side by side.

Update or remove percollate

Updates go through the same user prefix, so no sudo is needed. In October 2026 the update left us on 4.3.0, which was still the newest release on npm.

npm update -g percollate
percollate --version

To remove it, uninstall the package and then delete the Chrome cache yourself, because npm does not know about that 472 MB.

npm uninstall -g percollate
rm -rf ~/.cache/puppeteer

npm reports removed 354 packages, the same count the install added. If another Puppeteer-based tool on the machine uses that cache, leave it alone and let the next install reuse it.

Fix common percollate errors

Every message below came up while we installed percollate on Debian 13, either on the lab VM or in the clean containers. They tend to arrive in this order on a fresh box:

  1. EACCES during npm install -g
  2. missing libnss3.so at the first PDF
  3. the root sandbox refusal
  4. the credentials.cc sandbox failure inside a container
  5. the Puppeteer deprecation warning, on every install

Search the page for the first line of your error. Each entry gives the cause and the fix that worked in the lab.

npm ERR! code EACCES: permission denied, mkdir ‘/usr/local/lib/node_modules’

A global npm install as a normal user on Debian prints this block and stops.

npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules'

Debian’s npm defaults its global prefix to /usr/local, which belongs to root. Set a prefix under your home directory as shown in the user prefix section, then repeat the install. Avoid sudo npm install -g: it works, but Chrome then lands in root’s cache and leads straight to the root sandbox error below.

error while loading shared libraries: libnss3.so: cannot open shared object file

Percollate fetches and enhances the page, then Puppeteer fails to start Chrome with this message (the stack trace lines are removed here).

Error: Failed to launch the browser process!
/root/.cache/puppeteer/chrome/linux-1108766/chrome-linux/chrome: error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory


TROUBLESHOOTING: https://pptr.dev/troubleshooting

Chrome is missing system libraries, and libnss3.so is only the first one the loader trips on. Install the full package list from the Chrome libraries section rather than chasing one library at a time, then confirm with the ldd check that the “not found” count is zero. The Puppeteer troubleshooting page keeps the dependency lists for other distributions.

Running as root without –no-sandbox is not supported

With the libraries installed, running percollate as root fails one step later.

Error: Failed to launch the browser process! undefined
[11055:11055:1020/125100.105339:ERROR:zygote_host_impl_linux.cc(100)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.

Chrome refuses to run its sandbox as root. The right fix is to install and run percollate as a normal user. Inside a throwaway container, whichever user you run as, pass --no-sandbox, which percollate forwards to Puppeteer.

percollate pdf --no-sandbox https://en.wikipedia.org/wiki/Rsync -o r.pdf

That produced a 244,148-byte PDF as root in our container. Don’t make it a habit on a real machine, because the sandbox is what keeps a malicious page from reaching the rest of the system. A percollate installed by root also leaves its Chrome in /root/.cache/puppeteer, where other users cannot reach it, so switching to a normal user means installing again as that user.

FATAL:credentials.cc(127) Check failed: … Permission denied (13)

Running as a normal user does not save you inside a container. We installed percollate as an unprivileged user with its own ~/.npm-global prefix and all 13 libraries in a rootful podman debian:13 container, and Chrome still refused to start:

Error: Failed to launch the browser process!
[11076:11076:1020/161435.671062:FATAL:credentials.cc(127)] Check failed: error == EPERM || error == EUSERS || error == EINVAL || error == ENOSPC. : Permission denied (13)
#5 0x559022df8b07 logging::CheckError::~CheckError()
#6 0x559024bacb41 sandbox::Credentials::CanCreateProcessInNewUserNS()

The last frame names the cause. Chrome’s sandbox needs to create a new user namespace, and the container’s default security profile refuses that call with EACCES (13), an error Chrome’s check does not expect, so it aborts before any page is printed. On the Debian 13 VM itself the same normal-user run worked, because Debian allows unprivileged user namespaces on the host. In a throwaway container, add --no-sandbox as shown in the root entry above; the same normal user then saved the 244148-byte PDF in our container run. Puppeteer’s Docker notes also describe running the container with a relaxed seccomp profile or --cap-add=SYS_ADMIN so the sandbox can stay on, but we did not test either. Ubuntu 23.10 and later restrict unprivileged user namespaces through AppArmor, which can block the downloaded Chrome’s sandbox for normal users on the host too. We did not run percollate on Ubuntu, so check the Puppeteer troubleshooting page if you see this error there.

npm WARN deprecated [email protected]: < 24.15.0 is no longer supported

This warning appears on every install and does not stop anything. Percollate 4.3.0 depends on Puppeteer ^19, which resolves to 19.11.1 and is no longer supported upstream, and that is also why the Chrome it downloads is version 112. Every command in this guide worked with it. Keep an eye on the percollate repository for a release that moves to a newer Puppeteer, and run npm update -g percollate when it lands.

Keep reading

Configure Samba File Share on Debian 13 / 12 Debian Configure Samba File Share on Debian 13 / 12 Backup and Restore Linux Systems with Timeshift Debian Backup and Restore Linux Systems with Timeshift Install KVM on Debian 13 / Debian 12: Complete Guide KVM Install KVM on Debian 13 / Debian 12: Complete Guide Upgrade a Single Package on Ubuntu / Debian with apt Debian Upgrade a Single Package on Ubuntu / Debian with apt APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Cheat Sheets APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Install Prometheus 3 on Ubuntu 24.04 / Debian 13 Debian Install Prometheus 3 on Ubuntu 24.04 / Debian 13

Leave a Comment

Press ESC to close