ScreenshotNeo

BlogHow-to

How to Run Puppeteer on an Azure Virtual Machine

Install Puppeteer on an Azure Linux VM, handle Chrome dependencies and sandboxing, and verify a headless browser with a runnable smoke test.

By the ScreenshotNeo team4 October 202610 min read

To run Puppeteer on an Azure Linux virtual machine, install a supported Node.js version, install Puppeteer and its compatible Chrome for Testing browser, install the browser’s Linux libraries, then launch a small smoke test over SSH. Puppeteer’s current requirements list Node.js 22.12 or newer and Chrome for Testing on Debian or Ubuntu for x64 and arm64. The exact system packages depend on the VM image and browser build, so use Puppeteer’s dependency list for that image and check missing libraries if Chrome will not start.

This guide covers Azure Virtual Machines. An Azure App Service has a different deployment and operating-system model: the VM steps below assume you can install and manage OS packages directly.

1. Choose and access a Linux VM

Choose a Linux distribution and CPU architecture supported by the Node.js and Chrome versions you intend to use. This walkthrough uses Debian or Ubuntu examples; confirm Puppeteer’s current system requirements and troubleshooting guidance for your actual image before installing packages.

Connect to a VM with a public IP using SSH, or use Azure Bastion when the VM has no public IP. If you cannot connect or package installation later fails, check how the VM’s network is configured before treating it as a Puppeteer problem.

ssh <username>@<vm-public-ip>

After connecting, check the distribution and architecture:

cat /etc/os-release
uname -m

Common architecture names include x86_64 for x64 and aarch64 for arm64. Verify that the selected Puppeteer and browser setup supports the VM’s reported architecture.

2. Install a supported Node.js runtime

Install Node.js 22.12 or newer using a method appropriate for your distribution and operational policy. The commands below are checks, not a Node.js installation procedure; available repository versions and installation methods vary. Do not assume that a distribution’s default Node.js package meets Puppeteer’s current minimum.

node --version
npm --version

If either command is missing, or Node is older than the required version, install or update Node before creating the project. Recheck the version in the same account and shell that will run Puppeteer, especially when an application service uses a different user or PATH.

3. Install Puppeteer and its browser

For the usual setup, use puppeteer. It downloads a compatible Chrome for Testing browser during installation. In your application directory:

mkdir puppeteer-azure
cd puppeteer-azure
npm init -y
npm install puppeteer

If your package installation policy disables lifecycle scripts, Puppeteer may install without downloading the browser. Install it explicitly with Puppeteer’s documented browser installer:

npx puppeteer browsers install

Use puppeteer-core if you deliberately manage the browser yourself or connect to a remote browser. It does not download Chrome. You must provide a compatible browser executable path or the appropriate connection configuration yourself. For example, install the package and configure a path that exists and is executable by the application’s runtime user:

npm install puppeteer-core

Do not switch to puppeteer-core just to work around a failed download without also arranging and validating the browser it needs.

4. Install Chrome’s Linux dependencies

The npm package does not supply every native Linux library Chrome expects. On Debian or Ubuntu, consult Puppeteer’s current troubleshooting page for its documented dependency packages and install the ones applicable to your image and browser build. The list covers certificate and font support, GTK and ATK, NSS, GBM, X11 libraries, and sound libraries; package names can change as distributions evolve.

For example, after reviewing the current Puppeteer dependency list for your image, install the applicable packages through the VM’s package manager. The following illustrates the APT update step, but intentionally does not prescribe a fixed dependency list for every release:

sudo apt-get update

If Chrome reports a missing shared library, use ldd on the Chrome executable Puppeteer installed and inspect entries marked “not found.” Install the corresponding package from the VM distribution’s repositories, then repeat the check.

find "$HOME/.cache/puppeteer" -type f -name chrome -print

Run ldd using the executable path returned above:

ldd /path/to/chrome | grep "not found"

The browser may be stored in a different cache location when installation ran under another user or with a custom cache configuration. Check the environment and user that performed installation before concluding that the browser is absent.

5. Run a headless smoke test

Create smoke-test.cjs in the project directory. This CommonJS script launches Puppeteer’s downloaded browser, opens a page, navigates to a URL, prints the page title, and closes the browser even if navigation fails.

const puppeteer = require('puppeteer');

(async () => {
  let browser;
  try {
    browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    await page.goto('https://example.com', {
      waitUntil: 'domcontentloaded',
      timeout: 30000,
    });
    console.log(await page.title());
  } catch (error) {
    console.error('Puppeteer smoke test failed:', error);
    process.exitCode = 1;
  } finally {
    if (browser) await browser.close();
  }
})();

Run it as the same operating-system user and from the same deployment environment as the real application:

node smoke-test.cjs

This is a diagnostic example, not a claim that it has been run on your Azure VM. If the script prints the page title, browser launch and a basic outbound navigation worked for this run. It does not establish that every target website, workload, or production deployment will work.

6. Keep the browser sandbox enabled

Chrome uses Linux sandbox layers. Keep the sandbox enabled where possible and run the application as an appropriate unprivileged user. If launch fails with a sandbox error, investigate the VM’s kernel and distribution security policy, permissions, and runtime user. Some environments can be affected by user-namespace policy or AppArmor rules.

Puppeteer’s troubleshooting guidance says that launching with --no-sandbox is only an option when you absolutely trust the content opened in Chrome, and strongly discourages running without a sandbox. Do not add that flag as a routine fix, particularly when opening arbitrary URLs. If you consider it for a narrowly controlled workload, understand and accept the security tradeoff first.

