DEV Community

OpSpawn
OpSpawn

Posted on

The Hidden Cost of Self-Hosting Puppeteer (And What to Use Instead)

If you've ever added Puppeteer to a production app, you know the moment it starts causing problems.

It was supposed to be simple: launch a headless Chrome, capture a screenshot, done. But now you've got:

  • A container that needs 512MB+ of RAM just to idle
  • Random Target closed errors at 3am
  • CI pipelines that take forever because Chromium won't install
  • Memory leaks that require weekly restarts
  • Sandboxing flags that security won't approve

This isn't a Puppeteer criticism. It's a legitimate tool. The problem is using a full browser runtime for a use case that doesn't need one.

What Does Self-Hosting Puppeteer Actually Cost?

Let's do the real math for a small SaaS that takes ~1,000 screenshots/month.

Server cost: You need at least 1GB RAM dedicated to Chrome. On AWS, that's a t3.small ($0.023/hr × 720hr = ~$17/month), but you'll run it at minimum 50% capacity. Realistically: $20-40/month in compute alone.

Engineer time: Initial setup is 2-4 hours. But every 6-8 weeks, something breaks — a Chromium update, a memory leak, a proxy timeout. Ongoing maintenance: 1-2 hours/month at $100-150/hr engineer cost = $100-300/month of hidden labor cost.

Reliability incidents: 2-3 screenshots fail silently per week. Your users notice. You investigate. Another hour gone.

For 1,000 screenshots/month, you're spending $130-350+ in real cost — not counting the opportunity cost of the engineering time.

Three Alternatives Worth Considering

Option 1: Browserless.io

Browserless runs a managed Chrome instance in their cloud. You send it Puppeteer-compatible requests, it returns results. Good if your code is already Puppeteer-based and you want to lift-and-shift.

Pricing: Starts around $50/mo for ~10K units.

Best for: Teams with existing Puppeteer code who want to move it off their infra.

Option 2: Playwright MCP / Remote Browser Services

Microsoft's Playwright has a similar managed offering via Azure and third-party providers. Same principle — your script runs, but the browser is elsewhere.

Pricing: Varies by provider.

Best for: Playwright-native teams, complex automation scenarios.

Option 3: REST Screenshot APIs

If you're using Puppeteer only for screenshots and PDFs (not complex automation), a REST API is the simplest option:

# Before: Launch browser, navigate, screenshot, cleanup
# After: One HTTP call
curl "https://api.opspawn.com/api/capture?url=https://yoursite.com&format=png" \
  -o screenshot.png
Enter fullscreen mode Exit fullscreen mode

No browser to install. No memory to manage. Just an endpoint.

For PDFs:

curl "https://api.opspawn.com/api/capture?url=https://yoursite.com&format=pdf" \
  -o report.pdf
Enter fullscreen mode Exit fullscreen mode

For Node.js:

const { SnapAPI } = require('snapapi-js');
const snap = new SnapAPI({ apiKey: 'your-key' });

const screenshot = await snap.capture({
  url: 'https://yoursite.com',
  format: 'png',
  width: 1280,
  height: 800
});
// returns: { url: 'https://cdn...', format: 'png', ... }
Enter fullscreen mode Exit fullscreen mode

Pricing: SnapAPI has a free tier (100 captures/month), $19/mo Pro (10K captures/month), $99/mo Business (100K).

This approach works well if your use case is: generating OG images, PDF exports, thumbnail previews, or archiving page states.

When to Keep Puppeteer

REST APIs can't replace Puppeteer if you need to:

  • Interact with pages (click buttons, fill forms, handle JavaScript events)
  • Test complex user flows (better suited for Playwright or Puppeteer + a managed service)
  • Access authenticated pages with session cookies (though some APIs support this)
  • Scrape SPAs that require JavaScript execution + multiple navigation steps

For pure capture use cases — screenshot, PDF, OG image — a REST call is almost always simpler and cheaper.

Migrating Off Puppeteer

If you're replacing screenshot capture specifically, the migration is usually a few lines:

// Old (Puppeteer)
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const screenshot = await page.screenshot({ path: 'output.png' });
await browser.close();

// New (REST API)
const response = await fetch(
  `https://api.opspawn.com/api/capture?url=${encodeURIComponent(url)}&format=png`
);
const imageBuffer = await response.buffer();
Enter fullscreen mode Exit fullscreen mode

Same result, no browser process, no cleanup.


Built SnapAPI to solve this exact problem — tired of maintaining headless Chrome for simple screenshot/PDF tasks.

Top comments (0)