Skip to main content

Overview

Agent Browser can run in serverless environments by using lightweight Chromium builds and the programmatic API. The bundled Chromium (~684MB) is too large for most serverless platforms, so you’ll need to use optimized alternatives like @sparticuz/chromium (~50MB).

Quick Start

For serverless deployment, use the BrowserManager class directly and provide a custom executablePath:
import chromium from '@sparticuz/chromium';
import { BrowserManager } from 'agent-browser';

export async function handler() {
  const browser = new BrowserManager();
  await browser.launch({
    executablePath: await chromium.executablePath(),
    headless: true,
  });
  
  // Use browser API
  await browser.navigate('https://example.com');
  const { tree, refs } = await browser.getSnapshot();
  
  await browser.close();
  
  return { statusCode: 200, body: tree };
}

Platform-Specific Guides

Vercel

Deploy as a Vercel Function with @sparticuz/chromium. Installation:
npm install agent-browser @sparticuz/chromium
Example API Route (api/screenshot.ts):
import type { VercelRequest, VercelResponse } from '@vercel/node';
import chromium from '@sparticuz/chromium';
import { BrowserManager } from 'agent-browser';

export default async function handler(
  req: VercelRequest,
  res: VercelResponse
) {
  const { url } = req.query;
  
  if (!url || typeof url !== 'string') {
    return res.status(400).json({ error: 'Missing url parameter' });
  }
  
  const browser = new BrowserManager();
  
  try {
    await browser.launch({
      executablePath: await chromium.executablePath(),
      headless: true,
    });
    
    await browser.navigate(url);
    const page = browser.getPage();
    const screenshot = await page.screenshot({ type: 'png' });
    
    res.setHeader('Content-Type', 'image/png');
    res.send(screenshot);
  } catch (error) {
    console.error('Screenshot failed:', error);
    res.status(500).json({ error: 'Screenshot failed' });
  } finally {
    if (browser.isLaunched()) {
      await browser.close();
    }
  }
}
vercel.json configuration:
{
  "functions": {
    "api/**/*.ts": {
      "memory": 3008,
      "maxDuration": 30
    }
  }
}
Notes:
  • Increase memory to at least 3008 MB for Chromium
  • Increase maxDuration if operations take longer than 10s
  • Cold starts can take 5-10 seconds

AWS Lambda

Deploy using Lambda Functions with @sparticuz/chromium. Installation:
npm install agent-browser @sparticuz/chromium
Lambda Handler (index.ts):
import chromium from '@sparticuz/chromium';
import { BrowserManager } from 'agent-browser';
import type { APIGatewayProxyHandler } from 'aws-lambda';

export const handler: APIGatewayProxyHandler = async (event) => {
  const url = event.queryStringParameters?.url;
  
  if (!url) {
    return {
      statusCode: 400,
      body: JSON.stringify({ error: 'Missing url parameter' }),
    };
  }
  
  const browser = new BrowserManager();
  
  try {
    await browser.launch({
      executablePath: await chromium.executablePath(),
      headless: true,
      args: chromium.args,
    });
    
    await browser.navigate(url);
    const { tree, refs } = await browser.getSnapshot();
    
    return {
      statusCode: 200,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ snapshot: tree, refs }),
    };
  } catch (error) {
    console.error('Browser operation failed:', error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Browser operation failed' }),
    };
  } finally {
    if (browser.isLaunched()) {
      await browser.close();
    }
  }
};
Configuration (serverless.yml or SAM template):
functions:
  browserAutomation:
    handler: index.handler
    timeout: 30
    memorySize: 3008
    environment:
      NODE_ENV: production
Notes:
  • Set memory to at least 3008 MB
  • Set timeout to at least 30 seconds
  • Lambda’s /tmp directory has 512 MB limit; Chromium uses it for downloads and caching
  • @sparticuz/chromium is optimized for Lambda and includes necessary shared libraries

