[Skyvern](https://github.com/skyvern-ai/skyvern) is a tool for autonomous web browsing - just the kind of thing you might want to deploy on Coolify.
However, as of December 2025, if you try to do this using the [official docker-compose](https://github.com/Skyvern-AI/skyvern/blob/main/docker-compose.yml) you're in for a bad time: Skyvern is optimized for local development (via `git clone` and bind mounts) and you'll run into the following problems trying to containerize it:
## Problems
When you try to deploy Skyvern to Coolify with the existing docker-compose, you'll encounter the following problems:
1. **Missing `secrets.toml`:** The UI container crashes on boot because it expects a local file from the git repo to be mounted.
2. **Wrong Database Driver:** The default `DATABASE_STRING` uses a synchronous driver (`psycoph2`) that fails with Skyvern's async engine.
3. **Docker Networking/Port Conflicts:** Coolify's internal reverse proxy conflicts with the default `ports:` definitions.
4. **Skyvern UI Service hosts on Localhost:** We can't access it from the web this way.
5. **No UI Access Control:** The Skyvern UI requires no authentication to access, and must be secured before being exposed to the web.
> Discovering and solving these problems was a deeply frustrating experience, taking an unreasonable amount of effort and all-day to get it working. In the end, it only works without the ability to stream the browser - e.g. the equivalent of headless.
>
> I don't presume that their intent is to make self-hosting difficult in order to sell the cloud hosted service - it's also a new product and maybe they're planning to improve these issues. Regardless, I am sharing what I solved here in the hope it gets someone else closer - if you don't need to stream the view from the browser, this will do what you need.
## Solution and Guide
Follow these steps to get Skyvern working on Coolify (sans ability to stream):
### A) Generate a HTTP Auth Hash
1. Open a new terminal and use this command to generate a hash of a username and password: `htpasswd -nb your-username your-password`
2. Take note of the result, which will look like `your-username:$apr1$PNUAc/iN$I4qSGXrNCLSheezFmtCV8.`
### B) Create The Service
1. Create a new Coolify service/resource with the `Docker Compose Empty` template.
2. Paste in the below docker-compose.
3. Replace `YOUR_KEY_HASH_HERE` with the exact output from the previous step.
```yaml
services:
db:
image: 'postgres:14.1'
container_name: skyvern-db
restart: always
environment:
POSTGRES_USER: skyvern
POSTGRES_PASSWORD: '${DB_PASSWORD:-skyvern}'
POSTGRES_DB: skyvern
volumes:
- 'skyvern-data:/var/lib/postgresql/data'
healthcheck:
test:
- CMD-SHELL
- 'pg_isready -U skyvern'
interval: 5s
timeout: 5s
retries: 5
skyvern:
image: 'skyvern/skyvern:latest'
container_name: skyvern-api
restart: always
shm_size: 2gb
depends_on:
db:
condition: service_healthy
volumes:
- 'skyvern-data:/app/.skyvern'
environment:
- 'DATABASE_STRING=postgresql+asyncpg://skyvern:${DB_PASSWORD:-skyvern}@db:5432/skyvern'
- BROWSER_TYPE=chromium-headful
- ENABLE_OPENAI=false
- ENABLE_OPENAI_COMPATIBLE=true
- LLM_KEY=OPENAI_COMPATIBLE
- 'OPENAI_COMPATIBLE_API_BASE=${LLM_API_BASE}'
- 'OPENAI_COMPATIBLE_API_KEY=${LLM_API_KEY}'
- 'OPENAI_COMPATIBLE_MODEL_NAME=${LLM_MODEL_NAME}'
extra_hosts:
- "None:127.0.0.1"
skyvern-ui:
image: 'skyvern/skyvern-ui:latest'
container_name: skyvern-ui
restart: always
depends_on:
- skyvern
user: root
volumes:
- 'skyvern-ui-data:/app/.streamlit'
environment:
- 'VITE_API_BASE_URL=${API_PUBLIC_URL}'
- 'VITE_WSS_BASE_URL=${WS_PUBLIC_URL}'
- 'VITE_SKYVERN_API_KEY=${API_KEY}'
command:
- /bin/bash
- '-c'
- "mkdir -p /app/.streamlit\nif [ -z \"${API_KEY}\" ]; then\n touch /app/.streamlit/secrets.toml\nelse\n echo \"cred = \\\"${API_KEY}\\\"\" > /app/.streamlit/secrets.toml\nfi\n/bin/bash /app/entrypoint-skyvernui.sh\n"
labels:
- traefik.http.middlewares.skyvern-auth.basicauth.users=YOUR_KEY_HASH_HERE
volumes:
skyvern-data: null
skyvern-ui-data: null
```
## C) Update Service URLS
1. Enable "Connect to Predefined Network"
2. Enter your intended URL for the Skyvern service, with ending `:8000`.
3. Turn off `Enable Gzip Compression` for the Skyvern service.
4. Enter your intended URL for the Skyvern UI service, with ending `:8080`.
![[skyvern_coolify_service_stack.png]]
### D) Update Environment Variables
1. Skip `API_KEY` for now (we do this later).
2. Fill in `API_PUBLIC_URL` with `https://your.domain.com/api/v1`.
3. Paste your hashed username and password from the previous step into `BASIC_AUTH_HASH` and check `Is Literal?`.
4. Set any random password in `DB_PASSWORD` (you won't need this later).
5. Enter the `LLM_API_BASE`, `LLM_API_KEY` and `LLM_MODEL_NAME` you'd like to use Skyvern with.
6. Fill in WS_PUBLIC_URL with `wss://your.domain.com/api/v1`.
![[skyvern_coolify_env_vars.png]]
### E) Deploy and Regenerate API Key
1. Deploy the service and wait for it to report `Running`.
2. Visit the Skyvern UI service URL and login with your username and password.
3. Press the `Regenerate API Key` button.
4. Visit Skyvern `Settings` and copy the newly generated `API_KEY`
5. Return to Coolify, and set the `API_KEY` env var to the newly generated key, and check `Is Literal`.
6. Save changes and restart the service.
![[skyvern_regenerate_api_key_dialog.png]]
## Voila!
Your Skyvern instance is up and running on Coolify - Enjoy!
> **Except, what I recommend now, is uninstalling Skyvern and installing something that doesn't fight so hard against containerization.**