Loading...

文章背景图

Windmill:把脚本秒变内部工具与工作流的代码优先自动化平台|部署与使用全指南

2026-10-27
0
- 字
- 分钟
|

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 主视觉

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

Windmill 架构图

一、项目介绍与适用场景

1.1 它是什么

Windmill 由三部分组成(官方自托管文档):

  1. 一个 Postgres 数据库 —— 保存全部状态,包括任务队列;

  2. 运行在 server 模式 的 Windmill 容器 —— 同时提供前端与 API,对外暴露;

  3. 运行在 worker 模式 的 Windmill 容器 —— 从数据库队列里拉任务执行,可横向扩展。

另有三个可选组件:Windmill LSP(Monaco 编辑器智能提示)、Multiplayer(实时协作,企业版)、反向代理(官方 compose 用 Caddy,把 server / LSP / multiplayer 收敛到一个端口)。

1.2 技术栈(README "Stack" 一节)

层

技术

数据库

Postgres(兼容 Aurora、Cloud SQL、Neon、Azure PostgreSQL)

后端

Rust,无状态 API server 与 worker,从 Postgres 队列拉任务

前端

Svelte 5

沙箱

nsjail(文件系统/资源隔离)+ PID namespace 隔离

TypeScript/JS 运行时

Bun(默认);Deno 仅在 windmill-full 镜像里

Python

python3 + uv 做依赖管理

PHP

仅在 windmill-full 镜像里

其他

Go、Bash、PowerShell、Rust、C#、Java、Ansible

1.3 适用人群

人群

用法

后端 / 平台工程师

把运维脚本变成带权限、带审计、带 UI 的内部工具

数据工程师

用 DuckDB / dbt / pipeline 做数据管道,用 cron 定时跑

SRE / DevOps

Webhook + 定时任务做自动化响应;worker group 隔离 dev / prod 网络

业务团队

在浏览器里用 AI sessions 生成脚本,经工程师 review 后部署

需要替换 Retool / n8n / Airflow 的团队

自托管、Git 同步、环境间 promotion

安全合规要求高的组织

自托管 + nsjail + 每 workspace 独立加密密钥 + 审计日志

1.4 不适合的场景

  • 不会写任何代码、只想拖拽:Windmill 有可视化 flow 编辑器和低代码 App 编辑器,但核心范式是"写代码",纯无代码团队上手成本高。

  • 需要毫秒级延迟的交易链路:官方给出的额外开销约 50ms(入队 + 启动 + 回写),轻量 Deno 任务约 100ms 总耗时,不适合做在线请求的关键路径。

  • 要求"改代码随意提交"的开源协作:CONTRIBUTING.md 明确写明 "At this time, we are not seeking outside contribution",只接受小到十几行的修复 PR。

  • 想把 Windmill 包装成自己的产品对外售卖:需要商业授权(见第十一章)。

