Windmill:把脚本秒变内部工具与工作流的代码优先自动化平台|部署与使用全指南
适用版本段:本文基于 Windmill 最新 Release v1.849.0(发布于 2026-10-25)与 main 分支当日文档撰写。仓库
windmill-labs/windmill约 18.3k star、1.1k fork,main 分支 2026-10-26 仍有提交,属于高活跃度项目。许可:核心 AGPLv3 + 客户端与 OpenAPI 规范 Apache 2.0(官方 Docker 镜像里的 Community Edition 还包含专有的非开源特性,详见第十一章)。

Windmill 是一个开源、可自托管的代码优先(code-first)自动化平台。你把一段普通的 Python / TypeScript / Go / Bash / SQL / PHP / Rust 脚本贴进去,它自动解析函数签名生成:一个 Web 表单 UI、一个 HTTP 端点、一个 CLI 命令、一个可定时触发的 cron 任务。脚本之间可以编排成 flow(工作流),flow 之上还能搭 App。它是 Retool / n8n / Airflow / Temporal 的开源替代方案,官方自称"最快的自托管工作流引擎"。

一、项目介绍与适用场景
1.1 它是什么
Windmill 由三部分组成(官方自托管文档):
一个 Postgres 数据库 —— 保存全部状态,包括任务队列;
运行在 server 模式 的 Windmill 容器 —— 同时提供前端与 API,对外暴露;
运行在 worker 模式 的 Windmill 容器 —— 从数据库队列里拉任务执行,可横向扩展。
另有三个可选组件:Windmill LSP(Monaco 编辑器智能提示)、Multiplayer(实时协作,企业版)、反向代理(官方 compose 用 Caddy,把 server / LSP / multiplayer 收敛到一个端口)。
1.2 技术栈(README "Stack" 一节)
1.3 适用人群
1.4 不适合的场景
不会写任何代码、只想拖拽:Windmill 有可视化 flow 编辑器和低代码 App 编辑器,但核心范式是"写代码",纯无代码团队上手成本高。
需要毫秒级延迟的交易链路:官方给出的额外开销约 50ms(入队 + 启动 + 回写),轻量 Deno 任务约 100ms 总耗时,不适合做在线请求的关键路径。
要求"改代码随意提交"的开源协作:CONTRIBUTING.md 明确写明 "At this time, we are not seeking outside contribution",只接受小到十几行的修复 PR。
想把 Windmill 包装成自己的产品对外售卖:需要商业授权(见第十一章)。
二、核心功能逐条拆解
脚本即全栈产物:写一个带类型标注的
main(),自动得到自动生成的 UI 表单(union 类型变下拉、默认值推断类型)、同步/异步 HTTP 端点、CLI 命令、可分享的公开路径。多语言运行时:Python、TypeScript(Bun/Deno)、Go、Bash、SQL、GraphQL、PowerShell、Rust、C#、Java、Ansible、Nu、Ruby、DuckDB、dbt 等 20+ 语言。
依赖自动解析与锁文件:Python 用 uv 解析并生成 lockfile;TypeScript 直接从 npm 解析并 pin 版本;无需自己维护 requirements.txt。
Flow(工作流):可视化 flow 编辑器、YAML,或直接用
windmill-client的workflow()/task()写"代码即工作流";每个 step 是独立 job,有独立日志;支持waitForApproval人工审批(等待期间释放 worker)。触发器:schedule(cron)、webhook、HTTP route、Kafka、WebSocket、email(SMTP,Caddy 把 25 端口转到 server 的 2525)、Postgres、NATS、SQS、GCP Pub/Sub、MQTT 等。
Resources 与 Resource Types:资源就是"带类型的 JSON 凭证对象",200+ 官方 resource type(MySQL、MongoDB、OpenAI…),SDK 里按类型自动筛选,避免硬编码密码。
Variables 与 Secrets:所有变量(不只是 secret)都用 workspace 级对称密钥加密;secret 读取会写审计事件
variables.decrypt_secret。Worker 与 Worker Group:worker 按 tag 拉任务;默认 group 为
default与native;可建highmem、gpu、reports等组并配 init script 预装二进制。Git 同步与本地开发:
wmill sync pull/push双向同步;VS Code / Cursor 扩展;Git sync 让 workspace 自动提交到 Git 仓库并从仓库部署。AI 能力:AI sessions(浏览器里用 AI 生成脚本/flow/app,带 diff review)、MCP server(让编码 agent 操作 workspace)、保护规则集(禁止直接部署,必须经工程师 review)。
数据能力:Data tables(workspace 内置 Postgres 表)、DuckDB(直接查 S3 / Parquet / BigQuery)、DuckLake(lakehouse)、Pipelines(按数据依赖驱动,alpha)。
可观测性与治理:Runs 页面、审计日志、OpenTelemetry、Prometheus 指标、
/api/health/status与/api/health/detailed健康检查端点、保留期策略。

