Windmill on a VPS: Self-Hosted Workflow Automation and Script Platform (n8n Alternative)

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

  1. Visit https://windmill.yourdomain.com
  2. Default credentials: admin@windmill.dev / changeme
  3. Change password immediately
  4. 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)

  1. Schedules → New Schedule
  2. Select the script to run
  3. Set cron expression: */5 * * * * (every 5 minutes)
  4. Configure input arguments (URL, threshold)
  5. Set retry policy: 3 retries on failure, 60-second backoff
  6. 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)

  1. Resources → Add Resource → PostgreSQL
  2. Name: prod-database, Host/Port/DB/User/Password
  3. In scripts, reference it: pg = wmill.get_resource("f/prod-database")
  4. 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.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!