# Turn on the optional services

> Page fetching, search, voice, drive import, and share links are server-provided on hosted. What each one needs on your instance.

- Canonical: https://big-agi.com/docs/self-host-optional-services
- Minimum tier: open  [Open ⊂ Free ⊂ Pro - a tier only ever ADDS to the Open baseline]
- Kind: how-to · Last content update generation: 2026-07-27 · Review by: 2027-01-23
- Answers: Why can't my instance fetch a web page I linked? · How do I enable web search on my own instance? · How do I get shareable chat links working? · What breaks if I skip all of this?

> **You run the Open branch.** Everything here applies to the open-source `main` branch on a machine you control: the same product the hosted site runs, with the pieces hosted operates for you left in your hands. The code lives on [GitHub](https://github.com/enricoros/big-AGI) - if Open serves you, star the repo.

Four capabilities depend on something outside the app; the rest of the product works on a bare install.

| Capability | What you supply | Where it shows up |
|---|---|---|
| [Page fetching](#page-fetching) | A remote browser endpoint | Pasting a URL, `/browse [URL]`, agent page loads |
| [Web search](#web-search) | A Google Custom Search key and engine id | The search step inside the agent |
| [Voice](#voice) | Nothing, or your OpenAI key | Spoken answers |
| [Drive import](#drive-import) | A Google client id, at build time | Google Drive in the attachment menu |

Variables named here are placed like any other: [Environment variables](/docs/self-host-configuration).

## Page fetching

Set `PUPPETEER_WSS_ENDPOINT` to a remote browser and the app can load a page and hand its text to the model. Three kinds of endpoint qualify:

| Endpoint | Licence | Where it runs | What it brings |
|---|---|---|---|
| Browserless | open source | a container beside yours | a debug viewer and a concurrency setting |
| A cloud scraping browser | proprietary | the vendor's infrastructure | a global IP pool, nothing to operate |
| Any Puppeteer-compatible WSS service | varies | either | whatever that service documents |

Big-AGI's side of the connection is the same for all three:

| What | The rule |
|---|---|
| endpoint | `ws://` or `wss://` only - anything else: `Invalid WSS browser endpoint` |
| timeout | 20 seconds per page, compiled in |
| downloads | disabled, whatever a request asks for |
| failures | per URL - the rest of the batch still returns |

The variable is the instance-wide fallback, so nobody has to set an endpoint of their own.

A Browserless container runs on its own, next to an instance you started some other way:

```bash
docker run -p 9222:3000 browserless/chrome:latest
```

The endpoint is then `ws://127.0.0.1:9222`. The same address over HTTP - `http://127.0.0.1:9222` - opens the Browserless debug viewer. That page confirms the container is up, and carries its own options.

The repository ships a Compose file that starts both containers together:

```yaml
services:
  big-agi:
    image: ghcr.io/enricoros/big-agi:latest
    ports:
      - "3000:3000"
    environment:
      - PUPPETEER_WSS_ENDPOINT=ws://browserless:3000
    depends_on:
      - browserless
  browserless:
    image: browserless/chrome:latest
    environment:
      - MAX_CONCURRENT_SESSIONS=10
```

`MAX_CONCURRENT_SESSIONS` caps how many pages the browser opens at once. Failures are per URL, so this is the number to raise when a large batch comes back short.

The container reaches sites itself, over your network rather than through Big-AGI. Where outbound traffic passes a corporate proxy, hand the container that proxy - without it every fetch fails. Give the `browserless` service an `env_file` entry pointing at `.env`, and put both variables in that file:

```
https_proxy=http://PROXY-IP:PROXY-PORT
http_proxy=http://PROXY-IP:PROXY-PORT
```

Standalone, the same pair as flags:

```bash
docker run --env https_proxy=http://PROXY-IP:PROXY-PORT --env http_proxy=http://PROXY-IP:PROXY-PORT -p 9222:3000 browserless/chrome:latest
```

Experimental and untested: your own Chrome as the endpoint, with nothing else to run. Close every Chrome instance - on Windows, confirm in Task Manager. Start Chrome with `--remote-debugging-port=9222`. Open `http://localhost:9222/json/version` and copy the `webSocketDebuggerUrl` value into the endpoint field.

This route is unauthenticated by default, which makes your server a URL fetcher for anyone who can reach it: [Keys and access control](/docs/self-host-configuration).

## Web search

`GOOGLE_CLOUD_API_KEY` and `GOOGLE_CSE_ID` together, or nothing happens - one without the other leaves the capability off. Creating the key and the search engine is [Google's own procedure](https://developers.google.com/custom-search/v1/overview).

This turns on the search step the agent runs, and domain-restricted search. The web search built into modern models is a separate thing: it comes from the AI service and needs nothing from you. Misconfigured, the request returns `Missing API Key or Custom Search Engine ID`; a rejection from Google returns `Google Custom Search API error:` followed by their message.

## Voice

Spoken answers work at three levels, two needing no setup:

- **The browser's own voice**, always available where the browser supports it.
- **OpenAI speech**, as soon as `OPENAI_API_KEY` is set, on the default OpenAI endpoint only - a client-chosen host never receives your key.
- **Speech engines from AI services you configured**, which become selectable with nothing else entered.

ElevenLabs is the exception: a per-person credential on the Open branch, whatever you set - [below](#fails-without-an-error).

## Drive import

`NEXT_PUBLIC_GOOGLE_DRIVE_CLIENT_ID` is compiled into the browser bundle. This is the one service a prebuilt image cannot have: set it on a Vercel or source build ([build time and run time](/docs/self-host-configuration#build-time-and-run-time)).

On Google's side: enable the Drive and Picker APIs, then create an OAuth **Web application** client. Add your exact origin to its authorised JavaScript origins and scope it to `drive.file` - [their picker documentation](https://developers.google.com/workspace/drive/picker/guides/overview).

The client needs a consent screen, under APIs & Services -> OAuth consent screen. Create it as External or Internal and give it the `https://www.googleapis.com/auth/drive.file` scope. A screen left in testing mode admits only the accounts listed under test users. Add your own account there - without it, a correct configuration still refuses you.

Three checks cover the rest. A picker that never opens means the client id is unset, or one of the two APIs is off. An OAuth error means the origin is missing from authorised JavaScript origins, or your account is missing from test users. A download that fails is the file's own sharing permissions, or the Drive API.

Files then download as they are, and Google's own formats convert: Docs to Markdown, Sheets to CSV, Slides to PDF, Drawings to SVG.

## What you lose by skipping all of it

- Pasting a URL attaches nothing; the browse command and agent page loads stop.
- The agent's search step has no backend. Models with their own web search are unaffected.
- Google Drive is absent from the attachment menu; every other attachment source works.
- ElevenLabs voices need each person's own key; browser voice and OpenAI speech still work.

## Fails without an error

| What happens | How to tell |
|---|---|
| `ELEVENLABS_API_KEY` is reported as configured; the speech path does not read it | Requests fail with `Missing ElevenLabs API key` unless that person entered their own |
| `NEXT_PUBLIC_GOOGLE_DRIVE_CLIENT_ID` handed to a prebuilt image does nothing | Drive stays out of the attachment menu; it compiles in at build |
| A person's own endpoint wins over yours, for that person | Theirs is in Preferences -> Tools -> Browse; your variable is only the fallback |

## Related

- [Environment variables](/docs/self-host-configuration)
- [Keys and access control](/docs/self-host-configuration)
- [Run it yourself](/docs/self-host)
- [Export and share your chats](/docs/account-backup)
- [What leaves the browser](/docs/privacy-data-flow)
