ScreenshotNeo

BlogHow-to

Chrome Headless Screenshot Not Working on a VPS: Linux Fixes

Diagnose missing, blank, or incomplete Chrome Headless screenshots on a Linux VPS with a step-by-step check of commands, versions, readiness, GPU, and sandboxing.

By the ScreenshotNeo team4 October 20267 min read

If Chrome Headless screenshots are failing on a Linux VPS, first find out which failure you have: Chrome does not launch, the screenshot file is missing, or the file exists but is blank or incomplete. Run a minimal command from a known writable directory, check its exit status and output file, then investigate page readiness, browser version, GPU configuration, or sandboxing based on the evidence. Those symptoms have different causes, so changing several flags at once makes diagnosis harder.

1. Confirm Chrome launches and writes the screenshot

Start with Chrome’s documented headless screenshot command. Run it from a directory your user can write to, and check that the output file exists:

mkdir -p "$HOME/tmp/chrome-shot"
cd "$HOME/tmp/chrome-shot"
chrome --headless --screenshot https://example.com
status=$?
printf 'Chrome exit status: %s\n' "$status"
ls -l screenshot.png
file screenshot.png

Chrome writes screenshot.png to the current working directory by default. If Chrome is not on your PATH, use the actual executable name or full path for the installed build. Check the command’s exit status and stderr before interpreting a missing file as a rendering problem. The Chrome Headless documentation describes --screenshot and the default output.

Use a controlled viewport

A viewport makes results easier to compare and is useful when a page behaves differently at different widths:

chrome --headless --screenshot --window-size=412,892 https://example.com

If the file is created, the browser launched and got far enough to capture. The next question is whether the page had finished rendering. If no file appears, record the browser version, exact command, exit code, and stderr; then check the output directory’s permissions and whether the executable starts at all.

2. Diagnose blank or incomplete captures

A screenshot that exists but shows a blank page, a loading state, or missing content points toward page readiness or navigation behavior rather than an output-path problem. Chrome’s command-line reference documents --timeout in milliseconds: capture proceeds after that maximum wait even if the page is still loading.

chrome --headless --screenshot --timeout=10000 https://example.com

Increase the timeout only as a diagnostic when the page needs more time. A fixed delay cannot guarantee that a particular application has finished rendering: pages may load data after navigation, defer images until they are visible, or wait on third-party resources. Compare a short and longer timeout, and inspect the page in a regular browser if you need to determine whether the content is available at all. See the Headless command-line documentation for the timeout option.

Check what “not working” means

  • No image file: verify the current directory, write permissions, executable path, exit status, and stderr.
  • Zero-byte or unreadable image: check whether Chrome reported a capture or process error; confirm the file type with file.
  • White or mostly blank image: test whether the page is still loading, requires interaction, or is showing a bot check.
  • Missing images or lower-page content: determine whether the content is lazy-loaded or appears only after scrolling or interaction.
  • Wrong layout: set a known viewport with --window-size and compare again.

3. Check the Headless implementation and version

Ask which Chrome or Chromium build is installed before applying version-specific advice. Chromium documents that, as of M132, the old Headless implementation is no longer included in the Chrome binary. In current Chrome, --headless=old has no effect. If your workflow depends specifically on the old implementation, Chromium points to the separate chrome-headless-shell binary.

chrome --version
# If using Chromium, use the executable name installed on your system instead.

Do not add --headless=old as a generic fix. First establish whether the command or automation actually depends on old Headless behavior. The change and migration guidance are in the Chromium Headless implementation announcement.

4. Treat GPU flags as a targeted comparison

Ordinary headless screenshots do not automatically require you to force GPU settings. If Chrome logs point to a GPU or graphics initialization problem, compare the default headless run with an explicitly enabled GPU run, and keep the browser version and page constant. Chromium says --enable-gpu disables forced software rendering. Its documented Linux OpenGL autodetection requires an X11 server and DISPLAY; Vulkan has worked on some Linux configurations, but that does not make it a universal VPS setting.