二、核心功能逐条拆解

  1. 脚本即全栈产物:写一个带类型标注的 main(),自动得到自动生成的 UI 表单(union 类型变下拉、默认值推断类型)、同步/异步 HTTP 端点、CLI 命令、可分享的公开路径。

  2. 多语言运行时:Python、TypeScript(Bun/Deno)、Go、Bash、SQL、GraphQL、PowerShell、Rust、C#、Java、Ansible、Nu、Ruby、DuckDB、dbt 等 20+ 语言。

  3. 依赖自动解析与锁文件:Python 用 uv 解析并生成 lockfile;TypeScript 直接从 npm 解析并 pin 版本;无需自己维护 requirements.txt。

  4. Flow(工作流):可视化 flow 编辑器、YAML,或直接用 windmill-client 的 workflow() / task() 写"代码即工作流";每个 step 是独立 job,有独立日志;支持 waitForApproval 人工审批(等待期间释放 worker)。

  5. 触发器:schedule(cron)、webhook、HTTP route、Kafka、WebSocket、email(SMTP,Caddy 把 25 端口转到 server 的 2525)、Postgres、NATS、SQS、GCP Pub/Sub、MQTT 等。

  6. Resources 与 Resource Types:资源就是"带类型的 JSON 凭证对象",200+ 官方 resource type(MySQL、MongoDB、OpenAI…),SDK 里按类型自动筛选,避免硬编码密码。

  7. Variables 与 Secrets:所有变量(不只是 secret)都用 workspace 级对称密钥加密;secret 读取会写审计事件 variables.decrypt_secret。

  8. Worker 与 Worker Group:worker 按 tag 拉任务;默认 group 为 default 与 native;可建 highmem、gpu、reports 等组并配 init script 预装二进制。

  9. Git 同步与本地开发:wmill sync pull/push 双向同步;VS Code / Cursor 扩展;Git sync 让 workspace 自动提交到 Git 仓库并从仓库部署。

  10. AI 能力:AI sessions(浏览器里用 AI 生成脚本/flow/app,带 diff review)、MCP server(让编码 agent 操作 workspace)、保护规则集(禁止直接部署,必须经工程师 review)。

  11. 数据能力:Data tables(workspace 内置 Postgres 表)、DuckDB(直接查 S3 / Parquet / BigQuery)、DuckLake(lakehouse)、Pipelines(按数据依赖驱动,alpha)。

  12. 可观测性与治理:Runs 页面、审计日志、OpenTelemetry、Prometheus 指标、/api/health/status 与 /api/health/detailed 健康检查端点、保留期策略。

Windmill 脚本编辑器

三、全套官方链接

类别

链接

GitHub 仓库

https://github.com/windmill-labs/windmill

官网

https://www.windmill.dev/

官方文档

https://www.windmill.dev/docs/intro/

在线体验(Cloud)

https://app.windmill.dev

自托管文档

https://www.windmill.dev/docs/advanced/self_host

实例设置文档

https://www.windmill.dev/docs/advanced/instance_settings

OAuth / SSO 文档

https://www.windmill.dev/docs/advanced/setup_oauth

环境变量参考

https://www.windmill.dev/docs/core_concepts/environment_variables

Worker 与 Worker Group

https://www.windmill.dev/docs/core_concepts/worker_groups

CLI 文档

https://www.windmill.dev/docs/advanced/cli

CLI 安装

https://www.windmill.dev/docs/advanced/cli/installation

Sync 文档

https://www.windmill.dev/docs/advanced/cli/sync

Git sync 文档

https://www.windmill.dev/docs/advanced/git_sync

本地开发

https://www.windmill.dev/docs/advanced/local_development

安全与进程隔离

https://www.windmill.dev/docs/advanced/security_isolation

Helm Chart 仓库

https://github.com/windmill-labs/windmill-helm-charts

Helm repo 地址

https://windmill-labs.github.io/windmill-helm-charts/

Windmill Hub(社区脚本/资源类型)

https://hub.windmill.dev

Release 下载

https://github.com/windmill-labs/windmill/releases

Python 客户端(wmill)

https://pypi.org/project/wmill

Docker 镜像

ghcr.io/windmill-labs/windmill:main(CE)/ ghcr.io/windmill-labs/windmill-ee:main(EE)

License 文件

https://github.com/windmill-labs/windmill/blob/main/LICENSE

Discord 社区

https://discord.gg/V7PM2YHsPB

四、硬件与环境前置要求

项目

要求

说明

操作系统

Linux(推荐)/ macOS / Windows

官方 compose 在 Linux 上验证最充分;Windows 只能跑 worker,见 windows_workers 文档

Docker

Docker Engine + Compose V2

官方唯一推荐的快速部署方式

Kubernetes(可选)

1.24+,Helm 3

生产 / 大规模场景走 Helm chart

CPU

最小 2 vCPU;经验法则 1 worker / 1 vCPU

worker 是唯一吃 CPU 的组件

内存

最小 4 GB;1~2 GB / worker

compose 里每个 worker 限制 2048M

磁盘

≥ 20 GB(含 Postgres 数据卷)

db_data 卷存全部状态;日志默认轮转 20m × 10 个文件

