Back to Blog
Tutorial10 min read2026-07-07

How to Deploy Windmill for Internal Scripts and Workflows

Windmill turns scripts in Python, TypeScript, Go, and Bash into APIs, schedules, and UIs. Here's how to self-host its server plus worker architecture on top of Postgres.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Windmill is an open-source developer platform for turning scripts into production-grade workflows, schedules, and internal UIs. If your team writes a lot of glue scripts and ops automation, Windmill gives them a runtime, a permission model, secrets management, and a UI without building all that yourself. It's written in Rust and is notably resource-efficient compared to JVM-based alternatives.

Architecture: server, workers, Postgres

Windmill has three moving parts:

ComponentRole
ServerAPI + web UI, schedules jobs
WorkersExecute scripts/flows, pull from the job queue
PostgreSQLThe entire state + the job queue itself

The clever bit: Windmill uses Postgres as its job queue (via FOR UPDATE SKIP LOCKED), so there's no separate Redis or RabbitMQ to operate. Everything lives in Postgres. That makes the dependency footprint small but means your database is on the critical path for job throughput.

Deploying

Windmill ships the ghcr.io/windmill-labs/windmill image and runs the same image in two roles, switched by environment variable. You deploy at least two services from the same image:

# Server role
MODE=server
DATABASE_URL=postgres://...
BASE_URL=https://windmill.yourdomain.com

# Worker role (separate service)
MODE=worker
DATABASE_URL=postgres://...   # same database

On PandaStack, attach a managed PostgreSQL once and feed the injected DATABASE_URL to both the server and worker services. The server exposes the UI on port 8000; the workers don't need to be publicly reachable.

Worker groups and tags

Windmill workers can be specialized into groups via tags — for example a group with more memory for heavy data jobs, or a group that has native dependencies preinstalled. You assign scripts to a tag and only matching workers pick them up. In a containerized world, this maps neatly onto running multiple worker services on different compute tiers:

  • A default worker group on a small tier for quick scripts
  • A heavy worker group on a memory-optimized tier for data crunching

Scale the number of worker replicas to your concurrency needs; each worker runs a fixed number of jobs in parallel.

Dependencies and caching

Windmill resolves script dependencies (pip, npm) on the fly and caches them. For faster cold starts on Python/TypeScript scripts, give workers a persistent cache volume so resolved dependencies survive restarts. Without it, the first run of each script after a deploy re-installs everything.

Secrets and resources

Don't hardcode credentials in scripts. Windmill has a first-class resources and variables system with per-workspace secrets, encrypted at rest. Reference them in scripts via the Windmill SDK rather than passing them as plain env vars. This keeps a database password out of a script's source and out of execution logs.

Self-hosting vs the cloud

Windmill offers a hosted cloud and an enterprise edition with extra features (distributed dependency cache, audit logs, etc.). The open-source community edition is fully capable for most internal-tooling use cases. Be honest about which features you need before assuming the OSS build is missing something — the docs clearly mark enterprise-only capabilities.

Go-live checklist

  • Managed Postgres attached to both server and worker
  • BASE_URL set to your custom domain (for webhooks/links)
  • At least one worker service running (scale replicas to concurrency)
  • Optional heavy worker group on a memory-optimized tier
  • Dependency cache volume on workers
  • Secrets stored as Windmill resources, not in scripts

References

  • [Windmill self-host docs](https://www.windmill.dev/docs/advanced/self_host)
  • [Windmill architecture](https://www.windmill.dev/docs/misc/architecture)
  • [Windmill worker groups](https://www.windmill.dev/docs/core_concepts/worker_groups)
  • [Windmill resources & variables](https://www.windmill.dev/docs/core_concepts/resources_and_types)

Windmill's split of one image into server and worker roles fits PandaStack's container model perfectly, and Postgres-as-queue means a single managed database powers the whole thing. Try it on the free tier at [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also