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:
| Component | Role |
|---|---|
| Server | API + web UI, schedules jobs |
| Workers | Execute scripts/flows, pull from the job queue |
| PostgreSQL | The 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 databaseOn 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
heavyworker 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_URLset 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).