数据库

PostgreSQL(compose 自带 postgres:18)

兼容 Aurora、Cloud SQL、Azure、Neon;大版本升级需按官方流程

网络

对外暴露 80(可改);worker 需能连 DB

Caddy 还监听 25 端口用于 email 触发器

浏览器

现代浏览器(Svelte 5 前端)

编辑器智能提示依赖 LSP(3001)

CLI(可选)

Node > v20

npm install -g windmill-cli

源码编译(可选)

Rust + Cargo(推荐用 Nix),Node/npm

见 5.6

账号

无外部账号要求

首次登录用 admin@windmill.dev / changeme

五、多平台部署步骤

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 结构表)

服务

镜像(默认)

MODE

端口

挂载卷

说明

db

postgres:18

—

5432(仅 expose)

db_data:/var/lib/postgresql(注意:18 起挂父目录)

全部状态与任务队列;shm_size: 1g

windmill_server

${WM_IMAGE}

server

8000、2525

worker_logs:/tmp/windmill/logs

前端 + API;2525 是 email 触发器 SMTP

windmill_worker

${WM_IMAGE}

worker

—

worker_dependency_cache:/tmp/windmill/cache、worker_logs:/tmp/windmill/logs

默认 3 副本,内存上限 2048M,privileged: true(PID 隔离)

windmill_worker_native

${WM_IMAGE}

worker

—

worker_logs:/tmp/windmill/logs

1 副本,WORKER_GROUP=native、NATIVE_MODE=true、SLEEP_QUEUE=200

windmill_worker_reports(注释掉)

${WM_IMAGE}

worker

—

同上

WORKER_GROUP=reports,init script 装 chromium,用 chromium tag 命中

windmill_indexer

${WM_IMAGE}

indexer

8002

windmill_index:/tmp/windmill/search、worker_logs

全文任务/日志搜索,EE 特性,默认 replicas: 0

windmill_extra

ghcr.io/windmill-labs/windmill-extra:latest

—

3001(LSP)、3002(Multiplayer)、3003(Debugger)

lsp_cache:/pyls/.cache

合并的额外服务;ENABLE_LSP=true、ENABLE_MULTIPLAYER=false(EE)、ENABLE_DEBUGGER=true

caddy

ghcr.io/windmill-labs/caddy-l4:2.11.4-1

—

80:80、25:25(可选 443)

./Caddyfile、caddy_data:/data

反向代理;BASE_URL=":80"

命名卷: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 关键变量

变量

默认值

作用于

说明

DATABASE_URL

postgres://postgres:changeme@db/windmill?sslmode=disable

All

Postgres 连接串;用外部库时设它,并把 db 副本设为 0

WM_IMAGE

ghcr.io/windmill-labs/windmill:main

—

镜像;EE 为 ghcr.io/windmill-labs/windmill-ee:main

MODE

standalone

All

二进制模式:standalone / server / worker / agent(EE) / indexer(EE) / mcp

WORKER_GROUP

default

Worker

worker 所属的组,配置从该组拉取

WORKER_TAGS

—

Worker

该 worker 监听的 tag 列表,逗号分隔

NATIVE_MODE

false

Worker

true 时自动起 8 个 native subworker,且只接受 native job

BASE_URL

http://localhost:8000

Server

对外公开访问地址;会被实例设置覆盖

LICENSE_KEY

无(EE)

Worker

EE 许可证 key

LOG_MAX_SIZE / LOG_MAX_FILE

20m / 10

—

compose 日志轮转

BASE_URL(caddy 段)

":80"

Caddy

改对外端口时同步改这里与 compose 的 ports

6.2 环境变量全表(官方 Environment variables 参考节选)

名称

默认值

说明

作用范围

DATABASE_URL

—

Postgres 连接串

All

WORKER_GROUP

default

worker 组名

Worker

MODE

standalone

运行模式

All

METRICS_ADDR

无(EE)

Prometheus /metrics 暴露地址,设 true 用 8001

All

JSON_FMT

false

日志输出 JSON 而非 logfmt