7. Configure production runs reliably

Close browser resources

Close pages and browser instances in finally blocks. A process that creates a new browser for every job should always close it when the job completes or fails. For longer-lived workers, define when browser instances are recycled and observe their process and memory use under the real workload; requirements depend on page complexity and concurrency.

Set timeouts and report useful errors

Set explicit navigation or operation timeouts, and log the failing URL, operation, and error. Distinguish a launch error from a navigation timeout: launch errors often point to libraries, sandboxing, permissions, or an executable path, while navigation failures can involve DNS, outbound network rules, or the destination site.

Use the same runtime identity for install and execution

Browser downloads and caches can be user-specific. If deployment installs packages as one user and a service runs as another, confirm that the runtime user can access the browser executable and its cache. For a separately managed executable, check its path, file permissions, architecture, and compatibility with the installed Puppeteer version.

Plan around the VM’s network and disk

APT, npm, and Puppeteer’s browser installation need access to their respective repository or download endpoints. Browser binaries and native packages also consume VM disk space. Confirm outbound rules and available disk before automating repeated deployments; no single VM size or cost estimate fits every workload.

8. Troubleshoot common failures

Symptom Likely cause What to check or do
“Could not find Chrome” or no executable at launch The browser download did not run, installation scripts were disabled, or install and runtime users have different caches. Run npx puppeteer browsers install; confirm the browser exists and that the service user can access it. Check whether the project uses puppeteer or puppeteer-core.
“error while loading shared libraries” or a missing .so A required native Linux library is absent. Run ldd on the Chrome executable, identify entries marked “not found,” and install the corresponding package for the VM’s distribution and release. Consult Puppeteer’s current dependency guidance.
“No usable sandbox!” or Chrome exits during launch Sandbox prerequisites, permissions, kernel settings, or distribution security policy may prevent launch. Check the runtime user and Linux policy, including relevant user-namespace or AppArmor restrictions. Keep the sandbox enabled where possible; do not use --no-sandbox as a default workaround.
apt-get update cannot reach repositories Outbound connectivity, DNS, a firewall, NSG, NAT gateway, load balancer outbound rules, or virtual appliance policy may block access. Check the VM’s actual outbound design and repository reachability. Azure documents outbound networking as a possible cause of APT failures.
npm or browser download times out The VM cannot reach the needed package or browser download endpoint, or the path is slow or filtered. Check outbound policy, DNS, proxy requirements, and whether the configured network permits the relevant endpoint. Retry only after checking connectivity.
Configured executable path fails The path is wrong for the service user, not executable, points to a missing browser, or names an incompatible build. Check the file exists and is executable under the runtime identity. Confirm the selected browser and Puppeteer setup are compatible.
Navigation times out after Chrome launches The destination may be slow or unreachable from the VM, or the selected readiness condition may wait too long. Check DNS and outbound access from the VM, set an explicit timeout, and choose a navigation readiness condition that matches the page and task. Log the target and error.
Headful launch fails on a server A visible browser session needs a display environment. Prefer headless execution for unattended server automation. For a workflow that requires headful execution, provide a display environment; Puppeteer mentions Xvfb for headful CI use.

9. Distinguish an Azure VM from Azure App Service

A Linux VM gives you direct control of the OS packages and browser runtime, which is why this guide installs system dependencies on the machine. If your actual question is how to run Puppeteer in an Azure Node.js App Service, follow deployment guidance for that hosting environment instead of assuming VM SSH and package-management steps apply unchanged. Likewise, examples for Azure Stack Hub are not proof that every Azure VM image has the same Node.js version or defaults.

Or skip the browser setup

If your task is to capture website screenshots rather than automate arbitrary browser interactions, ScreenshotNeo provides a website screenshot API and MCP server. It accepts a URL in one GET request and returns PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for its parameters and response details.

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,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Cookie banners are accepted and removed before the shot, along with known newsletter popups and chat widgets. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

Performance, reliability, and cost considerations

  • Performance: browser startup and page rendering take resources, and workload needs vary with page complexity, concurrency, and the VM. Measure your own workload before choosing VM capacity; this guide makes no benchmark or sizing claim.
  • Reliability: make browser cleanup explicit, use bounded timeouts, log launch and navigation errors separately, and validate outbound access from the deployed VM. A successful smoke test covers only that environment and target at that time.
  • Cost: the VM and its storage/network use are separate from Puppeteer. Estimate Azure costs using your chosen region, VM, disk, and expected runtime; the research available for this guide does not establish a universal price.
  • Download footprint: Puppeteer adds a browser binary, and Chrome needs native libraries. Ensure deployment and VM disk policies account for both; exact download size varies and is intentionally not stated here.

FAQ

Can I run Puppeteer without installing Chrome myself?

Yes. The puppeteer package downloads a compatible Chrome for Testing browser by default, provided its installation step can run and the VM can reach the download endpoint. You still need the browser’s Linux libraries.

When should I choose puppeteer-core?

Choose it when you manage the browser separately or connect to a remote browser. It does not download Chrome, so configure the browser path or connection explicitly.

Does installing Node.js automatically make Puppeteer work?

No. The selected Node version, browser download, native Linux libraries, outbound network access, and sandbox configuration all matter.

Is --no-sandbox safe for a public URL screenshot service?

Puppeteer strongly discourages running without Chrome’s sandbox. Its documentation limits the option to cases where you absolutely trust the content opened in Chrome; a service that accepts arbitrary URLs should not treat it as a routine fix.

Official references