Windmill is an open-source developer platform that turns scripts into webhooks, workflows, cron jobs and internal UIs - positioned against Retool and Temporal rather than against a static-site host. It is licensed AGPL-3.0 for the community edition, with an enterprise build behind a licence key.
Sizing it is unlike sizing a typical web app, because the unit of capacity is the worker, not the application.
The Architecture That Decides Your Specs
The official docker-compose.yml brings up seven services:
| Service | Image | Role |
|---|---|---|
db | postgres:16 | Database and job queue |
windmill_server | ghcr.io/windmill-labs/windmill:main | API and web UI, port 8000 |
windmill_worker | same image, MODE=worker | Executes jobs |
windmill_worker_native | same image, NATIVE_MODE=true | Lightweight jobs only |
windmill_indexer | same image, MODE=indexer | Full-text job search |
windmill_extra | ghcr.io/windmill-labs/windmill-extra:latest | Language servers for the editor |
caddy | ghcr.io/windmill-labs/caddy-l4:2.11.4-1 | Reverse proxy on port 80 |
Two things in that table matter more than the rest.
There is no Redis. Windmill uses PostgreSQL as its queue. Plenty of third-party guides add a Redis container and a REDIS_URL; the official compose file has neither, and copying such a guide leaves you running a service Windmill never talks to.
The workers are the cost centre. The server is comparatively idle. Everything you actually pay for is worker capacity.
RAM and CPU: Size by Worker
The practical rule is one worker per vCPU, 1-2 GB of RAM each, plus overhead for Postgres and the server.
| Setup | vCPU / RAM | What it gives you |
|---|---|---|
| Trial, one worker | 2 / 2 GB | Works, but one job at a time and no headroom for a heavy dependency install |
| Default stack, 1-2 workers | 2-4 / 4 GB | The practical minimum for real use |
| Several concurrent jobs | 4 / 8 GB | The comfortable target for a small team |
Native workers are the exception worth knowing: they run lightweight jobs at roughly 0.1 CPU and 128 MB, which is why the default stack can ship one alongside a standard worker without demanding another core.
The variable nobody predicts is dependency installation. A Python job that pulls pandas or a TypeScript job with a large lockfile spikes memory well above the steady-state figure. If jobs fail only on first run and then succeed, you are watching the dependency cache being built on too little RAM.
Storage: The Dependency Cache Is the Growth
The database stays modest for a long time - job metadata, scripts, flows and logs are text. The volume that grows is worker_dependency_cache, where every Python wheel and npm package your jobs import is kept so later runs skip the install.
Start at 40 GB. Watch the cache volume rather than the database when deciding whether to resize, and prefer NVMe or SSD, since Postgres is doing queue duty as well as storage and is sensitive to I/O latency.
VPS Plans That Match These Requirements
| Provider | Plan | vCPU / RAM / Disk | Price | Verdict | Link |
|---|---|---|---|---|---|
| Contabo | Cloud VPS 4 | 4 / 8 GB / 100 GB SSD | 5.50 EUR/mo net | Four workersโ worth of headroom at the price of an entry plan | Contabo |
| Hetzner Cloud | CX23 | 2 / 4 GB / 40 GB NVMe | 5.49 EUR/mo net | Fine for the default two-worker stack | Hetzner |
| DigitalOcean | Basic 4 GB | 2 / 4 GB / 80 GB SSD | 24 USD/mo | Same specs as CX23, four times the price | DigitalOcean |
| Vultr | Regular 4 GB | 2 / 4 GB / 80 GB SSD | 20 USD/mo | Widest region choice | Vultr |
| Linode | Linode 4 GB | 2 / 4 GB / 80 GB SSD | 24 USD/mo | Predictable performance, strong API | Linode |
Windmill is one of the few self-hosted apps where a 1 GB plan is not automatically disqualified - a single native worker genuinely fits. But you cannot run a standard worker there, which means no Python or TypeScript jobs with dependencies, so it is a demo rather than a deployment.
Scaling Up
Windmill scales horizontally by adding worker containers rather than by making one process bigger. To double throughput, add a second windmill_worker service pointed at the same DATABASE_URL and give the box another core.
That has a practical consequence for VPS choice: more cores beat faster cores here. Contaboโs 4 vCPU at 5.50 EUR net buys four workersโ worth of parallelism, where Hetznerโs CX23 buys two at essentially the same price. If your workload is many small jobs rather than a few heavy ones, that ratio is the whole decision.
- Monitor with
docker statsto see which worker is saturated before resizing. - Group workers into tags so heavy jobs land on the machine sized for them.
- Postgres carries the queue, so give it fast disk before giving it more RAM.
For a wider look at plans across providers, see our full VPS comparison.
Frequently asked questions
How much RAM does a self-hosted Windmill instance need?
Size by worker rather than by app. The rule of thumb is one worker per vCPU with 1-2 GB of RAM each, on top of Postgres and the server process. A single-worker instance runs on 2 GB, the default compose stack with two workers is comfortable on 4 GB, and 8 GB with 4 vCPU is the sensible target once you run several jobs concurrently.
How many CPU cores should I allocate to Windmill?
Start with 2 vCPU and add cores as you add workers, since each standard worker executes one job at a time and is billed its own core in practice. Native workers are the exception: they handle lightweight jobs at roughly 0.1 CPU and 128 MB, so the default stack ships one alongside the standard worker without needing an extra core.
Does Windmill need Redis or any other message broker?
No. Windmill uses PostgreSQL as both its database and its job queue, which is unusual and worth knowing because many third-party guides add a Redis service that the official compose file does not contain. PostgreSQL 14 or newer is the only datastore required, and adding Redis gains you nothing but a wasted container.