# Baseline: use the default headless rendering path.
chrome --headless --screenshot https://example.com

# Comparison only when investigating GPU initialization:
chrome --headless --enable-gpu --screenshot https://example.com

If the GPU-enabled run fails while the baseline works, remove the GPU flag. If graphics errors appear in both, capture the exact log and check the server’s driver and display configuration before trying a backend-specific setting. Do not add Vulkan or other GPU flags without evidence that the VPS supports that path. See Chromium’s Headless GPU documentation.

5. Investigate sandbox errors without disabling protection by default

Do not treat --no-sandbox as a routine VPS fix. First confirm the failure is a sandbox launch error from Chrome’s stderr, then check which user runs Chrome, whether it is in a container, and which kernel sandbox mechanisms are available. Chromium describes Linux sandboxing as layered, with mechanisms selected according to available kernel features.

Chrome’s Headless documentation states that --no-sandbox is not needed when a user is properly set up in the container. Prefer correcting the runtime user or container configuration when that is the underlying issue. Disabling the sandbox removes a browser security boundary; only consider it in a controlled environment after identifying why the normal sandbox cannot start. Consult the Chrome Headless documentation and Chromium’s Linux sandbox design documentation.

6. A practical troubleshooting checklist

  1. Record chrome --version and the exact command, including all flags.
  2. Run a basic screenshot from a known writable directory with no extra GPU or sandbox flags.
  3. Check the exit status, stderr, and whether screenshot.png exists and is a valid image.
  4. If the image exists but is incomplete, compare with a longer --timeout and a controlled --window-size.
  5. If logs identify GPU initialization, compare the default path with --enable-gpu; verify X11 and DISPLAY before assuming the default OpenGL path can initialize.
  6. If logs identify sandbox startup, inspect the runtime user, container, and kernel support before changing sandbox flags.
  7. If the command explicitly uses --headless=old, check whether the installed Chrome is M132 or newer and whether the workflow needs chrome-headless-shell.

When asking for help, include the Linux distribution, Chrome or Chromium version, whether the process runs in a container, the exact command, stderr, exit code, and whether the result is missing, blank, or incomplete. The official guidance cannot identify a specific root cause without those details.

7. Reliability, speed, and cost considerations

Keep a known-good baseline command and change one variable at a time. A controlled viewport and explicit timeout help make runs easier to compare, but a timeout is only a maximum wait, not proof that an application is ready. Longer waits can help slow pages while increasing capture latency. GPU flags depend on the VPS’s graphics setup, and changing sandbox behavior affects security; neither should be part of a default command without a reason.

For repeated captures, record the browser version and command alongside the image so that a browser update or configuration change can be distinguished from a site change. The research for this guide does not establish universal VPS performance figures, a required memory setting, or a particular missing system library, so diagnose those from the actual logs and environment rather than assuming them.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. Instead of installing and operating a headless browser on the VPS, send one GET request. See the ScreenshotNeo API documentation for options and setup.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status. Its MCP server provides screenshot tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

Frequently asked questions

Does a VPS need a desktop session for Chrome Headless?

The documented screenshot command is headless. However, Chromium’s documented default Linux OpenGL autodetection requires X11 and DISPLAY, so graphics initialization details matter if you explicitly investigate that rendering path.

Should I always set a longer timeout?

No. Use a longer timeout as a comparison when the page is incomplete. It bounds how long Chrome waits; it does not confirm that application content is ready.

Is --no-sandbox the normal fix for Chrome on a VPS?

No. Identify a sandbox launch error first and inspect the user, container, and kernel context. Chrome says it is unnecessary when the container user is properly set up.

What details should I include in a bug report?

Include the browser version, exact command, exit status, stderr, Linux and container context, and whether the screenshot is missing, blank, or incomplete.