Skip to main content
Self-hosted Skyvern runs the API server, the browser, and the database on your infrastructure, with your own LLM keys. Every setting is an environment variable; the full list is in configuration.

Pick a path

pip: local server

Requires Python 3.11 to 3.14.
skyvern quickstart asks for your LLM provider and key, installs Chromium, sets up the database, writes a local API key to .env, and can configure MCP for your coding agent. It then offers to start the server. Start it later with:
The API listens on http://localhost:8000. Database choice:
  • If PostgreSQL is already running locally, quickstart uses it.
  • Otherwise it offers to start a PostgreSQL container, which needs Docker.
  • --no-postgres skips that and uses SQLite at ~/.skyvern/data.db.
  • --database-string=postgresql+psycopg://user:pass@host:5432/dbname uses a database you already have.
pip install skyvern without the [server] extra is the client SDK only. A pip install runs the API server without the web UI. For the UI, use Docker Compose or a source checkout.

Docker Compose

Starts PostgreSQL, the API server with its browser, and the web UI.
1

Get the code and env files

2

Set your LLM key

In .env, enable one provider (see Set up an LLM provider).
3

Clear the UI's placeholder key

In skyvern-frontend/.env, set VITE_SKYVERN_API_KEY= (empty). The UI then reads the key the server generates. If you leave YOUR_API_KEY there, the UI sends it as is and every request fails.
4

Start

The first start runs migrations and creates your organization. Wait until postgres and skyvern show healthy.
5

Open the app

Go to http://localhost:8080. The API is on port 8000. Your API key is the cred value in .skyvern/credentials.toml.
Recordings, artifacts, HAR files, logs, and downloads are written to folders next to docker-compose.yml.
Leave DATABASE_STRING in docker-compose.yml. The value in .env.example points at localhost, which inside the container is the container itself. If an older .env has DATABASE_STRING=...@localhost..., delete that line.
If you have the CLI installed, skyvern quickstart --docker-compose from the repo folder creates the env files, asks for your LLM key, and runs docker compose up -d for you. To serve it on a network, put a reverse proxy in front with authentication, and set VITE_API_BASE_URL, VITE_WSS_BASE_URL, and VITE_ARTIFACT_API_BASE_URL in skyvern-frontend/.env to the public URLs. Don’t expose Skyvern to the internet without authentication.

Kubernetes

The repository has plain manifests in kubernetes-deployment/: a backend, a frontend, PostgreSQL, and a Traefik ingress. Fill in backend/backend-secrets.yaml (the same variables as .env) and frontend/frontend-secrets.yaml, then run ./k8s-deploy.sh. Its README and configuration cover the API key, volumes, TLS, and scaling. The manifests have no authentication, so keep the cluster off the public internet.

Set up an LLM provider

Enable one provider and point LLM_KEY at a model. Quickstart writes these for you.
.env
.env
LLM_KEY defaults to OPENAI_GPT5_5. SECONDARY_LLM_KEY sets a cheaper model for lighter calls. Azure OpenAI, Gemini, Vertex AI, Bedrock, OpenRouter, Groq, and others work the same way. The provider flags and every valid LLM_KEY are in configuration. Restart the server after any change.

Use a local model

Ollama:
.env
Set OLLAMA_SUPPORTS_VISION=true only for a model that reads images. Skyvern works from screenshots, so a vision model gives much better results. Any OpenAI-compatible endpoint (LiteLLM, vLLM, LM Studio):
.env
If Skyvern runs in Docker, localhost is the container. Use http://host.docker.internal:11434 (or your port) to reach a model on the host. Without LLM_KEY, Skyvern keeps calling the default OpenAI model.

Browser options

BROWSER_TYPE sets how Skyvern runs Chromium. skyvern init browser walks you through cdp-connect. From Docker, reach Chrome on the host at http://host.docker.internal:9222. Other settings, including viewport, locale, timezone, timeouts and CDP details, are in configuration. For reusable sessions and profiles, see Browsers. Each concurrent run needs its own browser, and browsers use a lot of memory. Size memory for the number of runs you expect at once, and add servers to scale.

Storage

Artifacts (recordings, screenshots, HAR files, logs) go to local disk by default. For several servers, or for durability, set SKYVERN_STORAGE_TYPE. Create the S3 or GCS buckets before you start. Azure containers are created on first use. Links in run responses are signed. Screenshot and recording links expire after 1 hour; on S3, other links expire after PRESIGNED_URL_EXPIRATION seconds (default 86400, 24 hours). Switching storage doesn’t move existing artifacts. Bucket setup, IAM policy and migration are in configuration.

URL validation

Skyvern doesn’t block requests to internal addresses by default, so it reaches an intranet portal or a service on your network without extra setup. Set ENABLE_URL_VALIDATION=true to refuse loopback, private-network and cloud-metadata addresses instead. What it blocks, and how to allow individual hosts, is in configuration.
Whoever can start a task chooses the URLs Skyvern opens and fetches. If you share a server with people you don’t trust, turn URL validation on.

Google integrations

The Google Sheets, Gmail, and Google Drive integrations are off on a self-hosted server until you add a Google OAuth client.
1

Turn on encryption

Set ENABLE_ENCRYPTION=true and ENCRYPTOR_AES_SECRET_KEY (generate it with openssl rand -base64 32), then restart. Don’t change the key later: stored credentials become unreadable.
2

Create the OAuth client

In Google Cloud, enable the Google Sheets, Google Drive, and Gmail APIs. Configure the OAuth consent screen; for an External app, add each user as a test user (or publish the app) before they can connect. Then create an OAuth client ID of type Web application. Add https://<your-ui-host>/integrations/google/callback as an authorized redirect URI. For local use, that’s http://localhost:8080/integrations/google/callback.
3

Add it to Skyvern

In the app, open the Google OAuth form on Integrations or Settings. Fill in Client ID and Client Secret, set Redirect Hosts (host name only) and App Origins (for example https://skyvern.example.com), and click Save Configuration. Or set GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GOOGLE_OAUTH_REDIRECT_HOSTS, and GOOGLE_OAUTH_APP_ORIGINS on the server. The form wins over the variables.
Skyvern asks Google for the scopes each integration needs. If your consent screen restricts scopes, add these so users can grant them: gmail.send is a sensitive scope in Google Cloud. On the Gmail card, Read inbox is ticked by default and Send email is not; Google is asked only for what is ticked. Then click Connect on the integration card. If it fails, the redirect URI registered with Google must match Redirect Hosts, and App Origins must hold the exact origin the UI runs on.

Connect your code

Point the SDK at your server with the local API key.
The API answers on both /v1/ and /api/v1/.

Upgrade

Docker Compose: the server runs database migrations on start.
pip:
skyvern init runs the database migrations. Back up the database before upgrading. Release notes are in the changelog.

Next