三、全套官方链接
四、硬件与环境前置要求
五、多平台部署步骤
5.1 Linux(官方 Docker Compose,推荐)
# 1. 确保 Docker 已启动
sudo systemctl start docker
# 2. 拉取官方三个文件
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/docker-compose.yml -o docker-compose.yml
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/Caddyfile -o Caddyfile
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/.env -o .env
# 3. 启动
docker compose up -d
# 4. 查看状态
docker compose ps
docker compose logs -f windmill_server
打开 http://localhost ,用默认凭据登录:admin@windmill.dev / changeme。
官方未提供一个"单容器
docker run一键启动"命令。原因是 Windmill 必须有 Postgres(队列与全部状态都在库里),单容器无法自足。如果你坚持单容器运行,需要自备 Postgres 并在.env里设置DATABASE_URL,然后:
bash docker run -d --name windmill \ -p 8000:8000 \ -e DATABASE_URL=postgres://user:pass@your-postgres:5432/windmill?sslmode=disable \ -e MODE=standalone \ ghcr.io/windmill-labs/windmill:main这种方式没有 Caddy(LSP / multiplayer / email 触发器不可用),仅适用于验证;正式使用请以 5.1 的 docker compose 为准。
5.2 macOS
# 启动 Docker Desktop
open /Applications/Docker.app
# 后续同 5.1
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/docker-compose.yml -o docker-compose.yml
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/Caddyfile -o Caddyfile
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/.env -o .env
docker compose up -d
打开 http://localhost 即可。
5.3 Windows
服务端:官方推荐在 Linux 上运行 Windows 装 Docker Desktop 跑 compose 也可行,但官方未提供 Windows 原生的 server 部署文档。
Worker:官方提供 Windows workers 文档,可以不用 Docker 直接在 Windows 上跑 worker。Windows 上 PID namespace 隔离是 Linux-only,需要设置:
windmill_worker_windows:
environment:
- ENABLE_UNSHARE_PID=false
另外环境变量
LIMIT_WINDOWS_TO_1CU=true可把 Windows worker 限制到 1 vCPU + 2 GB 内存。
5.4 Docker Compose 各服务、端口与卷(官方 docker-compose.yml 结构表)
命名卷:db_data、worker_dependency_cache、worker_logs、worker_memory、windmill_index、lsp_cache、caddy_data。
.env 默认内容:
DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
# 企业版改用:
# WM_IMAGE=ghcr.io/windmill-labs/windmill-ee:main
WM_IMAGE=ghcr.io/windmill-labs/windmill:main
Caddyfile 要点:
{
layer4 {
:25 {
route {
proxy {
upstream windmill_server:2525
}
}
}
}
}
{$BASE_URL} {
bind {$ADDRESS:0.0.0.0 ::}
@extra path /ws/* /ws_mp/* /ws_debug/*
reverse_proxy @extra http://windmill_extra:3000
# reverse_proxy /api/srch/* http://windmill_indexer:8002
reverse_proxy /* http://windmill_server:8000
}
5.5 扩展 worker 数量(支撑几百 worker)
官方给出的经验法则:1 worker / 1 vCPU,1~2 GB RAM。
方式 A:改 compose 里的 deploy.replicas(官方推荐)
windmill_worker:
image: ${WM_IMAGE}
deploy:
replicas: 16 # ← 改成你需要的数量
resources:
limits:
memory: 2048M
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=default
- FAVOR_UNSHARE_PID=true
然后 docker compose up -d 生效。
方式 B:命令行临时扩缩容
docker compose up -d --scale windmill_worker=16
docker compose ps | grep windmill_worker
方式 C:新建专用 worker group(按 tag 分流)
windmill_worker_highmem:
image: ghcr.io/windmill-labs/windmill-ee:main
pull_policy: always
deploy:
replicas: 2
resources:
limits:
cpus: '1'
memory: 4096M
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=highmem
方式 D:多节点扩展 —— worker 只需要 DATABASE_URL,可以跑在独立 VPC(只要有到数据库的通道),因此可以在多台机器上各起一组 compose 只跑 worker。
5.6 Kubernetes(官方 Helm chart)
README 与自托管文档都给出了官方 chart:
helm repo add windmill https://windmill-labs.github.io/windmill-helm-charts/
helm install windmill-chart windmill/windmill \
--namespace=windmill \
--create-namespace
企业版在 values.yaml 里开启:
enterprise:
enabled: true
官方建议:使用 Helm 时应关掉 chart 自带的 postgresql,把
database_url指向你自己的托管 Postgres。超过 20 个 worker 时官方推荐使用 global S3 cache(需要 S3 协议兼容的对象存储)。详细配置见 https://github.com/windmill-labs/windmill-helm-charts 。
5.7 源码编译(Rust 后端 + Svelte 前端)
官方 frontend/README_DEV.md 给出两套方式,推荐 Nix:
# ===== 方式 A:Nix(推荐)=====
git clone https://github.com/windmill-labs/windmill.git
cd windmill
nix develop
./start-dev-db.sh # 起本地 db
wm # 跑前端
# 另一个终端
nix develop
cd backend
cargo run --features all_languages
# ===== 方式 B:传统方式 =====
# 前端
cd frontend/
npm install
npm run generate-backend-client # macOS 用 generate-backend-client-mac
npm run dev
# 后端
./start-dev-db.sh
cd backend
DATABASE_URL=postgres://postgres:changeme@127.0.0.1:5433/windmill?sslmode=disable cargo run
# 前端指向本地后端
cd frontend
REMOTE=http://127.0.0.1:8000 REMOTE_LSP=http://127.0.0.1:3001 npm run dev
前置依赖:Rust 工具链、LLVM(macOS brew install llvm gsed 并把 lld 加入 PATH)。
M1 Mac 已知问题:
# linking with cc failed
echo 'export RUSTFLAGS="-L/opt/homebrew/opt/libomp/lib"' >> ~/.zshrc && source ~/.zshrc
# deno_core 特性下 libffi-sys 编译失败 → 改用 deno_core_mac
cargo run --features deno_core_mac
用本地源码构建镜像(官方 docker/dev.nu):
docker/dev.nu up --features "python,static_frontend" --rebuild
注意:CONTRIBUTING.md 明确说明项目目前不接受外部代码贡献,只接受十几行以内、易验证的修复 PR,且需签署 CLA。因此源码编译主要用于自用与排错,而非提交 PR。
六、配置文件全部参数解释
6.1 .env / docker-compose 关键变量
6.2 环境变量全表(官方 Environment variables 参考节选)
6.3 脚本内可用的上下文变量(Contextual variables)
6.4 Worker Group / Tag 配置
6.5 Instance settings(超级管理员界面,会覆盖同义环境变量)
6.6 带注释的完整配置示例(生产化 docker-compose 片段 + .env)
# ============ .env ============
# 使用外部托管 Postgres(Aurora / Cloud SQL / Neon / Azure 均可)
DATABASE_URL=postgres://windmill:STRONG_PASSWORD@pg.internal.example.com:5432/windmill?sslmode=require
# 社区版镜像;EE 用 ghcr.io/windmill-labs/windmill-ee:main
WM_IMAGE=ghcr.io/windmill-labs/windmill:main
# 日志轮转
LOG_MAX_SIZE=20m
LOG_MAX_FILE=10
# ============ docker-compose.yml(关键片段)============
services:
windmill_server:
image: ${WM_IMAGE}
pull_policy: always
deploy:
replicas: 2 # 多副本做 HA;server 无状态
restart: unless-stopped
expose:
- 8000
- 2525
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=server
- BASE_URL=https://windmill.example.com # 与实例设置一致
- JSON_FMT=true # 结构化日志
- COOKIE_DOMAIN=example.com
- MAX_SESSION_VALIDITY_SECONDS=259200
- CREATE_WORKSPACE_REQUIRE_SUPERADMIN=true
volumes:
- worker_logs:/tmp/windmill/logs
logging: *default-logging
windmill_worker:
image: ${WM_IMAGE}
pull_policy: always
deploy:
replicas: 8 # 8 vCPU / 16 GB 的机器
resources:
limits:
memory: 2048M
restart: unless-stopped
privileged: true # PID namespace 隔离需要
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=default
- WORKER_TAGS=python3,deno,go,bash,bun,dependency,flow,hub
- FAVOR_UNSHARE_PID=true
- ENABLE_UNSHARE_PID=true
- DISABLE_NSJAIL=false # 开启 nsjail 沙箱
- KEEP_JOB_DIR=false
- QUIET=true # 生产降噪
- SLEEP_QUEUE=50
- PY_CONCURRENT_DOWNLOADS=20
- PIP_LOCAL_DEPENDENCIES=^torch$,^numpy$ # 已预装,跳过解析
volumes:
- worker_dependency_cache:/tmp/windmill/cache
- worker_logs:/tmp/windmill/logs
logging: *default-logging
windmill_worker_native:
image: ${WM_IMAGE}
deploy:
replicas: 2
resources:
limits:
memory: 2048M
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=native
- NATIVE_MODE=true
- SLEEP_QUEUE=200
volumes:
- worker_logs:/tmp/windmill/logs
# GPU / 高内存专用组
windmill_worker_highmem:
image: ${WM_IMAGE}
deploy:
replicas: 2
resources:
limits:
cpus: '2'
memory: 8192M
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=highmem # 在 UI 里给它配 tag 后即可被选中
volumes:
- worker_logs:/tmp/windmill/logs
caddy:
image: ghcr.io/windmill-labs/caddy-l4:2.11.4-1
restart: unless-stopped
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- ./certs:/certs # 自备证书时启用 HTTPS
ports:
- 443:443 # 改对外端口就改这里
- 25:25
environment:
- BASE_URL=windmill.example.com # 同步改 BASE_URL
logging: *default-logging
七、启动、后台常驻、开机自启、改端口
7.1 启动与停止
# 前台启动(看日志)
docker compose up
# 后台启动
docker compose up -d
# 停止(保留卷)
docker compose down
# 重启单个服务
docker compose restart windmill_worker
# 只看某个服务日志
docker compose logs -f windmill_server
docker compose logs -f windmill_worker --tail=200
7.2 开机自启
官方 compose 里每个服务都带了 restart: unless-stopped,Docker 守护进程开机自启后容器会自动拉起:
sudo systemctl enable --now docker
如果需要显式用 systemd 托管整套 compose:
# /etc/systemd/system/windmill.service
[Unit]
Description=Windmill compose stack
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/windmill
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now windmill
sudo systemctl status windmill
7.3 Supervisor(备选)
[program:windmill]
command=/usr/bin/docker compose up
directory=/opt/windmill
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stderr_logfile=/var/log/windmill.err.log
stdout_logfile=/var/log/windmill.out.log
7.4 Windows(NSSM / 计划任务)
Windows 上主要跑 worker(官方 windows_workers 文档支持不用 Docker 直接跑):
# 用 NSSM 把 windmill worker 注册为服务
nssm install windmill-worker "C:\windmill\windmill.exe"
nssm set windmill-worker AppEnvironmentExtra MODE=worker DATABASE_URL=postgres://... ENABLE_UNSHARE_PID=false
nssm start windmill-worker
若只需定时触发脚本,直接在 Windmill 里配 schedule(cron),不需要 Windows 侧的任务计划。
7.5 改端口
改对外端口(推荐,只动 caddy):
caddy:
ports:
- 8000:80 # 把宿主 8000 映射到 caddy 的 80
- 25:25
environment:
- BASE_URL=":80"
官方注释原文:"To change the exposed port, simply change 80:80 to <desired_port>:80. No other changes needed"。
直接访问 server(绕过 caddy,仅调试):
windmill_server:
ports:
- "8000:8000" # 把 expose 换成 ports
绕过 caddy 后 LSP(3001)、multiplayer(3002)、debugger(3003)与 email 触发器(25)都不可用,编辑器智能提示会失效。
启用 HTTPS(自备证书):
{$BASE_URL} {
bind {$ADDRESS:0.0.0.0 ::}
@extra path /ws/* /ws_mp/* /ws_debug/*
reverse_proxy @extra http://windmill_extra:3000
reverse_proxy /* http://windmill_server:8000
tls /certs/cert.pem /certs/key.pem
}
同时把 compose 里 443:443 打开、BASE_URL 改为 :443 或你的域名。
外部反向代理(Nginx 示例):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl http2;
server_name windmill.example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# LSP / multiplayer / debugger 的 WebSocket
location ~ ^/(ws|ws_mp|ws_debug)/ {
proxy_pass http://127.0.0.1:3000; # windmill_extra 的 3000 网关
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
location / {
proxy_pass http://127.0.0.1:8000; # windmill_server
proxy_http_version 1.1;
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;
client_max_body_size 100m;
}
}
记得把实例设置里的 Base url 改成 https://windmill.example.com。
7.6 健康检查
curl -s http://localhost/api/health/status # 无需鉴权,5 秒缓存
curl -s "http://localhost/api/health/status?force=true"
curl -s -H "Authorization: Bearer $WM_TOKEN" http://localhost/api/health/detailed
状态判定:DB 可达且 worker 存活 → healthy;worker 缺失或版本不一致 → degraded;DB 不可达 → unhealthy。用 SILENCE_HEALTH_LOGS=true 可抑制健康端点日志。
八、功能实操教程
8.1 场景一:第一次登录与初始化
打开 http://localhost ,用
admin@windmill.dev/changeme登录(即使配了 OAuth 也可以用这个账号登录)。会被重定向到 Instance settings 页面,之后可以随时改。
设置一个新的账号来覆盖默认 superadmin 账号;这一步还会从 Windmill Hub 同步资源类型,并可设为每天自动同步。
最后创建一个 workspace。
8.2 场景二:写一个 Python 脚本,自动生成 UI 与端点
# u/alice/hello_world
def main(name: str, times: int = 3) -> str:
"""向某人问好"""
return "\n".join([f"Hello, {name}!"] * times)
Windmill 解析
main()的签名 → 自动生成表单(name文本框、times数字框带默认值 3)。自动生成的同步端点:
POST /api/w/<workspace>/jobs/run_wait_result/p/u/alice/hello_world异步端点:
POST /api/w/<workspace>/jobs/run/p/u/alice/hello_world(返回 job uuid)隐藏路径版本:
/jobs/run/h/<hash>、/jobs/run_wait_result/h/<hash>
调用:
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{"name": "Windmill", "times": 2}' \
"http://localhost/api/w/demo/jobs/run_wait_result/p/u/alice/hello_world"
返回:
"Hello, Windmill!\nHello, Windmill!"
token 也可以放在查询参数
?token=<TOKEN>,但官方强烈建议放 header(URL 会被日志与浏览器历史记录)。
8.3 场景三:用 windmill-client 与 Python client 读写变量/资源
TypeScript(Bun):
import * as wmill from "windmill-client";
import dayjs from "dayjs";
export async function main(customerId: string, plan: "starter" | "pro", trialDays = 14) {
const trialEnd = dayjs().add(trialDays, "day").toISOString();
const sql = wmill.datatable();
await sql`INSERT INTO trials (customer_id, plan, trial_end) VALUES (${customerId}, ${plan}, ${trialEnd})`.fetch();
const slackToken = await wmill.getVariable("f/sales/slack_token");
await fetch("https://slack.com/api/chat.postMessage", {
method: "POST",
headers: { Authorization: `Bearer ${slackToken}`, "Content-Type": "application/json" },
body: JSON.stringify({ channel: "#sales", text: `New ${plan} trial: ${customerId}` }),
});
console.log(`Trial started for ${customerId}`);
return { customerId, plan, trialEnd };
}
Python:
import wmill
def main(path: str = "u/alice/api_key"):
value = wmill.get_variable(path) # 读取变量(含 secret)
wmill.set_variable("u/alice/counter", "42")
res = wmill.get_resource("f/devops/pg_prod") # 读取资源(JSON 对象)
return {"var": value, "resource": res}
资源里引用其它值(在资源表单里填):
8.4 场景四:编排一个带人工审批的 Flow(代码即工作流)
import { task, waitForApproval, workflow } from "windmill-client";
async function plan(env: string) {
return { env, changes: 3 };
}
async function apply(plan: { env: string; changes: number }) {
return `applied ${plan.changes} changes to ${plan.env}`;
}
export const main = workflow(async (env: string) => {
const changes = await task(plan)(env);
const approval = await waitForApproval({ timeout: 3600 });
if (!approval.approved) {
return `rejected by ${approval.approver}`;
}
return await task(apply)(changes);
});
每个 task() 是独立 job,有独立日志;waitForApproval 等待期间会释放 worker。

8.5 场景五:定时调度与 Webhook
Schedule:在脚本 / flow 的 Triggers 页加 Schedule,填 cron 表达式(如
0 9 * * 1=每周一 9:00)。UI 里可看到下次运行时间。Webhook:脚本 / flow 详情页的 "UUID/Async" 与 "Result/Sync" 两个 tab 给出 URL。异步返回 job uuid;同步直接返回结果。
HTTP route:可自定义路由路径。
Email 触发器:Caddy 把 25 端口转到 server 的 2525;实例设置里的 Email domain 要与 MX 记录一致。
同步 GET 调用(payload 需先 JSON 再 URL-safe base64):
PAYLOAD=$(node -e 'console.log(encodeURIComponent(Buffer.from(JSON.stringify({a:2})).toString("base64")))')
curl -X GET -H "Authorization: Bearer $TOKEN" \
".../w/demo/jobs/run_wait_result/p/u/bot/hello_world_deno?payload=$PAYLOAD"
非对象 body:会被包成 {"body": <payload>};也可用 ?wrap_body=true 强制包裹;?raw=true 会额外传入 raw_string(原始字符串,便于验签)。
8.6 场景六:CLI wmill 同步到 Git 并本地开发
# 1. 安装(Node > v20)
npm install -g windmill-cli
wmill --version
wmill upgrade # 升级 CLI
source <(wmill completions zsh)
# 2. 克隆 workspace 到本地
mkdir myworkspace && cd myworkspace
wmill workspace add myworkspace <workspace_id> <remote>
wmill sync pull
# 3.(推荐)只拉脚本/flow/app,跳过变量与资源
wmill sync pull --skip-variables --skip-secrets --skip-resources
# 4. 新建或修改脚本
wmill script bootstrap f/my_space/example_script python3
# ... 用 IDE 编辑 .py 文件 ...
wmill generate-metadata f/my_space/example_script.py
# 5. 推回 workspace
wmill sync push
# 6. 提交到 Git
git init && git remote add origin https://github.com/username/reponame.git
git add . && git commit -m 'Initial commit' && git push -u origin main
wmill.yaml 默认内容:
defaultTs: bun
includes:
- f/**
excludes: []
codebases: []
skipVariables: false
skipResources: false
skipResourceTypes: false
skipSecrets: true
includeSchedules: false
includeTriggers: false
⚠️
wmill sync pull/push是一次性全量覆盖操作:目标里存在而源里没有的会被删除。官方明确建议只在纳入 Git 版本控制的目录里执行,以便误操作时能回滚。执行前会列出变更并征求确认(--yes可跳过)。
其它实用子命令(每种类型都支持 list / get / new):
wmill script list --json | jq '.[].path'
wmill flow list
wmill resource get f/db/prod --json | jq .value.host
wmill variable list
wmill trigger new f/http/webhook --kind http
wmill docs "how do I create a flow?"
wmill digest f/infra/agent__flow --json
wmill config # 打印 wmill.yaml 全部选项参考
8.7 场景七:Git sync(双向自动同步)
workspace settings → Git Sync tab →
+ Add connection。创建或选择
git_repository资源,三种认证方式: - GitHub App(推荐,Windmill 托管或实例自托管); - GitLab(Enterprise); - Personal Access Token:URL 形如https://[USERNAME]:[TOKEN]@github.com/[ORG|USER]/[REPO_NAME].git。若仓库是空的,用 "Initialize Git repository" 自动生成
wmill.yaml并推送全部内容。Git sync 的过滤设置来自仓库里的
wmill.yaml,改完点按钮重新拉取即可。支持 GPG 签名提交(
gpg --full-generate-key,公钥加到 GitHub 账号)。
注意:Git sync 在社区版仅限 ≤2 人的 workspace;更大规模需要 Cloud 或 Enterprise Self-Hosted。
8.8 场景八:Worker 分流与 Python 依赖预装
# 场景:某些脚本必须在高内存机器上跑
# 1) compose 里加一个 worker group(见 5.5 方式 C),WORKER_GROUP=highmem
# 2) 在 UI:Workers 页 → 找到 highmem 组 → 编辑 config → 配 worker_tags
# 3) 在脚本 settings 里选这个 tag
# 社区版也可以直接用环境变量给 worker 打 tag
WORKER_TAGS=python3,gpu,highmem
Python 依赖最佳实践(官方 Worker group → Python runtime settings):
Additional Python paths:额外加入
sys.path的目录,里面的模块可被脚本直接 import。PIP_LOCAL_DEPENDENCIES(或环境变量,逗号分隔):用正则声明"worker 上已经预装"的包,依赖解析时跳过它们,避免无谓的 PyPI 解析与下载。适合你把
torch、numpy之类预装进自定义镜像或全局 site-packages 的情况。依赖解析由
dependencytag 的 job 完成;Enterprise 可用 "Dependency job tag" 把所有依赖 job 收敛到同一个 tag(如deps-$workspace)。
8.9 场景九:自建 App 界面
在 App editor 里把脚本 / flow 拖成组件,绑定输入输出,即可做出内部工具页面;也可做成公开 App 供无账号访客(guest)访问。

九、全套报错故障排查
十、升级、回滚与卸载
10.1 查看版本
实例设置页面顶部会显示当前 Windmill 版本。
镜像 tag:
ghcr.io/windmill-labs/windmill:main(滚动最新)或具体版本 tag(见 GitHub Releases,如v1.849.0)。
docker compose exec windmill_server windmill --version
wmill --version # CLI 版本
10.2 官方升级命令
docker compose stop windmill_worker
docker compose pull windmill_server
docker compose up -d
官方说明要点:
worker 会优雅退出,正在跑的任务不会被打断,除非任务耗时超过
docker stop的硬杀超时(30 秒)。数据库卷是持久的,同一 PostgreSQL 大版本内升级 db 镜像是安全的;跨大版本必须走《Upgrade PostgreSQL to 18》流程。
如果本来就是 detached 运行,直接再跑一次
docker compose up -d也会拉取最新:main并重启容器。旧镜像不会自动删除,需要手动
docker builder prune清理。
全量拉取(含 worker):
docker compose pull
docker compose up -d
docker image prune -f
10.3 回滚到指定版本
# 1. 修改 .env 固定镜像版本
# WM_IMAGE=ghcr.io/windmill-labs/windmill:v1.840.0
sed -i 's#WM_IMAGE=.*#WM_IMAGE=ghcr.io/windmill-labs/windmill:v1.840.0#' .env
# 2. 拉起
docker compose up -d
# 3. 确认
docker compose ps
⚠️ 回滚不等于数据库回滚:Windmill 的迁移是向前的,回滚镜像后如果数据库 schema 已经升级,可能出现不兼容。回滚前务必先备份数据库(
pg_dumpall,注意是整个集群而不只是 windmill 库)。
10.4 Helm 升级
helm repo update
helm upgrade windmill-chart windmill/windmill -n windmill -f values.yaml
helm rollback windmill-chart <revision> -n windmill
helm history windmill-chart -n windmill
10.5 完整卸载清单
# 1. 停止并删除容器
docker compose down
# 2. 删除数据卷(会清空全部状态:脚本、flow、job 历史、变量、资源)
docker compose down --volumes
docker volume rm -f windmill_db_data windmill_worker_logs \
windmill_worker_dependency_cache windmill_windmill_index \
windmill_lsp_cache windmill_caddy_data 2>/dev/null
# 卷名前缀取决于你的项目目录名,先 docker volume ls 确认
# 3. 删除镜像
docker rmi ghcr.io/windmill-labs/windmill:main \
ghcr.io/windmill-labs/windmill-extra:latest \
ghcr.io/windmill-labs/caddy-l4:2.11.4-1 \
postgres:18
# 4. 删除配置文件
rm -f docker-compose.yml Caddyfile .env
# 5. 若用 systemd 托管
sudo systemctl disable --now windmill
sudo rm -f /etc/systemd/system/windmill.service
sudo systemctl daemon-reload
# 6. 卸载 CLI
npm uninstall -g windmill-cli
# 7. Helm 卸载
helm uninstall windmill-chart -n windmill
kubectl delete namespace windmill
十一、商用许可与合规风险提示
11.1 License 结构(以官方 LICENSE 文件为准,不要想当然)
11.2 许可义务与红线
官方 LICENSE 原文要点(意译归纳):
免费可用的:把 Community Edition 原样(as is) 用于组织内部,或通过其 API 原样调用 —— 不需要商业许可。
需要商业许可的:
销售、转售,或把它作为托管服务提供;
修改或包装(modify or wrap)Community Edition;
把 Windmill 的任何部分直接重新暴露给你的用户作为你自己产品的一个功能(iframed 的公开 App 除外);
在 Community Edition 之上构建你售卖的商业功能,或把它嵌入可分发的产物 / 二进制。
源码路线:如果从本仓库源码编译(不带
enterpriseflag),你要么遵守 AGPLv3 条款,要么取得商业许可。私有 / 公开 fork 必须删除所有专有商业代码。
商业许可咨询:sales@windmill.dev。
11.3 AGPLv3 的特别注意
AGPLv3 是强 copyleft,且覆盖"通过网络提供服务"的场景:如果你修改了源码并对外提供服务,需要向用户提供修改后的源码。
如果你不希望承担 AGPL 义务,两条路:(1) 原样使用官方 Community Edition 镜像(走上面的商业条款);(2) 购买商业许可。
11.4 商标与品牌
"Windmill" 名称与 logo 归 Windmill Labs, Inc. 所有,许可协议不授予商标使用权。
对外宣传、白标(white labelling)需参考官方
white_labelling文档;把 Windmill 包装成自有产品对外提供属于需要商业授权的情形。
11.5 费用与配额
自托管 Community Edition 本身免费,成本来自你的基础设施(Postgres、worker 机器、对象存储)。
Cloud 与 Enterprise 按席位计费:官方以"最近 30 天有操作的用户"计数(User / Devops / Superadmin 等角色,Operator 也占完整席位)。
Guest(无账号访问公开 App 的人):30 天内前 100 个不同邮箱免费;超过后,持有 EE license 的实例按 4 guest = 1 seat 计量,没有 license 的实例不再接纳新 guest。
企业版 License key 通常 35 天有效,订阅有效时每天自动续期;可把实例标为 Non-prod 以免计算用量计入计费(席位仍全实例统计)。
11.6 数据隐私与出网
自托管的全部数据留在你自己的 Postgres 里,Windmill Labs 不接触你的业务数据;官方建议同时对 Postgres 加密。
每个 workspace 有独立的密钥加密其 K/V 存储中的凭证;secret 的每次读取都会产生
variables.decrypt_secret审计事件。出网方向由你的脚本决定:脚本可以访问任何 worker 能访问的网络。要做网络隔离就用 worker group + tag,让 dev worker 只连 dev 系统、prod worker 只跑已 review 的代码。
遥测(Telemetry)可在实例设置里调整;Hub 同步会访问 hub.windmill.dev,不需要时可 "Disable hub"。
若对接 AI 功能(AI sessions、AI agents),注意脚本会把数据发给对应的模型 provider。
11.7 生产环境风险
默认凭据必须立刻改:
admin@windmill.dev/changeme是公开的默认值,首次登录后要创建自己的 superadmin 账号覆盖它。NO_AUTH=true极其危险:它绕过全部鉴权,把所有请求当作 superadmin。只应在前面有可信认证网关的自托管环境使用,绝不能暴露到公网(云端实例会自动忽略该变量)。沙箱:社区版默认开启 PID namespace 隔离(
ENABLE_UNSHARE_PID=true,compose 中已开启),NSJAIL 默认关闭(DISABLE_NSJAIL=true)。跑不可信代码时建议设DISABLE_NSJAIL=false开启 nsjail(需要privileged: true)。不要挂载 Docker socket:官方注释明确警告,挂
/var/run/docker.sock会让用户脚本获得宿主机文件系统访问与提权能力,只应在完全信任所有用户时才这么做。数据库是单点:全部状态(含任务队列)都在 Postgres,务必做备份、监控连接池,并考虑托管 Postgres / 高可用方案。
Worker 容量规划:1 worker / 1 vCPU / 1~2 GB RAM 是官方经验值;worker 不够时任务会排队而不是丢失,但延迟会上升。
附录:命令速查
# ===== 部署 =====
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/docker-compose.yml -o docker-compose.yml
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/Caddyfile -o Caddyfile
curl https://raw.githubusercontent.com/windmill-labs/windmill/main/.env -o .env
docker compose up -d
docker compose ps
docker compose logs -f windmill_server
docker compose logs -f windmill_worker --tail=200
docker compose restart windmill_worker
# ===== 扩缩容 =====
docker compose up -d --scale windmill_worker=16 # 命令行
# 或在 compose 里改 deploy.replicas 后 docker compose up -d
# ===== 升级 / 回滚 =====
docker compose stop windmill_worker
docker compose pull windmill_server
docker compose up -d
docker compose pull && docker compose up -d && docker image prune -f
# 回滚:改 .env 的 WM_IMAGE 为具体版本 tag 后 docker compose up -d
# ===== 重置 =====
docker compose down --volumes
docker volume rm -f windmill_db_data
docker compose up -d
# ===== 健康检查 =====
curl -s http://localhost/api/health/status
curl -s "http://localhost/api/health/status?force=true"
curl -s -H "Authorization: Bearer $WM_TOKEN" http://localhost/api/health/detailed
# ===== CLI =====
npm install -g windmill-cli
wmill --version
wmill upgrade
source <(wmill completions bash) # bash / zsh / fish
wmill workspace add <name> <workspace_id> <remote>
wmill workspace switch <workspace_id>
wmill sync pull
wmill sync pull --skip-variables --skip-secrets --skip-resources
wmill sync push
wmill sync push --message "从 CI 部署"
wmill script bootstrap f/my_space/example_script python3
wmill flow bootstrap f/my_space/my_flow
wmill generate-metadata f/my_space/example_script.py
wmill script list --json | jq '.[].path'
wmill resource get f/db/prod --json | jq .value.host
wmill variable list
wmill trigger new f/http/webhook --kind http
wmill docs "how do I create a flow?"
wmill config
wmill digest f/infra/agent__flow --json
# ===== 调用端点 =====
# 同步
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
-d '{"name":"Windmill"}' \
"http://localhost/api/w/demo/jobs/run_wait_result/p/u/alice/hello_world"
# 异步
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
-d '{}' "http://localhost/api/w/demo/jobs/run/p/u/alice/hello_world"
# 强制包裹 body / 拿原始串
curl -X POST -d '[1,2,3]' ".../jobs/run/p/u/alice/x?wrap_body=true&raw=true"
# ===== Helm =====
helm repo add windmill https://windmill-labs.github.io/windmill-helm-charts/
helm install windmill-chart windmill/windmill --namespace=windmill --create-namespace
helm upgrade windmill-chart windmill/windmill -n windmill -f values.yaml
helm rollback windmill-chart <revision> -n windmill
# ===== 常用环境变量 =====
export DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
export WM_IMAGE=ghcr.io/windmill-labs/windmill:main
export MODE=server # standalone | server | worker | agent | indexer | mcp
export WORKER_GROUP=default
export WORKER_TAGS=python3,deno,go,bash
export NATIVE_MODE=true
export BASE_URL=https://windmill.example.com
export ENABLE_UNSHARE_PID=true
export DISABLE_NSJAIL=false
export DATABASE_CONNECTIONS=50
export QUIET=true
export JSON_FMT=true
export KEEP_JOB_DIR=true
export PIP_LOCAL_DEPENDENCIES='^torch$,^numpy$'
export LOG_MAX_SIZE=20m
export LOG_MAX_FILE=10