All

BASE_URL

http://localhost:8000

公开访问地址,实例设置优先

Server

ZOMBIE_JOB_TIMEOUT

60

判定僵尸任务的秒数(server 每 10s 检查)

Server

RESTART_ZOMBIE_JOBS

true

僵尸任务原地重启最多 3 次

Server

SLEEP_QUEUE

50

每 worker 两次拉队列之间的休眠毫秒

Worker

NATIVE_MODE

false

native 子 worker 模式

Worker

KEEP_JOB_DIR

false

保留 /tmp/windmill/<worker>/<job_id> 便于调试

Worker

ENABLE_UNSHARE_PID

false(compose 中 true)

PID namespace 隔离,仅 Linux

Worker

DISABLE_NSJAIL

true

true 表示关闭 NSJAIL;设 false 开启

Worker

NSJAIL_PY_RLIMIT_AS_MB

4096

NSJAIL 下 Python 任务虚拟地址空间上限(MiB)

Worker

NSJAIL_ANSIBLE_RLIMIT_AS_MB

4096

同上,Ansible 任务

Worker

NSJAIL_GVISOR_COMPAT

false

gVisor(GKE Sandbox)兼容模式

Worker

UNSHARE_ISOLATION_FLAGS

--user --map-root-user --pid --fork --mount-proc

unshare 隔离参数

Worker

UNSHARE_TINI_PATH

tini

PID 1 信号处理

Worker

EXIT_AFTER_N_JOBS

无

跑 N 个任务后退出,由 supervisor 重启

Worker

WORKER_SUFFIX

无

固定 worker 名后缀,≤64 字符

Worker

JOB_OOM_SCORE_ADJ

1000

任务子进程的 oom_score_adj(0~1000)

Worker

WHITELIST_ENVS

无

逗号分隔,透传给所有任务的 worker 环境变量

Worker

LICENSE_KEY

无(EE)

EE 许可 key

Worker

SLACK_SIGNING_SECRET

无

Slack app 签名密钥

Server

SLACK_OAUTH_SCOPES

见文档

连接 Slack 时请求的 bot scopes

Server

COOKIE_DOMAIN

无

Cookie 域名

Server

DENO_PATH

/usr/bin/deno

deno 二进制路径

Worker

PYTHON_PATH

/usr/local/bin/python3(不推荐改)

python 路径;改了后无法选版本

Worker

GO_PATH

/usr/bin/go

go 二进制路径

Worker

GOPRIVATE / GOPROXY / NETRC

—

Go 私有模块

Worker

GO_BUILD_MEMLIMIT

worker 内存的 75%

Go 编译内存预算(GOMEMLIMIT)

Worker

PY_CONCURRENT_DOWNLOADS

20

Python 下载最大并发

Worker

DATABASE_CONNECTIONS

Server 50 / Worker 3

连接池大小

All

SUPERADMIN_SECRET

无

可充当虚拟 superadmin 的 token

Server

NO_AUTH

false

设为 true/1 时绕过全部鉴权,所有请求视为 superadmin

Server

TIMEOUT_WAIT_RESULT

20

run_wait_result 端点等待秒数

Worker

QUEUE_LIMIT_WAIT_RESULT

无

队列任务数上限,超了立即拒绝

Worker

DENO_AUTH_TOKENS

无

私有 Deno 模块

Worker

DISABLE_RESPONSE_LOGS

false

关闭响应日志

Server

CREATE_WORKSPACE_REQUIRE_SUPERADMIN

true

只有 superadmin 能建 workspace

Server

MAX_SESSION_VALIDITY_SECONDS

3 天

会话无活动后的失效秒数

Server

GUEST_SESSION_VALIDITY_SECONDS

8 小时

guest app 会话有效期

Server

INVALIDATE_OLD_SESSIONS

false

建新会话时失效旧会话

Server

INVALIDATE_ALL_SESSIONS_ON_LOGOUT

false

登出时失效全部会话

Server

RUN_UPDATE_CA_CERTIFICATE_AT_START

