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.