> ## Documentation Index
> Fetch the complete documentation index at: https://skyvern.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Setup

> Deploy Skyvern using Docker Compose with the API server, embedded browser, PostgreSQL database, and web UI. Includes LLM provider configuration and first-run setup.

This guide walks you through deploying Skyvern using Docker Compose. By the end, you'll have a working Skyvern instance with the API server, browser, database, and web UI running on your machine.

## Prerequisites

* Docker and Docker Compose v2 installed ([Get Docker](https://docs.docker.com/get-docker/)). All commands use `docker compose` (with a space). If you have an older installation, replace with `docker-compose`.
* 4GB+ RAM available
* An LLM API key (OpenAI, Anthropic, Azure, Gemini, or Bedrock)

## Quick start

### 1. Clone the repository

```bash theme={null}
git clone https://github.com/Skyvern-AI/skyvern.git
cd skyvern
```

### 2. Configure environment variables

Copy the example environment file and configure your LLM provider:

```bash theme={null}
cp .env.example .env
```

Open `.env` and set your LLM provider. Here's an example for OpenAI:

```bash .env theme={null}
ENABLE_OPENAI=true
OPENAI_API_KEY=sk-your-api-key-here
LLM_KEY=OPENAI_GPT5_5
```

For other providers, see [LLM Configuration](/developers/self-hosted/llm-configuration).

### 3. Configure the frontend

Copy the frontend environment file:

```bash theme={null}
cp skyvern-frontend/.env.example skyvern-frontend/.env
```

The default values work for local development. Docker Compose mounts the generated local credentials into the UI container, so you can leave `VITE_SKYVERN_API_KEY` as `YOUR_API_KEY` for the first startup.

### 4. Start the services

```bash theme={null}
docker compose up -d
```

This pulls the images and starts three services:

* **postgres**: Database on port 5432 (internal only)
* **skyvern**: API server on port 8000
* **skyvern-ui**: Web interface on port 8080

First startup takes 1-2 minutes as it runs database migrations and creates your organization.

### 5. Get your API key

Wait for all services to be healthy before continuing:

```bash theme={null}
docker compose ps
```

All three services should show `healthy` in the STATUS column. The `skyvern` container runs database migrations and generates credentials on first startup. This takes 1-2 minutes.

Once healthy, retrieve your API key:

```bash theme={null}
cat .skyvern/credentials.toml
```

This file is auto-generated by Skyvern on first startup. The credentials inside are standard Skyvern API keys.

You'll see output like:

```toml theme={null}
[skyvern]
configs = [
    {env = "local", host = "http://skyvern:8000/api/v1", orgs = [{name="Skyvern", cred="eyJhbGciOiJIUzI1..."}]}
]
```

The `host` value uses the Docker-internal hostname `skyvern`. From your machine, use `http://localhost:8000` instead. You only need the `cred` value. This is your API key.

### 6. Verify the installation

Open [http://localhost:8080](http://localhost:8080) in your browser. You should see the Skyvern dashboard.

Test the API by listing workflows (should return an empty array on fresh install):

```bash theme={null}
curl -s http://localhost:8000/v1/workflows \
  -H "x-api-key: YOUR_API_KEY_HERE"
```

<Note>
  The API accepts requests on both `/v1/` and `/api/v1/`. The frontend uses `/api/v1` for backward compatibility. New integrations should use `/v1/`.
</Note>

***

## Understanding the services

The Docker Compose file defines three services that work together:

```mermaid theme={null}
flowchart LR
    UI[skyvern-ui<br/>:8080] --> API[skyvern<br/>:8000]
    API --> DB[(postgres<br/>:5432)]
    API --> Browser[Browser<br/>internal]
    API --> LLM[Your LLM<br/>Provider]
```

| Service      | Image                             | Ports           | Purpose                                               |
| ------------ | --------------------------------- | --------------- | ----------------------------------------------------- |
| `postgres`   | postgres:14-alpine                | 5432 (internal) | Stores tasks, workflows, credentials, and run history |
| `skyvern`    | public.ecr.aws/skyvern/skyvern    | 8000, 9222      | API server + embedded browser                         |
| `skyvern-ui` | public.ecr.aws/skyvern/skyvern-ui | 8080, 9090      | Web dashboard and artifact server                     |

The `skyvern` container includes Playwright with Chromium. The browser runs inside the same container as the API server. No separate browser service is needed.

### Data volumes

Docker Compose mounts several directories for persistent storage:

| Local path        | Container path             | Contents                         |
| ----------------- | -------------------------- | -------------------------------- |
| `./postgres-data` | `/var/lib/postgresql/data` | Database files                   |
| `./artifacts`     | `/data/artifacts`          | Extracted data, screenshots      |
| `./videos`        | `/data/videos`             | Browser session recordings       |
| `./har`           | `/data/har`                | HTTP Archive files for debugging |
| `./log`           | `/data/log`                | Application logs                 |
| `./.skyvern`      | `/app/.skyvern`            | Generated API credentials        |

***

## Environment variables reference

The `.env` file controls the Skyvern server. Here are the most important variables grouped by purpose.

### Environment files

`docker-compose.yml` uses TWO sources for service environment:

1. **Inline `environment:` block** — compose-network values that depend on the docker network topology. Examples: `DATABASE_STRING=postgresql+psycopg://skyvern:skyvern@postgres:5432/skyvern` (the `postgres` hostname only resolves inside the compose network), `BROWSER_TYPE`, `BROWSER_STREAMING_MODE`, `ENABLE_CODE_BLOCK`.

2. **`env_file: .env`** — user secrets (LLM API keys, etc.).

**Do not move `DATABASE_STRING` from compose into `.env`.** The `.env.example` shipped with the repo uses a `localhost` host that points at the backend container itself when used inside compose — moving that value into `.env` will break the database connection.

If you have an existing `.env` from a pre-1.0.32 `skyvern quickstart` run, it may contain a leftover `DATABASE_STRING=postgresql+psycopg://skyvern@localhost/skyvern` line. Remove it:

```bash theme={null}
sed -i.bak '/^DATABASE_STRING.*localhost/d' .env
```

### LLM Configuration

Configure at least one LLM provider. See [LLM Configuration](/developers/self-hosted/llm-configuration) for all providers.

```bash theme={null}
# OpenAI
ENABLE_OPENAI=true
OPENAI_API_KEY=sk-...
LLM_KEY=OPENAI_GPT5_5

# Or Anthropic
ENABLE_ANTHROPIC=true
ANTHROPIC_API_KEY=sk-ant-...
LLM_KEY=ANTHROPIC_CLAUDE4.7_OPUS
```

### Browser settings

```bash theme={null}
# Browser mode: chromium-headful (visible) or chromium-headless (no display)
BROWSER_TYPE=chromium-headful

# Browser livestreaming mode: cdp (default local screencast) or vnc
BROWSER_STREAMING_MODE=cdp

# Timeout for individual browser actions (milliseconds)
BROWSER_ACTION_TIMEOUT_MS=5000

# Where to save recordings
VIDEO_PATH=./videos
```

### Task execution

```bash theme={null}
# Maximum steps before a task times out
MAX_STEPS_PER_RUN=10

# Server port
PORT=8000

# Log verbosity: DEBUG, INFO, WARNING, ERROR
LOG_LEVEL=INFO
```

### Database

The database connection is set in `docker-compose.yml`, not `.env`:

```yaml theme={null}
environment:
  - DATABASE_STRING=postgresql+psycopg://skyvern:skyvern@postgres:5432/skyvern
```

To use an external database, update this connection string and remove the `postgres` service.

***

## Common operations

### View logs

```bash theme={null}
# All services
docker compose logs -f

# Specific service
docker compose logs -f skyvern
```

### Restart after configuration changes

```bash theme={null}
docker compose restart skyvern
```

### Stop all services

```bash theme={null}
docker compose down
```

### Update to latest version

```bash theme={null}
docker compose pull
docker compose up -d
```

### Reset everything (including database)

<Warning>
  This deletes all data including task history, credentials, and recordings.
</Warning>

```bash theme={null}
docker compose down -v
rm -rf postgres-data artifacts videos har log .skyvern
docker compose up -d
```

***

## Exposing to the network

By default, Skyvern only accepts connections from localhost. To expose it on your network:

### Option 1: Bind to all interfaces

Edit `docker-compose.yml` to change the port bindings:

```yaml theme={null}
services:
  skyvern:
    ports:
      - "0.0.0.0:8000:8000"  # Accept connections from any IP
```

### Option 2: Use a reverse proxy

For production deployments, put Skyvern behind nginx or Traefik:

```
your-domain.com → nginx → localhost:8080 (UI)
api.your-domain.com → nginx → localhost:8000 (API)
```

Update the frontend environment to use your domain:

```bash skyvern-frontend/.env theme={null}
VITE_API_BASE_URL=https://api.your-domain.com/api/v1
VITE_WSS_BASE_URL=wss://api.your-domain.com/api/v1
VITE_ARTIFACT_API_BASE_URL=https://artifacts.your-domain.com
```

<Warning>
  If exposing Skyvern to the internet, add authentication at the reverse proxy layer or use a VPN.
</Warning>

***

## Troubleshooting

<Accordion title="Container exits immediately">
  Check the logs for the failing service:

  ```bash theme={null}
  docker compose logs skyvern
  ```

  Common causes:

  * Missing or invalid LLM API key: look for LLM-related errors in logs
  * Database connection failed: check if `postgres` service is healthy with `docker compose ps`
</Accordion>

<Accordion title="API returns &#x22;Invalid credentials&#x22; (403)">
  The API key is missing, malformed, or doesn't match the organization. Verify:

  1. The `x-api-key` header is included in your request
  2. The key matches exactly what's in `.skyvern/credentials.toml`
  3. No extra whitespace or newlines in the key

  ```bash theme={null}
  # Correct format
  curl -H "x-api-key: eyJhbGciOiJIUzI1..." http://localhost:8000/v1/workflows
  ```
</Accordion>

<Accordion title="API returns &#x22;Could not validate credentials&#x22; (403)">
  The API key format is invalid (JWT decode failed). This usually means:

  * The key was truncated or corrupted during copy-paste
  * You're using an API key from a different Skyvern installation

  Regenerate credentials by resetting the installation:

  ```bash theme={null}
  rm -rf .skyvern
  docker compose restart skyvern
  cat .skyvern/credentials.toml  # Get new key
  ```
</Accordion>

<Accordion title="UI shows blank page or connection errors">
  Check that `skyvern-frontend/.env` has the correct values:

  ```bash theme={null}
  VITE_API_BASE_URL=http://localhost:8000/api/v1
  VITE_WSS_BASE_URL=ws://localhost:8000/api/v1
  VITE_SKYVERN_API_KEY=<your-key-from-credentials.toml>
  ```

  After updating, restart the UI:

  ```bash theme={null}
  docker compose restart skyvern-ui
  ```
</Accordion>

<Accordion title="Task fails with &#x22;Unknown browser type&#x22;">
  The `BROWSER_TYPE` environment variable has an invalid value. Valid options:

  * `chromium-headful`: Browser with visible window (default)
  * `chromium-headless`: No visible window
  * `cdp-connect`: Connect to external Chrome
</Accordion>

<Accordion title="Task fails with &#x22;Context window exceeded&#x22;">
  The page content is too large for the LLM. Try:

  * Simplifying your prompt
  * Starting from a more specific URL
  * Using a model with a larger context window
</Accordion>

***

## Next steps

<CardGroup cols={2}>
  <Card title="LLM Configuration" icon="microchip" href="/developers/self-hosted/llm-configuration">
    Configure OpenAI, Anthropic, Azure, Ollama, and other providers
  </Card>

  <Card title="Browser Configuration" icon="window" href="/developers/self-hosted/browser">
    Customize browser settings, locales, and connect to external Chrome
  </Card>

  <Card title="Storage Configuration" icon="hard-drive" href="/developers/self-hosted/storage">
    Store artifacts in S3 or Azure Blob instead of local disk
  </Card>

  <Card title="Proxy Setup" icon="shield" href="/developers/self-hosted/proxy">
    Configure proxies to avoid bot detection
  </Card>
</CardGroup>