false

启动时更新 CA 证书

All

RUN_UPDATE_CA_CERTIFICATE_PATH

/usr/sbin/update-ca-certificates

CA 更新命令路径

All

OIDC_TOKEN_EXPIRY_SECS

48 小时

OIDC token 默认有效期

All

QUIET

false

静默模式,减少每任务日志

Worker

LOGIN_DOMAIN

无

邮箱无 @ 时自动补 @{LOGIN_DOMAIN} 并转小写

Server

HIDE_WORKERS_FOR_NON_ADMINS

false

对非管理员隐藏 Workers 页面

Server

WINDMILL_DIR

/tmp/windmill

临时任务文件根目录

Worker

LIMIT_WINDOWS_TO_1CU

false

Windows worker 限 1 vCPU / 2 GB

Worker

RSCRIPT_PATH

/usr/bin/Rscript

R 脚本路径,仅 windmill-full 镜像

Worker

ALLOW_LOCAL_GIT_REMOTES

false

允许访问 localhost/内网 Git 主机

Server

DISABLE_ANTHROPIC_PROMPT_CACHING

false

关闭 Anthropic prompt caching 断点(GCP 关闭该特性时需要)

Worker

PG_SCHEMA

public

非 public schema 时要传给每个 server

Server

6.3 脚本内可用的上下文变量(Contextual variables)

名称

示例值

说明

WM_WORKSPACE

demo

当前 workspace id

WM_TOKEN

q1E2qcLy...

与本次运行同等权限的临时 token(可作 Bearer)

WM_EMAIL

ruben@windmill.dev

执行者邮箱

WM_USERNAME

ruben

执行者用户名

WM_BASE_URL

https://app.windmill.dev/

实例 base url

WM_JOB_ID

017e0ad5-...

当前 job id

WM_JOB_PATH

u/user/script_path

脚本/flow 路径

WM_FLOW_JOB_ID / WM_ROOT_FLOW_JOB_ID

—

所属 flow / 根 flow 的 job id

WM_FLOW_PATH

u/user/flow_path

所属 flow 路径

WM_SCHEDULE_PATH

—

触发该任务的 schedule 路径

WM_PERMISSIONED_AS

u/henri

执行者全限定名

WM_STATE_PATH

—

脚本 + 触发器唯一的 state 资源路径

WM_FLOW_STEP_ID

c

flow 中的节点 id

WM_OBJECT_PATH

—

脚本/步骤执行的唯一路径

WM_WORKER_GROUP

default

运行该任务的 worker group

WM_RUNNABLE_ID

1712845957812678132

脚本 hash,适合做缓存 key

WM_END_USER_EMAIL

—

实际服务对象邮箱(app 浏览者 / 直接触发者)

6.4 Worker Group / Tag 配置

配置项

说明

WORKER_GROUP=<name>

worker 加入哪个组,组的配置(含 init script)自动下发并热更新

WORKER_TAGS=tag1,tag2

该 worker 监听的 tag

默认 default 组 tag

deno、python3、go、bash、powershell、dependency、flow、hub、bun、php、rust、ansible、csharp、java、nu、ruby、duckdb、dbt、other

默认 native 组 tag

nativets、postgresql、mysql、mssql、graphql、snowflake、bigquery、oracledb

tag 限定 workspace

gpu(workspace+workspace2);排除写法 gpu(^workspace);加 * 延伸到 fork

动态 tag

$workspace(替换为 workspace id)、$args[argName]、$flow_expr[results.<step_id>].x

优先级

同一队列内 job 优先级 1~100,高者优先

Python 运行时

Additional Python paths(额外 sys.path)、PIP_LOCAL_DEPENDENCIES(正则,跳过已预装包的解析)

6.5 Instance settings(超级管理员界面,会覆盖同义环境变量)

设置

说明

Base url

实例公开地址。设错会导致服务端生成的 URL(如 resume URL)不对,OAuth / SSO 也会失效

WebSocket base url

前端 WebSocket(LSP / debugger / multiplayer)的 base url 覆盖;为空则回退到当前 window.location