AWS Lambda with Docker

For more control, use a Lambda container image: Dockerfile:
FROM public.ecr.aws/lambda/nodejs:18

# Install Chromium dependencies
RUN yum install -y \
  atk \
  cups-libs \
  gtk3 \
  libXcomposite \
  libXcursor \
  libXdamage \
  libXext \
  libXi \
  libXrandr \
  libXScrnSaver \
  libXtst \
  pango \
  alsa-lib \
  && yum clean all

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --production

# Install Chromium
RUN npx agent-browser install

# Copy application code
COPY index.ts ./

CMD ["index.handler"]
Build and deploy:
docker build -t agent-browser-lambda .
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com
docker tag agent-browser-lambda:latest <account-id>.dkr.ecr.us-east-1.amazonaws.com/agent-browser-lambda:latest
docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/agent-browser-lambda:latest

Google Cloud Functions

Installation:
npm install agent-browser @sparticuz/chromium
Function Handler (index.ts):
import type { HttpFunction } from '@google-cloud/functions-framework';
import chromium from '@sparticuz/chromium';
import { BrowserManager } from 'agent-browser';

export const browserAutomation: HttpFunction = async (req, res) => {
  const { url } = req.query;
  
  if (!url || typeof url !== 'string') {
    res.status(400).send({ error: 'Missing url parameter' });
    return;
  }
  
  const browser = new BrowserManager();
  
  try {
    await browser.launch({
      executablePath: await chromium.executablePath(),
      headless: true,
    });
    
    await browser.navigate(url);
    const page = browser.getPage();
    const title = await page.title();
    
    res.status(200).json({ url, title });
  } catch (error) {
    console.error('Browser operation failed:', error);
    res.status(500).send({ error: 'Browser operation failed' });
  } finally {
    if (browser.isLaunched()) {
      await browser.close();
    }
  }
};
Deploy:
gcloud functions deploy browserAutomation \
  --runtime nodejs18 \
  --trigger-http \
  --allow-unauthenticated \
  --memory 2048MB \
  --timeout 60s

Testing Serverless Setup Locally

Before deploying, test your serverless setup locally to verify @sparticuz/chromium integration:
// test/serverless.test.ts
import { describe, it, expect, afterAll } from 'vitest';
import { BrowserManager } from '../src/browser.js';
import * as os from 'os';

const isLinux = os.platform() === 'linux';

// @sparticuz/chromium only works on Linux
const canRunTest = await (async () => {
  if (!isLinux) return false;
  try {
    await import('@sparticuz/chromium');
    return true;
  } catch {
    return false;
  }
})();

describe.skipIf(!canRunTest)('Serverless Chromium Integration', () => {
  let browser: BrowserManager;
  let chromiumPath: string;

  it('should get executable path from @sparticuz/chromium', async () => {
    const chromium = await import('@sparticuz/chromium');
    chromiumPath = await chromium.default.executablePath();
    expect(chromiumPath).toBeTruthy();
  });

  it('should launch browser with custom executablePath', async () => {
    const chromium = await import('@sparticuz/chromium');
    chromiumPath = await chromium.default.executablePath();

    browser = new BrowserManager();
    await browser.launch({
      headless: true,
      executablePath: chromiumPath,
    });

    expect(browser.isLaunched()).toBe(true);
  });

  it('should navigate and get page title', async () => {
    const page = browser.getPage();
    await page.goto('https://example.com');
    const title = await page.title();
    expect(title).toBe('Example Domain');
  });

  it('should take snapshot with refs', async () => {
    const { tree, refs } = await browser.getSnapshot();
    expect(tree).toContain('Example Domain');
    expect(Object.keys(refs).length).toBeGreaterThan(0);
  });

  afterAll(async () => {
    if (browser?.isLaunched()) {
      await browser.close();
    }
  });
});
See test/serverless.test.ts in the source code for a complete example.

Performance Optimization

