Windmill on a VPS: Self-Hosted Workflow Automation and Script Platform (n8n Alternative)
Windmill is an open-source developer platform for running scripts, building workflows, and creating internal tools — it combines a script editor (Python, TypeScript, Bash, Go, SQL), a workflow orchestrator (visual DAG editor), a job scheduler (cron), an approval flow system, and a form builder for internal apps. Unlike n8n (no-code focus), Windmill is built for developers who want to write real code while getting scheduling, UI, secrets management, and execution infrastructure for free.
Windmill vs n8n vs Temporal
- Windmill: Developer-first, real code (Python/TypeScript/Bash), workflow DAGs, UI builder, approval flows. Best for engineering teams.
- n8n: No-code/low-code visual flows, hundreds of built-in integrations, business user-friendly. Best for ops and marketing automation.
- Temporal: Production-grade workflow engine for complex long-running processes, requires significant setup. Best for engineering teams building core product workflows.
What Windmill Provides
- Script Hub: Write and share Python, TypeScript, Bash, Go, Deno, and SQL scripts
- Workflows: Connect scripts into DAG workflows with branching, loops, and error handling
- Schedules: Cron-based scheduling with per-job retry policies and alerting
- Apps: Build internal admin panels and dashboards backed by scripts
- Approval Flows: Require human approval before a step executes (e.g., approve a database delete)
- Variables and Secrets: Encrypted secret storage accessible from any script
- Resources: Connection configurations for databases, APIs, and services reused across scripts
Step 1: Docker Compose Setup
<code">mkdir -p /opt/windmill && cd /opt/windmill wget https://raw.githubusercontent.com/windmill-labs/windmill/main/docker-compose.yml wget https://raw.githubusercontent.com/windmill-labs/windmill/main/.env nano .env
<code"># Required changes: WM_BASE_URL=https://windmill.yourdomain.com POSTGRES_PASSWORD=StrongWindmillDbPassword! # Generate secret: openssl rand -hex 32 JWT_SECRET=your_jwt_secret_here
<code">chmod 600 .env docker compose up -d docker compose logs -f windmill-server
Step 2: Nginx Reverse Proxy
<code">sudo nano /etc/nginx/sites-available/windmill
<code">server {
listen 80;
server_name windmill.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name windmill.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/windmill.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/windmill.yourdomain.com/privkey.pem;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s;
}
}
<code">sudo ln -s /etc/nginx/sites-available/windmill /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx sudo certbot --nginx -d windmill.yourdomain.com
Step 3: First Login
- Visit
https://windmill.yourdomain.com - Default credentials:
admin@windmill.dev/changeme - Change password immediately
- Create a workspace (e.g., “Engineering”)
Step 4: Write Your First Script
Scripts → New Script → Python:
<code">import wmill # Windmill helper library
def main(
url: str,
threshold: float = 0.9,
) -> dict:
"""
Monitor a URL and alert if response time exceeds threshold.
Parameters appear as form fields when running manually.
"""
import httpx
import time
start = time.time()
response = httpx.get(url, timeout=10)
duration = time.time() - start
result = {
"url": url,
"status": response.status_code,
"duration_ms": round(duration * 1000, 2),
"slow": duration > threshold,
}
# Send Telegram alert if slow
if result["slow"]:
token = wmill.get_variable("f/alerts/telegram_token")
chat_id = wmill.get_variable("f/alerts/telegram_chat")
httpx.post(
f"https://api.telegram.org/bot{token}/sendMessage",
json={"chat_id": chat_id, "text": f"⚠️ {url} is slow: {duration:.2f}s"}
)
return result
Step 5: Schedule a Script (Cron Job)
- Schedules → New Schedule
- Select the script to run
- Set cron expression:
*/5 * * * *(every 5 minutes) - Configure input arguments (URL, threshold)
- Set retry policy: 3 retries on failure, 60-second backoff
- Set alert: send email/Slack if the schedule fails 3 consecutive times
Step 6: Build a Workflow
Workflows → New Flow (visual DAG editor):
<code"># Example: Daily data pipeline workflow # Step 1: Extract — fetch data from API # Step 2: Transform — clean and normalize (Python script) # Step 3: Approval — require engineer approval before loading to production # Step 4: Load — insert into PostgreSQL database # Step 5: Notify — send Slack summary # Branching: if Step 2 finds anomalies, go to "Alert" branch instead of "Load"
Step 7: Configure Resources (Database Connections)
- Resources → Add Resource → PostgreSQL
- Name:
prod-database, Host/Port/DB/User/Password - In scripts, reference it:
pg = wmill.get_resource("f/prod-database") - Same pattern for HTTP APIs, S3, Redis, MongoDB, etc.
Build Internal Apps
Windmill’s App builder creates browser-based tools backed by scripts:
- User lookup tool: Input user ID → runs SQL query → displays results in table
- Deploy tool: Select environment + version → triggers deployment script + approval flow
- Data correction tool: Form to correct database records, backed by audit-logged SQL scripts
Getting Started
Windmill needs 2–4 GB RAM for the server, workers, and PostgreSQL. A 4 GB Ubuntu VPS at VPS.DO runs Windmill comfortably with multiple concurrent workers for parallel job execution. The open-source Community Edition is fully featured — the paid cloud version adds SAML SSO and audit logs, neither required for most self-hosted deployments.
Conclusion
Windmill on a VPS provides a complete developer automation platform — cron scheduling, workflow orchestration, approval flows, internal app builder, and encrypted secrets management — built around writing real Python, TypeScript, and Bash code instead of visual blocks. For engineering teams automating data pipelines, internal operations workflows, scheduled maintenance tasks, and admin tooling, Windmill combines the power of custom code with the infrastructure of a managed platform at self-hosted cost.