ScreenshotNeo

BlogScreenshots on your device

How to Take Screenshots on Ubuntu Server with npm desktop-screenshot

Use npm desktop-screenshot on Ubuntu Server with scrot, X11 or Xvfb, plus fixes for display errors and a hosted API alternative.

By the ScreenshotNeo team30 September 20266 min read

How to Take Screenshots on Ubuntu Server with npm desktop-screenshot

Yes, but only when the Node.js process can reach an X display. The npm package desktop-screenshot captures the computer screen through platform-specific external tools. On Linux its README specifies scrot. A minimal Ubuntu Server installation usually has no graphical session, so you must either expose an existing X display or start a virtual display such as Xvfb.

This package captures the desktop framebuffer. It does not open a URL in a browser, wait for a page, or render a website independently. If you need website screenshots from a server, skip to ScreenshotNeo.

1. Check that you installed the right package

Two npm names are easy to confuse:

Package Linux dependency documented by its README API style
desktop-screenshot scrot Callback-based example that writes a named file
screenshot-desktop ImageMagick Different package and different options

The commands below are for desktop-screenshot. Confirm your package.json and import statement before troubleshooting.

2. Install Node.js, scrot and the package

sudo apt update
sudo apt install -y nodejs npm scrot
mkdir -p ~/desktop-shot
cd ~/desktop-shot
npm init -y
npm install desktop-screenshot

Run the install and the capture as the same Linux account used by your service. A package installed for one user is not automatically available to another service account.

3. Capture the current display

Create capture.js:

The capture path: Node.js calls the package, scrot reads the X display, and the image is written to disk.
The capture path: Node.js calls the package, scrot reads the X display, and the image is written to disk.
const screenshot = require('desktop-screenshot');

const output = '/tmp/ubuntu-screen.png';

screenshot(output, (error, complete) => {
  if (error) {
    console.error('Screenshot failed:', error);
    process.exitCode = 1;
    return;
  }

  console.log('Screenshot written:', output, complete);
});

Start it from a shell that already has a graphical display:

echo "$DISPLAY"
node capture.js
file /tmp/ubuntu-screen.png

If DISPLAY is empty, Node has no display to capture. Logging in over SSH does not create a desktop session.

4. Resize the output or set JPEG quality

The package README shows optional width, height and JPEG quality settings. Keep the callback error check even when using options. A typical call is:

const screenshot = require('desktop-screenshot');

const options = {
  width: 1280,
  height: 720,
  quality: 85
};

screenshot('/tmp/ubuntu-screen.jpg', options, (error) => {
  if (error) {
    console.error(error);
    process.exitCode = 1;
    return;
  }
  console.log('JPEG saved');
});

Check the installed package README if your version reports an options-signature error. The exact available values can vary with the package version and the underlying capture utility.

5. Run it on a server with an existing X session

If a desktop session is already running, identify its display and authorization cookie:

echo "$DISPLAY"
who
ps -ef | grep -E 'Xorg|Xwayland' | grep -v grep

Launch the Node process under the account that owns that session, or configure the service with the correct DISPLAY and X authority environment. Merely setting DISPLAY=:0 is insufficient if the account cannot authenticate to that display.

6. Use a virtual display for a headless Ubuntu Server

When no physical desktop is available, Xvfb can provide a virtual X server. This is a general headless pattern documented for screenshot utilities; the research available for this article does not establish a package-specific desktop-screenshot and xvfb-run compatibility test. Verify the command in your own Ubuntu image.

Xvfb supplies the virtual display that headless processes otherwise lack.
Xvfb supplies the virtual display that headless processes otherwise lack.
sudo apt update
sudo apt install -y xvfb scrot
xvfb-run --auto-servernum --server-args='-screen 0 1280x720x24' node capture.js

The virtual screen is initially empty. It is useful for programs that draw into X, but it will not reproduce a user’s physical desktop unless you also start the graphical application inside that display. For a browser, start the browser and the capture process within the same Xvfb invocation or session.

7. A production-friendly wrapper

const fs = require('node:fs');
const path = require('node:path');
const screenshot = require('desktop-screenshot');

const output = path.resolve(process.env.OUTPUT || '/var/tmp/desktop-shot.png');
fs.mkdirSync(path.dirname(output), { recursive: true });

screenshot(output, (error) => {
  if (error) {
    console.error(JSON.stringify({ ok: false, output, error: String(error) }));
    process.exit(1);
  }

  if (!fs.existsSync(output)) {
    console.error(JSON.stringify({ ok: false, output, error: 'Output file was not created' }));
    process.exit(1);
  }

  console.log(JSON.stringify({ ok: true, output, bytes: fs.statSync(output).size }));
});

Use a writable absolute path, avoid exposing screenshots through a public temporary directory, and set a timeout at the process-supervisor level if the external utility can hang.

8. Troubleshooting

Symptom Likely cause Fix
Cannot open display or an empty DISPLAY No X server is available to the process Use the existing session’s display or run through Xvfb.
It works in SSH but not as a service The service has different environment variables or X authority Run under the same account and explicitly configure DISPLAY and authorization.
scrot: command not found The Linux capture utility is missing Install it with sudo apt install scrot.
Permission denied writing the image The output directory is not writable by the service account Choose a writable absolute path or fix ownership and permissions.
Blank or black image under Xvfb No application is drawing in the virtual display Start the target application inside the same Xvfb display and wait for it to render.
Options argument causes a type error Wrong package or a version-specific API difference Check that the dependency is desktop-screenshot and read the installed README before changing the call signature.
Capture hangs External tool, display connection or application is blocked Reproduce the scrot command manually, add a supervisor timeout, and inspect process logs.

9. Reliability, performance and cost

  • Reliability: success depends on the X server, X authorization, scrot, filesystem permissions and the application being visible in that display. Treat the callback error as authoritative and verify the output file.
  • Performance: screen size, image format, JPEG quality and the startup cost of Xvfb affect runtime and file size. Keep a long-lived virtual display for repeated captures instead of starting a new server for every request when your process model allows it.
  • Isolation: a shared display can expose other windows or desktop data. Use a dedicated account and display for automated work.
  • Cost: the npm approach consumes your server’s CPU, memory and storage. Xvfb adds another process; it does not provide browser rendering or hosting.

10. When desktop-screenshot is the wrong tool

Use this package when you specifically need the pixels of an X desktop on the machine running Node. Choose a browser screenshot API when the input is a URL and you need page loading, full-page output, cookie handling, responsive viewports, PDFs or repeatable server-side rendering.

Or skip the browser setup

ScreenshotNeo takes a URL and returns a PNG, JPEG, WebP or PDF without configuring X11, scrot or Xvfb. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Before the capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account and start with 1,000 screenshots per month at no charge.

FAQ

Does desktop-screenshot take a screenshot of a URL?

No. It captures the desktop visible to the X display. Use a browser automation tool or ScreenshotNeo for URL-based captures.

Can I run it without installing a desktop environment?

Usually yes, with a virtual X server such as Xvfb, but start and test the target application inside that display.

Is Xvfb required on every Ubuntu Server?

No. If the process already has access to a suitable running X display, Xvfb is unnecessary.

Why is screenshot-desktop mentioned?

It is a different npm package with different Linux requirements. Installing or configuring it does not validate a desktop-screenshot setup.

Where should I investigate first when a capture fails?

Check the exact package name, scrot installation, DISPLAY, X authorization, output permissions and the callback error in that order.