Reduce Cold Start Time

  1. Use deployment packages: Pre-bundle dependencies to reduce cold start
  2. Minimize dependencies: Only include what you need
  3. Use provisioned concurrency (AWS Lambda) for zero cold starts
  4. Keep functions warm with periodic invocations

Memory and CPU Allocation

Chromium requires significant resources:
  • Minimum: 1024 MB memory (will be slow)
  • Recommended: 2048-3008 MB memory for better performance
  • CPU: Scales with memory on most platforms

Reuse Browser Instances

Warning: Reusing browser instances across invocations can cause memory leaks and stale state. Safe approach:
let browser: BrowserManager | null = null;

export async function handler(event: any) {
  try {
    // Launch fresh browser for each invocation
    browser = new BrowserManager();
    await browser.launch({
      executablePath: await chromium.executablePath(),
      headless: true,
    });
    
    // Use browser
    await browser.navigate(event.url);
    const result = await browser.getSnapshot();
    
    return { statusCode: 200, body: JSON.stringify(result) };
  } finally {
    // Always close browser
    if (browser?.isLaunched()) {
      await browser.close();
    }
    browser = null;
  }
}

Alternatives to @sparticuz/chromium

chrome-aws-lambda

Older alternative, less maintained:
import chromium from 'chrome-aws-lambda';
import { BrowserManager } from 'agent-browser';

const browser = new BrowserManager();
await browser.launch({
  executablePath: await chromium.executablePath,
  args: chromium.args,
  headless: chromium.headless,
});

System Chrome/Chromium

If your serverless environment has Chrome/Chromium pre-installed:
import { BrowserManager } from 'agent-browser';

const browser = new BrowserManager();
await browser.launch({
  executablePath: '/usr/bin/chromium-browser',
  headless: true,
});

Cloud Browser Services

For production deployments, consider cloud browser services instead of running Chromium in serverless functions:

Browserbase

export BROWSERBASE_API_KEY="your-api-key"
export BROWSERBASE_PROJECT_ID="your-project-id"
export AGENT_BROWSER_PROVIDER=browserbase

agent-browser open https://example.com
See Browserbase integration for details.

Browser Use

export BROWSER_USE_API_KEY="your-api-key"
export AGENT_BROWSER_PROVIDER=browseruse

agent-browser open https://example.com
See Browser Use integration for details.

Kernel

export KERNEL_API_KEY="your-api-key"
export AGENT_BROWSER_PROVIDER=kernel

agent-browser open https://example.com
See Kernel integration for details.

Environment Variables

Key environment variables for serverless deployment:
VariableDescription
AGENT_BROWSER_EXECUTABLE_PATHPath to Chromium binary
AGENT_BROWSER_DEFAULT_TIMEOUTPlaywright timeout in ms (default: 25000)
AGENT_BROWSER_PROVIDERCloud browser provider (browserbase, browseruse, kernel)
NODE_ENVSet to production for optimizations

Troubleshooting

”Failed to launch browser” errors

Ensure:
  1. Memory is sufficient (at least 1024 MB, recommended 2048+ MB)
  2. Timeout is adequate (at least 30 seconds)
  3. executablePath is correct:
    const path = await chromium.executablePath();
    console.log('Chromium path:', path);
    

Missing shared libraries

@sparticuz/chromium includes necessary libraries for Lambda. For other platforms, install system dependencies:
# Debian/Ubuntu
apt-get install -y \
  libnss3 libxss1 libasound2 libatk-bridge2.0-0 \
  libgtk-3-0 libgbm1

# Alpine
apk add --no-cache \
  chromium nss freetype harfbuzz ca-certificates ttf-freefont

Timeout on first invocation

Cold starts can take 5-10 seconds. Increase function timeout to at least 30 seconds.

Out of memory errors

Increase function memory allocation or use cloud browser services instead. See Troubleshooting for more common issues.

Build docs developers (and LLMs) love