Email domain

email 触发器在 webhook 中显示的域名,应与 MX 记录一致

Request size limit in MB

HTTP 请求体上限(Cloud only)

Default timeout

单个 job 的默认超时(秒)

Max timeout for sync endpoints

同步端点最长运行时间(秒)

Keep job directories for debug

保留 /tmp/windmill/<worker>/<job_id>

License key

启用 Enterprise Edition;key 通常 35 天有效,订阅有效时每天自动续期

Non-prod instance

标记为非生产,计算用量不计入计费(席位仍全实例统计)

Accent color

#rrggbb,区分 staging / prod 环境

HTTP route default allowed origins

HTTP route 的默认 CORS origin 列表

Retention period in secs

job 数据保留时长(社区版最多 30 天)

Audit log retention (days)

审计日志保留天数

Maximum token expiration (days)

token 最长有效期

Instance object storage

S3 / Azure Blob / AWS OIDC / GCS

Critical alert channels

Email / Slack / Microsoft Teams 告警通道(Email 需先配 SMTP)

SMTP

Host / Port / Username / Password / From Address / Implicit TLS;也可在 .env 用 SMTP_HOST、SMTP_PORT、SMTP_USERNAME、SMTP_PASSWORD、SMTP_FROM(该方式已废弃)

Single Sign On (SSO)

Google / Microsoft / Okta / GitHub / GitLab / Jumpcloud / Keycloak / Authentik / Authelia / Kanidm / Zitadel / Nextcloud / PocketID / 自定义

OAuth(资源用)

Slack、GitHub、GitLab、Bitbucket、Google Sheets、GDrive、Gmail、GCalendar、GForms、GCloud、GWorkspace、Basecamp、Notion、LinkedIn、QuickBooks、Visma、Snowflake、Teams、自定义

SCIM/SAML

SAML metadata(URL 或 XML 内容);group 属性名;内网 IdP 需 ALLOW_PRIVATE_SAML_METADATA_URLS=true

UV / npm / Bun / Maven / Cargo / Nuget 注册表

私有包源;可针对 workspace 覆盖

Sync resource types from the hub daily

每天从 WindmillHub 同步资源类型

Disable hub

断开 Hub(Hub 不可达时可用它消除警告)

AI / OpenTelemetry / Prometheus / Indexer / Telemetry

AI_HTTP_HEADERS、OTEL tracing、Prometheus、全文索引参数、遥测开关

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 场景一:第一次登录与初始化

  1. 打开 http://localhost ,用 admin@windmill.dev / changeme 登录(即使配了 OAuth 也可以用这个账号登录)。

  2. 会被重定向到 Instance settings 页面,之后可以随时改。

  3. 设置一个新的账号来覆盖默认 superadmin 账号;这一步还会从 Windmill Hub 同步资源类型,并可设为每天自动同步。

  4. 最后创建一个 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}

资源里引用其它值(在资源表单里填):

占位符

含义

$var:<NAME_OF_VAR>

插入一个 Variable 的值

$res:<RESOURCE_PATH>

内嵌另一个 Resource 的值

$WM_*

上下文变量,如 $WM_TOKEN、$WM_JOB_ID、$WM_WORKSPACE

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。

Flow 编辑器

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(双向自动同步)

  1. workspace settings → Git Sync tab → + Add connection。

  2. 创建或选择 git_repository 资源,三种认证方式: - GitHub App(推荐,Windmill 托管或实例自托管); - GitLab(Enterprise); - Personal Access Token:URL 形如 https://[USERNAME]:[TOKEN]@github.com/[ORG|USER]/[REPO_NAME].git。

  3. 若仓库是空的,用 "Initialize Git repository" 自动生成 wmill.yaml 并推送全部内容。

  4. Git sync 的过滤设置来自仓库里的 wmill.yaml,改完点按钮重新拉取即可。

  5. 支持 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 的情况。

  • 依赖解析由 dependency tag 的 job 完成;Enterprise 可用 "Dependency job tag" 把所有依赖 job 收敛到同一个 tag(如 deps-$workspace)。

