Why self-host Windmill on a VPS
Windmill runs continuously on your server and exposes each of your scripts as a signed REST API, a scheduled job or an auto-generated UI. Unlike a serverless function, there are no cold starts and no execution limits imposed by a third party. Your Python, TypeScript or Bash scripts access databases and internal services on your VPS directly, without going through a cloud gateway. The engine is written in Rust: it consumes less than 100 MB of RAM at idle and starts in seconds even on the smallest Cloud VPS.
Concrete self-hosting benefits
- No per-execution cost: thousands of jobs per day with no cloud bill.
- Direct access to internal VPS databases and services, without a tunnel.
- Secrets and API keys stored in Windmill and never leaving your server.
- Input forms and REST endpoints auto-generated for every script.
- Additional workers added on the fly to handle load spikes.
- Built-in Git versioning — every script change is traceable.
Technical requirements
Windmill requires at least 2 vCPU and 4 GB RAM to run the server, one default worker and one native worker against PostgreSQL 16. Allow 10 GB of disk for logs, worker cache and database volumes. Ubuntu 22.04 or 24.04 with Docker installed is sufficient. No GPU required. If you plan to run heavy Python workers (ML, pandas on large datasets), go for 4 vCPU and 8 GB RAM.
Deploy Windmill with Docker Compose
Clone the official repository
SSH into your VPS and pull the official docker-compose.yml:
git clone https://github.com/windmill-labs/windmill.git && cd windmillFor production, pin a stable version (check GitHub releases). The main branch is stable but may include patches under test.
Generate environment secrets
Copy .env.example to .env and fill in the variables:
cp .env.example .envFor DATABASE_URL, you can use the PostgreSQL included in the stack (--profiles pg up) or point to an existing instance. Set BASE_URL to https://your-domain.com — Windmill uses it to sign webhooks and build flow callback URLs.
Start the stack
Launch the server, the default worker and the native worker:
docker compose --profile pg up -dCheck that all four containers are Up with docker compose ps. The server listens on port 8000 on the loopback interface.
Configure nginx reverse proxy
Windmill exposes port 8000 on 127.0.0.1. Configure nginx to proxy and terminate TLS:
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}The Upgrade and Connection headers are required for WebSockets used by the real-time editor.
Log in and secure
Open https://your-domain.com, log in with [email protected] / changeme, then go to Settings → Users to set a strong password and optionally change the admin email. Leaving the editor open without authentication is the most common vulnerability on publicly exposed Windmill instances.
Deploy it in one click from the ServOrbit Marketplace. If you prefer to skip manual configuration, the ServOrbit Marketplace provisions Windmill automatically — server, two workers, PostgreSQL 16, nginx reverse proxy and a Let's Encrypt certificate — in a few minutes. The "First access" section of your client area shows the URL and generated credentials. Head to the [Windmill listing](/marketplace/automatisation/windmill) to get started.