How to Set Up a Headless Ubuntu Server for Browser Automation
Set up a secure, patched Ubuntu server for Playwright browser automation with SSH, browser dependencies, jobs, troubleshooting, and scaling guidance.
Direct answer: deploy a supported Ubuntu Server image, connect with SSH, create a non-root automation user, patch the operating system, install your Playwright package and matching browser binaries, then run jobs through a service or scheduler. Keep secrets and artifacts outside source code, measure resource use with representative pages, and allow only the network traffic your deployment needs.
This guide uses Ubuntu Server 24.04 LTS amd64 and Node.js with Playwright. Playwright currently lists Ubuntu 22.04, 24.04 and 26.04 on x86-64 or arm64; verify the support list before publishing or provisioning because it can change (Playwright system requirements). The commands work on a cloud image or owned hardware, but provider networking and firewall controls differ.
1. Choose the server and Ubuntu release
Ubuntu’s published figures are operating-system requirements, not browser-concurrency guarantees. For Ubuntu 24.04 LTS amd64, Ubuntu lists 1 GB RAM and 4 GB storage as minimums for cloud images, and suggests 3 GB or more RAM and 25 GB or more storage (Ubuntu Server system requirements). Browser processes, downloads, screenshots, traces and parallel jobs need additional headroom.
| Decision | What to check |
|---|---|
| Cloud image | Image availability, SSH key injection, private networking, snapshots, disk persistence and provider firewall controls. |
| Owned hardware | Physical access, power recovery, inbound network policy, disk replacement and remote administration. |
| Ubuntu release | Playwright support, provider image availability, lifecycle and compatibility with your runtime. |
| Architecture | Use x86-64 or arm64 only when your selected Playwright/browser dependencies support it. |
Start with a modest machine for one lightweight job, then measure CPU, memory, disk and network use while loading your real pages. Increase capacity for parallel browsers, video, large downloads, PDFs, traces or long retention.
2. Connect with SSH and create a limited account
Confirm that a new SSH session works before changing firewall rules. Ubuntu’s OpenSSH guidance covers server installation and key-based access (Ubuntu OpenSSH documentation).
# From your local computer
ssh-keygen -t ed25519 -C "automation-server"
ssh ubuntu@SERVER_IP
# On the server: create a dedicated operator account
sudo adduser browserops
sudo usermod -aG sudo browserops
# Copy your authorized key from the initial account
sudo install -d -m 700 -o browserops -g browserops /home/browserops/.ssh
sudo cp ~/.ssh/authorized_keys /home/browserops/.ssh/authorized_keys
sudo chown browserops:browserops /home/browserops/.ssh/authorized_keys
sudo chmod 600 /home/browserops/.ssh/authorized_keys
# Verify a second session before hardening the first one
ssh browserops@SERVER_IP
Use routine automation as browserops, not root. Ubuntu’s security guidance recommends enforcing least privilege (Ubuntu security suggestions). If you disable password authentication or root SSH login, keep an already verified key-based session open until the new policy is confirmed.
Firewall principles
Allow only traffic required by your trigger model. A server that makes outbound browser requests may need no public HTTP port; a webhook receiver or internal service may need one. The exact rules depend on your provider and network.
# Review before enabling; replace the SSH port if you changed it
sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status verbose
Ubuntu documents UFW as its uncomplicated firewall tool. Provider firewalls and host firewalls both matter, so check both layers.
3. Patch Ubuntu and decide how reboots affect jobs
sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl git unzip ufw
sudo reboot
Ubuntu normally installs unattended-upgrades and applies security updates daily. Its configuration controls eligible origins and cadence; automatic reboot is configurable and defaults to false (Ubuntu automatic updates). Updates can restart services or require a reboot, so schedule restarts around automation windows and inspect logs.
sudo systemctl status unattended-upgrades
sudo journalctl -u unattended-upgrades --since yesterday
sudo grep -R "Automatic-Reboot" /etc/apt/apt.conf.d/
Pin application dependencies and record the Ubuntu image, Node.js version, Playwright version and browser revision used by each deployment.
4. Install Node.js and Playwright
Install Node.js using the method your project standardizes on, then install Playwright in the project. The framework package and browser binaries are separate installations.
mkdir -p ~/browser-jobs
cd ~/browser-jobs
npm init -y
npm install playwright
npx playwright install --with-deps chromium
Playwright’s browser documentation also supports a headless-shell-only download with --only-shell when the project uses that mode; do not select it automatically for projects that need the full Chromium browser (Playwright browser installation). When upgrading Playwright, check its instructions and install matching browser binaries and Linux dependencies again.
5. Run a representative headless job
cat > capture.js <<'EOF'
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'networkidle', timeout: 60000 });
await page.screenshot({ path: 'example.png', fullPage: true });
await browser.close();
})();
EOF
node capture.js
file example.png
Use a small page set that reflects production. Confirm browser launch, DNS and outbound HTTPS access, screenshot output, log collection and disk growth before scheduling parallel work.
6. Run jobs as a service
A systemd unit restarts a failed process and keeps execution separate from SSH sessions. The following example runs a long-lived worker; adapt the command to your queue or scheduler.
sudo tee /etc/systemd/system/browser-worker.service > /dev/null <<'EOF'
[Unit]
Description=Headless browser automation worker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=browserops
WorkingDirectory=/home/browserops/browser-jobs
ExecStart=/usr/bin/node /home/browserops/browser-jobs/worker.js
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now browser-worker
sudo systemctl status browser-worker
journalctl -u browser-worker -f
Keep API keys and cookies in a protected environment file or secret store rather than source code. Restrict screenshot, trace, download and log directories to the worker account, and define retention so a busy job cannot fill the disk.
7. Resource planning and performance
- Measure peak RSS memory per browser and page, not just idle server memory.
- Limit concurrency until CPU, memory, file descriptors and network bandwidth remain stable.
- Reuse a browser process where safe, but isolate jobs that require separate cookies or permissions with separate contexts.
- Use explicit navigation and action timeouts. Avoid waiting for network idle on pages that keep analytics or websocket connections open; wait for a known selector when possible.
- Store large artifacts on separate durable storage when local disk is small.
- Use a swap policy appropriate to your environment, but treat swapping as a failure signal rather than capacity.
These are operational recommendations, not Playwright benchmarks. Establish your own limits from the pages and concurrency you actually run.
8. Reliability checklist
- Verify SSH access from a second session before firewall or SSH changes.
- Record the Ubuntu release and architecture with
lsb_release -aanduname -m. - Pin package versions and rebuild browser binaries during controlled deployments.
- Log URL, job ID, browser version, duration, exit status and failure category without logging secrets.
- Keep retries bounded and use backoff for transient network failures.
- Capture a diagnostic screenshot, trace or HTML only when policy permits and retention is defined.
- Monitor disk, memory, CPU, failed services and unattended-upgrade activity.
- Test recovery from a failed process, reboot and expired credentials.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Executable doesn't exist |
Playwright package installed without its browser revision. | Run npx playwright install chromium (or the browser required by the project), then verify the package and browser versions match. |
| Missing shared libraries | Linux browser dependencies were not installed. | Run npx playwright install --with-deps chromium on a supported Ubuntu image. |
| SSH connection refused | Wrong address, provider firewall, UFW rule or stopped SSH service. | Use the provider console, verify the address and security rules, then check sudo systemctl status ssh. |
| Jobs work over SSH but stop after logout | Process is tied to the shell session. | Run it under systemd or a scheduler instead of an interactive shell. |
| Timeouts on real sites | Slow page, blocked outbound traffic, DNS failure or a page that never becomes idle. | Test DNS and HTTPS, increase a bounded timeout, and wait for a meaningful selector instead of indefinite network idle. |
| Blank or partial screenshots | Lazy content has not loaded, viewport is wrong or the page requires interaction. | Wait for the target selector, scroll or trigger the required interaction, and record the viewport used. |
| Out-of-memory kills | Too many concurrent browsers, large pages or oversized artifacts. | Reduce concurrency, close contexts, increase memory and inspect kernel logs with dmesg or journalctl -k. |
| Failures after an update | Runtime, browser revision or system library changed. | Review unattended-upgrade logs, pin and redeploy known-good versions, then schedule reboots deliberately. |
10. Cost and hosting trade-offs
A cloud VPS usually reduces hardware work and offers snapshots and provider networking, while owned hardware can provide persistent local storage and physical control. Compare recurring cost, administration time, recovery options, network configuration and the consequences of a failed update. Ubuntu’s minimum requirements should not be treated as a guaranteed browser capacity.
Or skip the browser setup
If you only need website screenshots, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server supports Claude, Cursor and other MCP clients through take_screenshot, get_page_info and capture_pdf.
See the ScreenshotNeo API documentation for all options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
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)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Every feature is on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account.
FAQ
Does headless Ubuntu need a desktop environment?
No. Playwright launches browsers in headless mode, so a graphical desktop session is not required.
Should I use Ubuntu 22.04, 24.04 or 26.04?
Choose a release supported by your provider and project runtime. Playwright currently lists all three, but recheck its requirements before deployment.
Can I run automation as root?
Use a dedicated non-root account for routine jobs. Reserve sudo for administration and package maintenance.
When is a managed screenshot API a better fit?
Use one when you need screenshots without maintaining browser binaries, Linux dependencies, servers, patching, concurrency and artifact storage. ScreenshotNeo also provides consent cleanup, billing verdict headers and an MCP server for AI agents.