8.9 场景九:自建 App 界面

在 App editor 里把脚本 / flow 拖成组件,绑定输入输出,即可做出内部工具页面;也可做成公开 App 供无账号访客(guest)访问。

App 编辑器

九、全套报错故障排查

#

现象

原因

解决

1

Bind for 0.0.0.0:80 failed: port is already allocated

宿主 80 端口被占用(Nginx / Apache / 其它容器)

改 compose 里 caddy 的 ports 为 8000:80;或停掉占用进程 sudo lsof -i :80

2

打开页面空白 / LSP 无智能提示

直接访问 8000 端口绕过了 caddy,LSP(3001)没被代理

走 caddy;或反代时加上 /ws/*、/ws_mp/*、/ws_debug/* 的 WebSocket 转发

3

role "windmill_admin" does not exist

托管 Postgres 下数据库用户不是 superuser,无法创建 BYPASSRLS 角色

用有建角色权限的账号执行官方 init-db-as-superuser.sql,再 GRANT windmill_admin TO <user>、GRANT windmill_user TO <user>

4

permission denied for schema public

托管库剥离了 public schema 的默认 USAGE 授权

用特权角色执行 GRANT USAGE ON SCHEMA public TO windmill_user, windmill_admin;;或让数据库归属于 Windmill 用户

5

升级到 postgres:18 后 db 容器直接退出

旧 db_data 是 16 的集群,18 读不了

按官方《Upgrade PostgreSQL to 18》做整个集群的 pg_dumpall 迁移;不要只 dump windmill 库(还有 datatable / DuckLake / wm_fork_*)

6

任务一直排队不执行

没有 worker 监听对应 tag

检查 Workers 页面;确认 tag 覆盖(自定义组必须覆盖全部语言 tag);WORKER_TAGS 是否配了

7

native 组任务永远不跑

把 native 组副本设为 0,却没把 native tag 分给其它组

保留 windmill_worker_native 或把 nativets 等 tag 加到其它组

8

worker 起来了但立刻退出

DATABASE_URL 写错 / 数据库不可达

docker compose logs windmill_worker;在 worker 容器里 pg_isready 验证连通性

9

提示 license 无效 / EE 功能不可用

未设置或过期

社区版镜像不含 EE;EE 需要 WM_IMAGE=ghcr.io/windmill-labs/windmill-ee:main 并在实例设置填 License key;key 通常 35 天有效,订阅有效时每天自动续期

10

token 过期 / 401 Unauthorized

MAX_SESSION_VALIDITY_SECONDS 默认 3 天无活动即失效

重新生成 token;在实例设置调大 Maximum token expiration (days);配好 SMTP 后 token 到期前 7 天会邮件提醒

11

OAuth / SSO 登录后回调失败

Base url 设错

实例设置里把 Base url 改成实际域名(含 https),并保证与 IdP 的 Redirect URI 完全一致(如 https://<INSTANCE>/user/login_callback/google)

12

自建 CA 环境下 HTTPS 请求失败

容器内未信任企业 CA

挂载 .crt 到 /usr/local/share/ca-certificates,worker 设 RUN_UPDATE_CA_CERTIFICATE_AT_START=true;Deno 设 DENO_TLS_CA_STORE=system,mozilla;Python 设 REQUESTS_CA_BUNDLE / SSL_CERT_FILE;Java 用 keytool 导入

13

Job 报 OOM / 被系统杀掉

内存不足或 oom_score_adj 不合理

提高 worker 内存 limit;调整 JOB_OOM_SCORE_ADJ(0~1000);若用 earlyoom / systemd-oomd,把它降到 300

14

Python 依赖解析失败 / 超慢

私有源未配或预装包未声明

在实例设置配 UV / pip 索引;用 PIP_LOCAL_DEPENDENCIES 跳过预装包;PY_CONCURRENT_DOWNLOADS 调并发

15

Go 编译把 worker 打挂

Go 编译内存无上限

设 GO_BUILD_MEMLIMIT(如 2GiB),它是软限制,超了会变慢而非 OOM

16

僵尸任务反复重启

worker 崩溃后任务无心跳

60 秒后判定为僵尸,默认原地重启最多 3 次;修 worker 稳定性;调 ZOMBIE_JOB_TIMEOUT / RESTART_ZOMBIE_JOBS=false

17

脚本里拿不到某个环境变量

worker 环境变量默认不透传给 job

用 WHITELIST_ENVS(会警告疑似 secret 的名字);敏感值请放 Variable / Resource

18

平台要求非 root 运行

云厂商限制

用 windmill 用户(uid/gid 1000)或 --user windmill;Helm 用 podSecurityContext.runAsUser / runAsNonRoot

19

Windows worker 启动失败

PID namespace 隔离是 Linux-only

ENABLE_UNSHARE_PID=false;可加 LIMIT_WINDOWS_TO_1CU=true

20

磁盘被日志撑满

日志未轮转或保留期过长

compose 已配 max-size: 20m / max-file: 10;用 LOG_MAX_SIZE / LOG_MAX_FILE 调;调 Retention period in secs(社区版上限 30 天)

21

数据库连接耗尽

连接池不够

调 DATABASE_CONNECTIONS(Server 默认 50 / Worker 默认 3);用 /api/health/detailed 看连接池与队列深度

22

前端 WebSocket 连不上(LSP / multiplayer / debugger)

WebSocket 服务不在同一 host

实例设置里填 WebSocket base url(如 wss://ws.example.com),用页面上的 "Test connectivity" 验证

23

从 Hub 拉取资源类型失败

Hub 不可达

实例设置打开 Disable hub 消除警告,或手工导入资源类型

24

wmill sync push 删掉了远端内容

sync 是全量覆盖语义

只在 Git 版本控制的目录里操作;先用 wmill sync pull 对齐;用 includes / excludes 限定范围

25

内网 Git 仓库被拒绝

默认禁止用 Windmill 探测内网

服务端设 ALLOW_LOCAL_GIT_REMOTES=true(只对 server 生效);内网自建 Git 的自动同步默认已允许

26

忘记 superadmin 密码

—

用默认 admin@windmill.dev / changeme 登录(即使配了 OAuth 也保留);或用 SUPERADMIN_SECRET / NO_AUTH=true 临时进入后重置(用完务必关闭 NO_AUTH)

27

实例数据需要彻底重置

—

docker compose down --volumes + docker volume rm -f windmill_db_data,再 docker compose up -d

十、升级、回滚与卸载

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 文件为准,不要想当然)

部分

许可

backend/ 源码

AGPLv3,但 "enterprise" 编译开关下的片段与文件属于专有商业许可

frontend/ 源码

AGPLv3,但需要正向 license 校验才能激活的片段属于专有商业许可

python-client/ deno-client/ go-client/ powershell-client/

Apache 2.0

OpenAPI 文件(含 OpenFlow spec)

Apache 2.0

不带 enterprise feature flag 从源码编译出的二进制

开源,AGPLv3

ghcr.io/windmill-labs/windmill 镜像与 GitHub Release 二进制里的 Community Edition

含 AGPLv3 + Apache 2.0 代码,同时包含专有的非公开代码与特性

11.2 许可义务与红线

官方 LICENSE 原文要点(意译归纳):

  • 免费可用的:把 Community Edition 原样(as is) 用于组织内部,或通过其 API 原样调用 —— 不需要商业许可。

  • 需要商业许可的:

  • 销售、转售,或把它作为托管服务提供;

  • 修改或包装(modify or wrap)Community Edition;

  • 把 Windmill 的任何部分直接重新暴露给你的用户作为你自己产品的一个功能(iframed 的公开 App 除外);

  • 在 Community Edition 之上构建你售卖的商业功能,或把它嵌入可分发的产物 / 二进制。

  • 源码路线:如果从本仓库源码编译(不带 enterprise flag),你要么遵守 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

评论交流

文章目录