# Windmill > Windmill is an open-source and self-hostable workflow engine and developer platform to build, orchestrate, and monitor internal software at scale. Build workflows, internal tools, AI agents and automations. Full flexibility of code with 20+ languages, public and private library imports, and a local development experience optimized for AI (Claude Code, Cursor, CLI & VS Code extension). Enterprise-grade integrations with Postgres, Snowflake, Kafka, DuckDB and 50+ services. Enterprise-grade security with SSO, SAML, SCIM and audit logs. Built in Rust, optimized for scale. Website: https://www.windmill.dev Documentation: https://www.windmill.dev/docs GitHub: https://github.com/windmill-labs/windmill Cloud: https://app.windmill.dev Hub: https://hub.windmill.dev OpenAPI: https://app.windmill.dev/openapi.html This file contains the entire Windmill documentation as a single document. A curated per-page index with one-line descriptions is available at https://www.windmill.dev/llms.txt. ## Browser automation Source: https://www.windmill.dev/docs/advanced/browser_automation # Browser automation Windmill makes it easy to perform browser automation tasks, such as testing or web scraping. :::info Not sure what a worker group is? You should probably [read about it first](../../core_concepts/9_worker_groups/index.mdx). ::: By default, a worker group named `reports` is available which will handle jobs with the `chromium` tag. Workers assigned to this group will install chromium on start (learn more about [init scripts](../../core_concepts/9_worker_groups/index.mdx#init-scripts)). You have to set the worker group of at least one worker to `reports`. There is a sample worker container definition called `windmill_worker_reports` in the `docker-compose.yml` file which you can uncomment to quickly start a worker with the right worker group. The chromium binary will be available on these workers at `/usr/bin/chromium`. You will need to disable the sandbox to run it inside windmill workers. You can do this by passing the `--no-sandbox` flag. :::caution Running chromium without the sandbox is a security risk. Make sure you trust the website you are visiting. ::: To run jobs on a chromium-equipped worker, you have to select the `chromium` tag in the settings of the script or flow step. [Learn how here](../../core_concepts/9_worker_groups/index.mdx). ## Examples ### Playwright (Bun) ```typescript const page = await browser.newPage(); await page.goto("https://google.com"); const title = await page.title(); await browser.close() return title } ``` ### Puppeteer (Bun) ```typescript const page = await browser.newPage(); await page.goto("https://google.com"); const title = await page.title(); await browser.close(); return title; } ``` --- ## Canonical deployment setups Source: https://www.windmill.dev/docs/advanced/canonical_deployment_setups # Collaboration and deployment stages This page describes how to set up collaboration and deployment in Windmill as a progression of four stages, from simplest to most advanced. Each stage is additive: it builds on the previous one and adds one capability. Most teams settle at stage 2 or stage 3 — stage 4 is only needed when you need a separate production environment with a promotion workflow. | Stage | What you add | Git required | Forks | Workspaces | |-------|--------------|--------------|-------|------------| | [Stage 1](#stage-1--shared-workspace) | Shared workspace, `u/` → `f/` | No | No | 1 | | [Stage 2](#stage-2--add-forks-and-the-merge-ui) | Forks, merge UI, protection rulesets | No | Yes | 1 | | [Stage 3](#stage-3--add-git-sync) | Bi-directional git sync, local development | Yes | Yes | 1 | | [Stage 4](#stage-4--add-multi-workspace-promotion) | Multi-workspace (dev/staging/prod) | Yes | Yes | 2+ | :::info Edition requirements - **Stage 1** works on any edition. - **Stage 2** relies on [workspace forks](../20_workspace_forks/index.mdx) and [protection rulesets](../../core_concepts/56_protection_rulesets/index.mdx). Forks are available on all self-hosted editions but count toward the global 3-workspace limit on Community Edition. - **Stage 3** adds [git sync](../11_git_sync/index.mdx), available on [Cloud and Enterprise Self-Hosted](/pricing), and on Community Edition for workspaces with **up to 2 users**. - **Stage 4** requires git sync and is [Cloud or Enterprise Self-Hosted](/pricing) only. ::: ## Stage 1 — Shared workspace Everyone on the team works in the same workspace. Developers iterate in their own [user space](../../core_concepts/16_roles_and_permissions/index.mdx#path) (`u//`) and, once an item is ready to be shared, move it to a shared [folder](../../core_concepts/8_groups_and_folders/index.mdx) (`f//`). This is the simplest possible setup and the right starting point for almost every team. ### Architecture - One workspace (for example `main`) - Developers own `u//` and iterate there - Shared items live under `f//`, with folder-level [permissions](../../core_concepts/16_roles_and_permissions/index.mdx) granting write access to the relevant group - No git, no forks, no protection rulesets ### Setup 1. Create a workspace and invite your team. 2. Create one or more [folders](../../core_concepts/8_groups_and_folders/index.mdx#folders) that represent your projects, and assign write permission to the groups that should own each project. 3. That's it — developers can start building. ### Developer workflow 1. A developer creates a script, flow or app under `u//`. Items in user space are private to them by default. 2. They iterate freely, test, and share drafts with teammates via the [draft system](../../core_concepts/0_draft_and_deploy/index.mdx). 3. When the item is ready, they move it from `u//` to the appropriate `f//`. Moving an item into a folder transfers ownership to that folder's permissions. 4. Other members of the folder's group can now edit, run, and depend on the item. ### When to move to stage 2 - Someone broke an item in the shared workspace and you wish it had been isolated. - You want a review gate before changes reach the shared workspace. - Developers want sandboxes to test risky changes without affecting anyone else. ## Stage 2 — Add forks and the merge UI Stop editing the shared workspace directly. Instead, developers create a [workspace fork](../20_workspace_forks/index.mdx) for each change, iterate in the fork, and merge back via the [merge UI](../20_workspace_forks/index.mdx#merge-workspaces-from-the-ui-merge-ui). [Protection rulesets](../../core_concepts/56_protection_rulesets/index.mdx) enforce this: direct deploys to the shared workspace are blocked except for a small `wm_deployer` group. This is still a pure UI workflow — no git, no CI/CD, no external tools. Stage 3 will add git sync on top, and forks integrate seamlessly with it when you get there. :::note Forks are optional Forks are not a prerequisite for stage 3. Git sync works on a plain shared workspace, so if you already want git history and local development, you can skip this stage and go straight to [stage 3](#stage-3--add-git-sync). Adopt forks later if you need isolated sandboxes and a review gate. ::: ### Architecture - One shared workspace (same as stage 1), now called `prod` or `main` - [Protection rulesets](../../core_concepts/56_protection_rulesets/index.mdx) block direct deploys; a `wm_deployer` group has bypass permissions - Developers create [workspace forks](../20_workspace_forks/index.mdx) to develop features - Changes merge back through the [merge UI](../20_workspace_forks/index.mdx#merge-workspaces-from-the-ui-merge-ui) ![UI only forks](./workspace_forks_ui_only.png) ### Setup #### 1. Create the `wm_deployer` group 1. Open the workspace settings and go to **Groups**. 2. Create a group called `wm_deployer` and add the users who are allowed to deploy directly or approve merges. #### 2. Enable protection rulesets 1. Go to **Workspace settings** → **Protection Rulesets**. 2. Add a rule that enables **Disable direct deployment**. 3. Add the `wm_deployer` group to the bypass list. This blocks everyone except the deployer group from modifying `f/` items directly. Developers must now use forks. ### Developer workflow 1. A developer creates a [workspace fork](../20_workspace_forks/index.mdx#fork-creation) from the shared workspace via the workspace menu. 2. They iterate, test, and deploy freely *inside* the fork — triggers are not copied over to avoid unwanted executions. 3. When ready, they open the fork home page, click **Review & Deploy Changes**, and review the diff in the [merge UI](../20_workspace_forks/index.mdx#merge-workspaces-from-the-ui-merge-ui). 4. A member of `wm_deployer` approves the merge. The changes land in the shared workspace. ### When to move to stage 3 - You want a history of changes outside Windmill (audit, backup, rollback). - You want to edit scripts and flows locally in your IDE or with [agentic coding tools](../../misc/9_guides/local_dev_with_ai/index.mdx) like Claude Code. - You want fork branches mirrored in git so PRs can be reviewed there too. - You're planning to add a separate production environment (stage 4), which requires git sync. ## Stage 3 — Add git sync Connect the workspace from stages 1 and 2 to a git repository using [git sync](../11_git_sync/index.mdx). Every deploy is committed to git, and changes pushed to the repository are synced back to Windmill via CI/CD. This gives you a full audit trail, external version control, and [local development](../4_local_development/index.mdx) — and each fork from stage 2 now automatically gets its own git branch, so the merge UI and git PRs stay in lockstep. Git sync is available on [Cloud and Enterprise Self-Hosted](/pricing), and on Community Edition for workspaces with up to 2 users. ### Architecture - The shared workspace and its forks from stage 2, unchanged - A git repository with a single branch (for example `main`) - [Git sync](../11_git_sync/index.mdx) configured on the workspace: each deploy pushes a commit - A CI/CD workflow that syncs commits back to the workspace, enabling bi-directional sync - A `wmill.yaml` file in the repository with a single entry in the [`workspaces:`](../3_cli/environment-specific-items.mdx) key - Fork branches auto-created as `wm-fork//` ![Workspace Forks + git sync](./workspace_forks.png) ### Setup #### 1. Create the git repository Create an empty repository on GitHub, GitLab or similar with a single `main` branch. #### 2. Configure git sync on the workspace 1. Go to **Workspace settings** → **Git Sync**. 2. Click **+ Add connection** and create a [`git_repository`](../../integrations/git_repository.mdx) resource pointing at your repository and the `main` branch. 3. Save. Use **Initialize Git repository** to populate the repository with the current workspace contents and a default `wmill.yaml`. See the full walkthrough in [git sync setup](../11_git_sync/index.mdx#setup---git-sync-from-windmill). #### 3. Add the CI/CD workflows Git sync only pushes *from* Windmill *to* git by default. To make changes in git flow back into the workspace, add GitHub Actions workflows (or equivalent). A working reference is the [windmill-sync-example](https://github.com/windmill-labs/windmill-sync-example) repository. You will want: - [`push-on-merge.yaml`](../11_git_sync/index.mdx#setup---cicd-from-git-repository) — runs `wmill sync push` when commits land on `main`. - [`push-on-merge-to-forks.yaml`](../11_git_sync/index.mdx#setup---cicd-from-git-repository) and [`open-pr-on-fork-commit.yaml`](../11_git_sync/index.mdx#setup---cicd-from-git-repository) — keep fork branches in sync and open PRs automatically. Without the fork workflows, forks still work exactly as in stage 2 — their git branches just won't receive external edits. You will need: - A [Windmill user token](../../core_concepts/4_webhooks/index.mdx#user-token) saved as a `WMILL_TOKEN` GitHub secret. - The `WMILL_WORKSPACE` and `WMILL_URL` variables set in the workflows. Full details in [git sync CI/CD setup](../11_git_sync/index.mdx#setup---cicd-from-git-repository). #### 4. Minimal `wmill.yaml` A single-workspace `wmill.yaml` looks like this: ```yaml defaultTs: bun includes: - f/** excludes: [] skipVariables: true skipResources: true skipSecrets: true workspaces: main: baseUrl: https://app.windmill.dev gitBranch: main ``` See [workspace-specific items](../3_cli/environment-specific-items.mdx) for the full schema. ### Developer workflow 1. Developers continue to use forks from stage 2. When they create a fork, a matching `wm-fork/main/` git branch is created automatically. 2. They can now also edit the fork locally: [work on the workspace outside of Windmill](../4_local_development/index.mdx) with `wmill sync pull`, edit in their IDE, the [VS Code extension](../../cli_local_dev/1_vscode-extension/index.mdx), or an [agentic coding tool](../../misc/9_guides/local_dev_with_ai/index.mdx) like Claude Code, then `wmill sync push` back. 3. Every deploy creates a commit in the git repository. 4. Merging a fork via the merge UI *or* via a normal git PR both work — pick whichever your team prefers for review. 5. External commits (from local development or PR merges) are replayed into the workspace by the CI/CD workflow. ### When to move to stage 4 - You need a separate production environment that is fully isolated from day-to-day development. - You want PR-based review on the *promotion* step, not just the merge-to-shared step. - You're deploying across multiple Windmill instances. ## Stage 4 — Add multi-workspace promotion Introduce one or more additional workspaces (for example `staging` and `prod`), each connected to its own git branch. Day-to-day development happens on `staging` using forks (exactly as in stage 3). When changes are ready for production, they are promoted from `staging` to `prod` through [git promotion](../9_deploy_gh_gl/index.mdx), which opens a PR on the `prod` branch. The `prod` workspace is never edited directly. Its only source of change is a merged PR on the `prod` branch. ### Architecture - Two (or more) workspaces, each on its own git branch - Each workspace has its own `git_repository` resource and its own entry under `workspaces:` in `wmill.yaml` - [Git Promotion](../11_git_sync/index.mdx#git-promotion-workflow-cross-instance-deployment-using-a-git-workflow) is configured from `staging` to `prod` - Developers edit `staging` via [forks](../20_workspace_forks/index.mdx) (stage 2 pattern) - `prod` is protected by [protection rulesets](../../core_concepts/56_protection_rulesets/index.mdx) and only the promotion CI/CD can deploy to it | Workspace | Git branch | Promotes to | |-----------|------------|-------------| | staging | staging | prod | | prod | prod | — | ![Git Promotion](./git_promotion.png 'Edits to staging use the stage 2 fork workflow') ### Setup #### 1. Create the branches and workspaces 1. In your git repository, create a `staging` branch and a `prod` branch. 2. Create a `staging` workspace and a `prod` workspace in Windmill. #### 2. Configure git sync on each workspace For each workspace, go to **Workspace settings** → **Git Sync** and create a `git_repository` resource pointing at the corresponding branch: - `staging` workspace → `git_repository` on `staging` branch - `prod` workspace → `git_repository` on `prod` branch #### 3. Configure the promotion target On the `staging` workspace: 1. Under the git sync connection, click **Add promotion target**. 2. Create a **separate** `git_repository` resource targeting the `prod` branch (this is not the same resource as the one used for git sync). 3. Save. :::caution Two resources are required The git sync resource points at the workspace's *own* branch (`staging`). The promotion target points at the *target* branch (`prod`). They must be different `git_repository` resources, even if they point at the same repository. ::: #### 4. Add the CI/CD workflows - `push-on-merge.yaml` — one copy per workspace (`WMILL_WORKSPACE=staging`, `WMILL_WORKSPACE=prod`), triggered by merges to `staging` and `prod` respectively. - `open-pr-on-promotion-commit.yaml` — opens a PR on `prod` when staging promotes a change. - `push-on-merge-to-forks.yaml` and `open-pr-on-fork-commit.yaml` — unchanged from stage 3, now scoped to the `staging` workspace. See [git sync CI/CD setup](../11_git_sync/index.mdx#setup---cicd-from-git-repository) for the workflow files. #### 5. Multi-workspace `wmill.yaml` ```yaml defaultTs: bun includes: - f/** excludes: [] workspaces: staging: baseUrl: https://app.windmill.dev gitBranch: staging specificItems: resources: - "f/config/**" settings: true prod: baseUrl: https://app.windmill.dev gitBranch: prod specificItems: resources: - "f/config/**" settings: true ``` Each workspace declares its own `gitBranch`. Items listed under `specificItems` (for example, environment-specific database credentials) are stored per workspace on disk. See [workspace-specific items](../3_cli/environment-specific-items.mdx) for the full schema. :::info Per-folder ownership defaults on deploy By default, a newly deployed script, flow, app, schedule or trigger runs as whichever user pushed it. For CLI / CI/CD deploys that's the identity behind `WMILL_TOKEN` — typically a workspace admin or a dedicated deploy user that you've added to `wm_deployers`. If you want items under a folder to run as a specific service account instead — and to differ per environment — set `default_permissioned_as` rules on the folder in the target workspace: ```yaml # f/customer_x/folder.meta.yaml — committed per workspace via specificItems summary: '' display_name: customer_x owners: [] extra_perms: {} default_permissioned_as: - path_glob: 'jobs/**' permissioned_as: u/customer_x_svc # or g/customer_x or an email - path_glob: '**' permissioned_as: u/ops ``` Rules are ordered; the first `path_glob` (relative to the folder root) that matches an item wins. Applied only at **create time** and only when the caller is admin or a member of `wm_deployers` — existing items are untouched, and non-deployer users still deploy as themselves. Set `folders: ["f/**/folder.meta.yaml"]` under `specificItems` in `wmill.yaml` if you want different rules per workspace on the same path. Edit the rules in the folder settings UI, or commit them directly in `folder.meta.yaml`. The CLI also exposes per-item overrides for already-deployed items via `wmill script|flow|app|schedule|trigger set-permissioned-as `. Folder defaults are advisory: they only fill in the default when the pushed item does not already carry an explicit `on_behalf_of`. To opt in to CLI preview of which items will be affected by rules on the next push, set `syncBehavior: v1` in `wmill.yaml`. ::: #### 6. Protect `prod` Enable [protection rulesets](../../core_concepts/56_protection_rulesets/index.mdx) on the `prod` workspace with no bypass group — the only way in is the promotion PR. ### Developer workflow 1. A developer needs to change something. They [fork](../20_workspace_forks/index.mdx) the **staging** workspace (not prod). 2. They iterate in the fork and merge back to `staging` via the merge UI. This is the stage 2 workflow plus the git sync integration from stage 3. 3. When the change needs to go live, they trigger a [git promotion](../9_deploy_gh_gl/index.mdx) from `staging` to `prod`. 4. A PR is opened on the `prod` branch. The team reviews it in their git platform. 5. Merging the PR triggers the `push-on-merge.yaml` workflow, which syncs `prod` to the new state. ### Advantages - Fully isolated production environment with a git PR as the only gate. - Dev/staging/prod across multiple Windmill instances if needed. - Per-workspace configuration via [workspace-specific items](../3_cli/environment-specific-items.mdx). - All the benefits of stages 1–3 still apply. ## Choosing a stage Most teams should start at **stage 1** and only move up when they feel the pain the next stage solves. Stage 4 is real operational overhead and should not be the default — the majority of teams are well served by stages 2 or 3. - **Stage 1** if you have a small team, no compliance requirements, and a single environment is fine. - **Stage 2** if you want enforced review on every change and isolated sandboxes, but don't need external version control. - **Stage 3** if you want git history, backup, local development, and the option of git PR-based review on top of stage 2. - **Stage 4** if you need a separate production environment with a promotion gate, or you deploy across multiple Windmill instances. Stages 1 → 2 → 3 → 4 are designed to be adopted in order, but you can skip stages: a small team on Community Edition can go stage 1 → stage 3 (skip forks) to get git sync and local development without needing fork-based review. ## Related documentation --- ## Ci tests Source: https://www.windmill.dev/docs/advanced/ci_tests # CI test scripts Add a `test:` annotation at the top of any script to turn it into a CI test. When the tested [script](../../getting_started/0_scripts_quickstart/index.mdx) or [flow](../../getting_started/6_flows_quickstart/index.mdx) is deployed, the test runs automatically. ## Writing a test script ### Single target ```typescript // test: script/u/admin/my_script if (result !== 42) { throw new Error(`Expected 42, got ${JSON.stringify(result)}`); } return result; } ``` ### Multiple targets ```typescript // test: // script/u/admin/script_a // script/u/admin/script_b // flow/u/admin/my_flow export async function main() { // test all items } ``` ### Wildcards Annotations support glob patterns so a single test script can cover a whole folder tree: - `*` matches one path segment. - `**` matches any depth. ```typescript // test: script/u/admin/* // test: flow/u/team/** ``` The first pattern matches every script directly under `u/admin/`. The second matches every flow at any depth under `u/team/`. Wildcards can be combined with multi-target annotations on separate lines. ### Branching on the triggering runnable CI test jobs receive a `WM_TESTED_RUNNABLE` environment variable containing `{kind}/{path}` of the runnable that triggered the test (for example, `script/u/admin/my_script`). This lets a single test script cover multiple targets and branch on which one fired it. ```typescript // test: script/u/admin/* export async function main() { const tested = process.env.WM_TESTED_RUNNABLE!; // e.g. "script/u/admin/my_script" const path = tested.replace(/^script\//, ""); return await wmill.runScript(path, {}); } ``` ### Python ```python # test: script/u/admin/my_script import wmill def main(): result = wmill.run_script("u/admin/my_script", {}) assert result == 42 return result ``` In Python, the triggering runnable is exposed as `os.environ["WM_TESTED_RUNNABLE"]`. The annotation uses the comment syntax of each language (`//` for TypeScript, `#` for Python, etc.). The script creation page also provides ready-made CI test templates for TypeScript and Python. ## Where results appear Test scripts show a yellow **CI test** badge in the script list and detail page. On the detail page of the tested script or flow, each test is listed with its status (pass, fail, or running) and a link to the job run. Results auto-refresh while tests are running. In [workspace forks](../20_workspace_forks/index.mdx), CI results are also visible: - The fork banner shows a summary of passing, failing, and running tests across all changed items. - On the [comparison page](../20_workspace_forks/index.mdx#merge-workspaces-from-the-ui-merge-ui), each changed item displays a CI badge, and a test summary lists all test scripts with their latest results. --- ## Cli Source: https://www.windmill.dev/docs/advanced/cli # Command-line interface (CLI) The Windmill CLI, `wmill` allows you to interact with Windmill instances right from your terminal. You can also use it for various automation tasks, including [syncing](./sync.mdx) folders & [GitHub repositories](./sync.mdx), or just running all you scripts and flows. See [Installation](./installation.md) for getting started. ## Unified `list` / `get` / `new` subcommands Every item type supports the same set of subcommands so the CLI can be used as a full API client in shell scripts — piping JSON output to `jq`, etc. | Subcommand | Behavior | | ---------- | -------- | | ` list [--json]` | Lists items. With `--json`, outputs machine-readable JSON. | | ` get [--json]` | Pretty-prints an item. With `--json`, outputs the full API response. | | ` new [...]` | Bootstraps a local template file for the item. `bootstrap` remains as an alias for script/flow. | The supported types are: `script`, `flow`, `app`, `resource`, `resource-type`, `variable`, `schedule`, `folder`, and `trigger`. For `trigger`, pass `--kind ` (e.g. `http`, `websocket`, `kafka`, ...) to `new` and to `get` when multiple trigger kinds share a path. ```bash wmill script list --json | jq '.[].path' wmill trigger new f/http/webhook --kind http wmill resource get f/db/prod --json | jq .value.host ``` ## `wmill docs` The `wmill docs` command searches the Windmill documentation from your terminal. It calls the backend search endpoint and prints formatted results. ```bash wmill docs [--json] ``` ### Example ```bash wmill docs "how do I create a flow?" wmill docs "what are resources?" --json | jq .answer ``` Documentation search is an [Enterprise Edition](/pricing) feature — on a Community Edition instance, the command prints a message indicating EE is required. ## Using the CLI with AI coding assistants If you drive the CLI from Claude Code, Codex, Cursor, or a similar AI coding assistant, you can give the agent up-to-date `wmill` command reference by adding the [Context7](https://context7.com) plugin and pointing it at the Windmill CLI docs index at [`context7.com/windmill-labs/windmill-cli-docs`](https://context7.com/windmill-labs/windmill-cli-docs). See [Local development with AI](../../misc/9_guides/local_dev_with_ai/index.mdx) for the full local AI workflow, including `wmill init`, the VS Code extension, and the MCP server. --- ## App Source: https://www.windmill.dev/docs/advanced/cli/app # Apps ## Listing apps The `wmill app` list command is used to list all apps in the remote workspace. ```bash wmill app ``` ## Pushing an app Pushing an app to a Windmill instance is done using the `wmill app push` command. ```bash wmill app push ``` ### Arguments | Argument | Description | | ----------- | --------------------------------- | | `file_path` | The path to the app file to push. | ### Examples 1. Push the app located at `./my_app.json`. ```bash wmill app push ./my_app.json ``` ## Full-code app commands The CLI provides additional commands for [full-code apps](/docs/full_code_apps): ### Create a new full-code app ```bash wmill app new ``` Interactive wizard to scaffold a full-code app with React, Svelte or Vue. ### Start the dev server From the app directory: ```bash wmill app dev ``` Starts a local development server with hot reload and WebSocket backend. Options: `--port`, `--host`, `--entry`, `--no-open`. ### Generate lock files Generate `.lock` files for backend runnables with dependencies using the [`wmill generate-metadata`](./generate-metadata.md) command: ```bash wmill generate-metadata ``` To only update app lockfiles, use: ```bash wmill generate-metadata --skip-scripts --skip-flows ``` Options: `--yes`, `--dry-run`, `--default-ts`. :::info Legacy command Prior to the unified command, this was done with `wmill app generate-locks`. This command is now deprecated but still works. ::: ### Generate agent documentation From the app directory: ```bash wmill app generate-agents ``` Generates `AGENTS.md` and `DATATABLES.md` for AI coding agent context. ## Remote path format ```js //... ``` --- ## Datatable Source: https://www.windmill.dev/docs/advanced/cli/datatable # Datatables The `wmill datatable` commands let you query and manage [data tables](../../core_concepts/11_persistent_storage/data_tables.mdx) from the CLI. You can list available datatables, run SQL queries, or start an interactive PostgreSQL session. ## Listing datatables List all datatables in the current workspace. ```bash wmill datatable list [options] ``` ### Options | Option | Description | | -------- | --------------------------------- | | `--json` | Output as JSON (for piping to jq) | ### Example ```bash wmill datatable list ``` Output: ``` +------------+---------------+---------------+ | Name | Resource Type | Resource Path | +------------+---------------+---------------+ | main | postgresql | f/db/main | | analytics | postgresql | f/db/stats | +------------+---------------+---------------+ ``` ## Running a query Execute a SQL query against a datatable and display results. ```bash wmill datatable run [options] ``` ### Arguments | Argument | Description | | -------- | ---------------------------- | | `sql` | The SQL query to execute | ### Options | Option | Parameters | Description | | ------------------- | ---------- | ----------------------------------------------------- | | `-n, --name` | `name` | Datatable name to query (default: `main`) | | `-s, --silent` | | Output only the final result as JSON (for scripting) | ### Examples 1. Basic query: ```bash wmill datatable run "SELECT * FROM users LIMIT 10" ``` 2. Query a specific datatable: ```bash wmill datatable run -n analytics "SELECT COUNT(*) as count FROM events" ``` 3. Silent mode for scripting: ```bash wmill datatable run -s "SELECT version()" | jq '.version' ``` ## Starting a PostgreSQL proxy server Start a PostgreSQL wire-protocol proxy that serves all datatables. This allows any Postgres-compatible client (psql, DBeaver, pgAdmin, etc.) to connect and query your datatables. ```bash wmill datatable serve [options] ``` ### Options | Option | Parameters | Description | | ------------ | ---------- | ----------------------------------------------------- | | `--port` | `port` | Port to listen on (default: first available 5433-5500)| | `--host` | `host` | Bind address (default: `127.0.0.1`) | | `--password` | `password` | Connection password (default: randomly generated) | ### Behavior - Implements the PostgreSQL wire protocol (read-only queries only) - Each datatable appears as a separate database in the connection - Supports prepared statements and parameterized queries - Emulates Postgres system tables (like `pg_database`) for tool compatibility ### Example ```bash wmill datatable serve ``` Output: ``` Serving datatables on 127.0.0.1:5433 via Postgres wire protocol Available datatables: psql 'postgresql://wmill:a1b2c3d4e5f6@127.0.0.1:5433/main' psql 'postgresql://wmill:a1b2c3d4e5f6@127.0.0.1:5433/analytics' Press Ctrl+C to stop. ``` Connect with any Postgres client using the connection string shown. ## Interactive psql session Start a proxy server and launch an interactive `psql` session connected to it. ```bash wmill datatable psql [options] ``` ### Options | Option | Parameters | Description | | ------------------- | ---------- | ----------------------------------------------------- | | `-n, --name` | `name` | Datatable to connect to (default: `main`) | | `--port` | `port` | Port for the proxy (default: first available 5433-5500)| | `--host` | `host` | Bind address for the proxy (default: `127.0.0.1`) | | `--password` | `password` | Connection password (default: randomly generated) | ### Prerequisites The `psql` client must be installed: - **Linux**: `apt install postgresql-client` - **macOS**: `brew install libpq` ### Examples 1. Interactive session with the default datatable: ```bash wmill datatable psql ``` 2. Connect to a specific datatable: ```bash wmill datatable psql -n analytics ``` ## Summary | Command | Purpose | | ------- | ---------------------------------------------- | | `list` | List all datatables in the workspace | | `run` | Execute a single SQL query | | `serve` | Start a Postgres proxy for external clients | | `psql` | Launch an interactive psql session | --- ## Dev preview Source: https://www.windmill.dev/docs/advanced/cli/dev-preview # Dev & Preview The CLI can run scripts and flows against a remote workspace without deploying them, and it can serve a live-reload dev page that mirrors your local files. This is useful for iterating on local files, validating codebase scripts, or testing flow / app changes with local inline script modifications. ## `wmill dev` `wmill dev` starts a local live-reload server and opens a Windmill dev page that renders your local flow, script, or raw app. Edits on disk reload the page; edits in the page round-trip back to the local files (`flow.yaml`, inline scripts, `app.yaml`, etc.). ```bash wmill dev [--path ] [--proxy-port ] [--no-open] ``` When invoked from inside a `*__flow/` folder, `wmill dev` auto-detects the wm path. When invoked from a workspace root with no `--path`, the dev page shows a workspace picker — flows, scripts, and raw apps grouped by folder / user, with search and a kind filter. ### Run modes - **Direct** (default): a WebSocket server on a random port. The dev page lives on the remote workspace and connects back to localhost over WebSocket for live reload. This is the mode the [VS Code extension](../../cli_local_dev/1_vscode-extension/index.mdx) iframe and a regular browser tab use. - **Proxy** (`--proxy-port `): a reverse proxy at `http://localhost:/` that 302-redirects `/` to the remote dev page with workspace, auth token, and `--path` baked in, and tunnels other HTTP / WebSocket traffic to the remote workspace. This is the mode [Claude Code](../../misc/9_guides/local_dev_with_ai/index.mdx)'s port-detection preview pane uses. ### Options | Option | Description | | ----------------------- | -------------------------------------------------------------------------------------------- | | `--path ` | Open a specific flow, script, or raw app instead of the workspace picker. | | `--proxy-port ` | Run in proxy mode on the given port; needed for Claude Code's preview pane. | | `--no-open` | Don't open the browser; print the URL so the caller (e.g. an AI agent) can hand it to you. | ### Examples ```bash # Workspace picker wmill dev # Jump straight into a flow wmill dev --path f/my_flows/etl # Auto-detected when run from inside a flow folder cd f/my_flows/etl__flow && wmill dev # Proxy mode for Claude Code's preview pane wmill dev --proxy-port 4000 --path f/my_flows/etl ``` ### Round-trip editing Editing a flow or script from the dev page writes back to disk: - `flow.yaml` is rewritten only when content actually differs. - Inline scripts (`.py`, `.ts`, `.bun.ts`, `.go`, `.sh`, etc.) are kept in sync. - `PathScript` references (modules with `type: 'script'` and a `path:`) survive the round-trip. - Fixture files inside the flow folder (`README.md`, `data.json`, `.env`, etc.) are preserved. When `--path` is set, edits to other files don't push to the page, so you can keep working on unrelated files in the same workspace folder. ## `wmill script preview` Preview a local script file against the remote workspace. Supports both regular scripts and [codebase](../../core_concepts/33_codebases_and_bundles/index.mdx) scripts (which are bundled before running). ```bash wmill script preview [options] ``` ### Options | Option | Description | | ------------------ | ----------------------------------------------------------------- | | `-d, --data `| Inputs as a JSON string, `@filename`, or `@-` for stdin. | | `-s, --silent` | Only output the final result (no logs, useful for scripting). | ### Examples ```bash # Regular script wmill script preview u/admin/my_script.ts --data '{"x": 5}' # Codebase script (bundled before preview) wmill script preview f/codebase_test/my_script.ts --data '{"x": 7}' # Silent mode for piping wmill script preview f/scripts/hello.ts -d '{"n":3}' --silent | jq ``` ## `wmill flow preview` Preview a local flow against the remote workspace. The flow definition is read from the local `.flow` / `__flow` folder — any changes to inline scripts are picked up without deploying. ```bash wmill flow preview [options] ``` ### Options Same `-d, --data` and `-s, --silent` options as `script preview`. ### Example ```bash wmill flow preview f/my_flows/etl__flow --data '{"date":"2026-01-01"}' ``` Use `wmill dev` when you want a live UI that round-trips edits back to disk; use `wmill flow preview` when you want a one-shot run with a specific payload. --- ## Environment specific items Source: https://www.windmill.dev/docs/advanced/cli/environment-specific-items # Workspace-specific items Workspace-specific items let you store different versions of [resources](../../core_concepts/3_resources_and_types/index.mdx), [variables](../../core_concepts/2_variables_and_secrets/index.mdx), triggers, folders, and workspace settings per workspace (dev / staging / prod). The CLI transforms file paths so that each workspace has its own namespaced files locally while keeping clean base paths in the Windmill workspace itself. :::tip Multi-workspace setups are stage 4 of the collaboration guide Using `workspaces:` with multiple entries (one per environment, each with its own `gitBranch`) corresponds to **stage 4** of the [collaboration and deployment stages](../23_canonical_deployment_setups/index.mdx#stage-4--add-multi-workspace-promotion) guide. The single-workspace form of `workspaces:` is used starting at [stage 3](../23_canonical_deployment_setups/index.mdx#stage-3--add-git-sync). ::: This replaces the older split between `gitBranches` (multi-branch) and `environments` (single-branch). Both are now a single `workspaces:` key — see [Migrating from `gitBranches` / `environments`](./sync.mdx#migrating-from-gitbranches--environments). ## How it works When a file matches a pattern in your `wmill.yaml` configuration, the CLI inserts the workspace name into the local file path: - **Local files**: `database.dev.resource.yaml`, `database.prod.resource.yaml` - **Windmill workspace**: `database.resource.yaml` (clean base path) Each workspace has its own version of the same logical resource. In practice, multiple workspace-specific files usually coexist in the repository (e.g. `database.dev.resource.yaml` and `database.prod.resource.yaml` side by side) — by design when one git branch hosts multiple workspaces, or as a result of merging branches when each workspace has its own branch. The CLI only reads the files matching the current workspace and ignores the others. ## Configuration Workspace-specific items are declared inside a `workspaces:` entry, either per workspace under `specificItems`, or shared across every workspace under `commonSpecificItems`. ```yaml workspaces: dev: baseUrl: https://dev.windmill.dev overrides: skipSecrets: true specificItems: resources: - "u/alex/config/**" variables: - "u/alex/env_*" triggers: - "u/alex/kafka_*" folders: - "f/env_*" settings: true prod: baseUrl: https://app.windmill.dev gitBranch: main # workspace "prod" lives on git branch "main" overrides: skipSecrets: false specificItems: resources: - "u/alex/config/**" variables: - "u/alex/env_*" folders: - "f/env_*" settings: true # Items that are workspace-specific across ALL workspaces commonSpecificItems: resources: - "u/alex/config/**" variables: - "u/alex/database_*" folders: - "f/env_*" settings: true ``` The `workspaces` key is the map key (`dev`, `prod`, …) and is also the suffix inserted into file names on disk. `gitBranch` and `workspaceId` default to that key — only set them when they differ. ## Resolving the current workspace `wmill sync pull` and `wmill sync push` pick the workspace in this order: 1. `--workspace ` — explicit override, looks up the entry by key. 2. **Current git branch** — the first entry whose effective `gitBranch` matches `git rev-parse --abbrev-ref HEAD`. 3. No match — top-level sync options are used as-is, no workspace-specific transform. ### One git branch per workspace Each workspace has its own git branch. The CLI detects the current branch automatically and applies the matching entry. ```bash git checkout main wmill sync pull # Uses workspace "prod" (gitBranch: main) # Creates: u/alex/config/database.prod.resource.yaml git checkout dev wmill sync pull # Uses workspace "dev" # Creates: u/alex/config/database.dev.resource.yaml ``` ### Multiple workspaces on one git branch All workspaces live on the same branch and you pick the target with `--workspace`. ```bash wmill sync pull --workspace dev # Creates: u/alex/config/database.dev.resource.yaml wmill sync pull --workspace prod # Creates: u/alex/config/database.prod.resource.yaml wmill sync push --workspace staging ``` No `git checkout` is needed — all workspace-specific files coexist on the same branch. :::info Migrating from `--env` and `--branch` `--branch` and `--env` are still accepted on sync commands but are deprecated — they print a one-time warning and resolve the same way `--workspace` does. Update any scripts or CI jobs to use `--workspace` instead. ::: ## File path transformation ### Transform logic **Pull** (Windmill workspace → local): - `u/alex/database.resource.yaml` → `u/alex/database.prod.resource.yaml` - `u/alex/orders.kafka_trigger.yaml` → `u/alex/orders.prod.kafka_trigger.yaml` - `f/env_staging/folder.meta.yaml` → `f/env_staging/folder.prod.meta.yaml` - `settings.yaml` → `settings.prod.yaml` **Push** (local → Windmill workspace): - `u/alex/database.dev.resource.yaml` → `u/alex/database.resource.yaml` - `u/alex/orders.dev.kafka_trigger.yaml` → `u/alex/orders.kafka_trigger.yaml` - `f/env_staging/folder.dev.meta.yaml` → `f/env_staging/folder.meta.yaml` - `settings.dev.yaml` → `settings.yaml` The suffix is the **workspace name** (the key in `workspaces:`). In migrated configs where the workspace name equals the old branch name, existing files keep working unchanged. ### Resource files Resources can include associated files (certificates, config files, etc.) alongside their YAML definitions. These follow the same transformation: - Base file: `u/alex/certificate.resource.file.pem` - Workspace-specific: `u/alex/certificate.dev.resource.file.pem` When a resource YAML file is marked as workspace-specific, all associated resource files are automatically treated the same way. ### Supported file types - **Variables**: `*.variable.yaml` - **Resources**: `*.resource.yaml` and resource files (`*.resource.file.*`) - **Triggers**: `*.kafka_trigger.yaml`, `*.http_trigger.yaml`, `*.websocket_trigger.yaml`, `*.nats_trigger.yaml`, `*.postgres_trigger.yaml`, `*.mqtt_trigger.yaml`, `*.sqs_trigger.yaml`, `*.gcp_trigger.yaml` - **Schedules**: `*.schedule.yaml` - **Folders**: `folder.meta.yaml` files within folder directories - **Settings**: `settings.yaml` (workspace settings file) ## Pattern matching Patterns support standard glob syntax: - `*` matches any characters within a path segment - `**` matches any characters across path segments - `u/alex/database_*` matches `u/alex/database_config`, `u/alex/database_url`, etc. - `f/environments/**` matches all files under `f/environments/` recursively ## Common specific items Items that should be workspace-specific across all workspaces can be declared once under `commonSpecificItems`: ```yaml workspaces: commonSpecificItems: variables: - "u/alex/database_*" - "f/config/**" resources: - "u/alex/api_keys/**" - "f/environments/**" triggers: - "u/alex/kafka_*" - "f/streaming/**" folders: - "f/env_*" settings: true ``` ## Per-workspace specific items If some resources only exist in certain workspaces, define different patterns per workspace instead of using `commonSpecificItems`: ```yaml workspaces: prod: specificItems: variables: - "u/alex/prod_*" resources: - "u/alex/production/**" triggers: - "u/alex/prod_kafka_*" folders: - "f/prod_config" settings: true dev: specificItems: variables: - "u/alex/dev_*" resources: - "u/alex/development/**" triggers: - "u/alex/dev_kafka_*" folders: - "f/dev_config" settings: true ``` Here, `prod_kafka_*` triggers only get the workspace suffix on `prod` — they don't exist in `dev`, so there's no need to mark them there. Most setups use `commonSpecificItems` since the same resources typically exist across all workspaces. ## Name safety Workspace names containing certain characters are automatically sanitized for filesystem safety: - **Unsafe characters**: `/ \ : * ? " < > | .` - **Replacement**: All unsafe characters are replaced with `_` - **Warning**: The CLI shows a clear warning when sanitization occurs ```bash Warning: Branch name "feature/api-v2.1" contains filesystem-unsafe characters (/ \ : * ? " < > | .) and was sanitized to "feature_api-v2_1". This may cause collisions with other similarly named branches. ``` Prefer simple workspace keys (`dev`, `staging`, `prod`) to avoid collisions. ## Best practices - **Start broad**: Use `commonSpecificItems` for widely-used resources - **Be specific**: Target exact paths rather than overly broad patterns - **Group logically**: Organize patterns by team, workspace, or function - **Define patterns early**: Establish patterns before team members start using them - **Test locally**: Verify workspace-specific behavior works correctly before CI/CD integration ## Troubleshooting ### Files not being detected as workspace-specific 1. **Check patterns**: Ensure your glob patterns match the file paths exactly 2. **Verify file types**: Only the types listed above are supported 3. **Pattern testing**: Use tools like [globster.xyz](https://globster.xyz/) to test your patterns ### Files syncing to the wrong workspace 1. **Check config**: Ensure your `workspaces:` entry is correct 2. **Verify current context**: Make sure you're on the expected git branch or using the right `--workspace` flag 3. **Configuration precedence**: CLI flags override configuration file settings ## Git sync integration When [Git sync](../11_git_sync/index.mdx) is configured, workspace-specific items work seamlessly with your Git repository setup. On each Windmill-triggered sync, the backend invokes the CLI with `--base-url` and `--workspace` set to the current instance and workspace id. The CLI matches these against the `workspaces:` entries in `wmill.yaml` (on `baseUrl` + `workspaceId`) and applies the corresponding entry — no extra configuration on the Windmill side. - **One git branch per workspace**: each Windmill workspace's `git_repository` resource points to a different branch, and the `wmill.yaml` entry for that workspace sets `gitBranch` accordingly. - **Production workspace**: `git_repository` pointing to `repoX` on `main` branch - Changes to `database.resource.yaml` → pushed as `database.prod.resource.yaml` - **Development workspace**: `git_repository` pointing to `repoX` on `dev` branch - Changes to `database.resource.yaml` → pushed as `database.dev.resource.yaml` - **Multiple workspaces on one git branch** (mono-branch): all Windmill workspaces point to the same branch. List each workspace under `workspaces:` with a distinct `workspaceId` (and the same `gitBranch`); the CLI selects the right entry from the `--workspace` flag the backend passes, so no UI toggle is needed. ## Related documentation - [CLI sync](./sync.mdx) - Main sync operations and configuration - [Git sync settings](./gitsync-settings.mdx) - Managing sync configuration - [Git sync](../11_git_sync/index.mdx) - Backend Git integration --- ## Flow Source: https://www.windmill.dev/docs/advanced/cli/flow # Flows ## Listing flows The `wmill flow` list command is used to list all flows in the remote workspace. ```bash wmill flow ``` ## Pushing a flow Pushing a flow to a Windmill instance is done using the `wmill flow push` command. ```bash wmill flow push ``` ### Arguments | Argument | Description | | ------------- | -------------------------------------------------------------- | | `file_path` | The path to the flow file to push. | | `remote_path` | The remote path where the flow specification should be pushed. | ### Examples 1. Push the flow located at `path/to/local/flow.yaml` to the remote path `f/flows/test`. ```bash wmill flow push path/to/local/flow.yaml f/flows/test ``` ## Creating a new flow The wmill flow bootstrap command is used to create a new flow locally. ```bash wmill flow bootstrap [--summary ] [--description ] ``` ### Arguments | Argument | Description | | ---------- | ------------------------------------ | | `path` | The path of the flow to be created. | ### Examples 1. Create a new flow `f/flows/flashy_flow` ```bash wmill flow bootstrap f/flows/flashy_flow ``` ## Running a flow Running a flow by its path is done using the `wmill flow run` command. Logs are streamed step-by-step with labeled headers showing each module's ID and summary. For-loop iterations are tracked individually as they complete. ```bash wmill flow run [options] ``` ### Arguments | Argument | Description | | ------------- | ------------------------------- | | `remote_path` | The path of the flow to be run. | ### Options | Option | Parameters | Description | | -------------- | ---------- | ----------------------------------------------------------------------------- | | `-d, --data` | `data` | Inputs specified as a JSON string or a file using @filename or stdin using @- . Resources and variables must be passed using "$res:..." or "$var:..." For example `wmill flow run u/henri/message_to_slack -d '{"slack":"$res:u/henri/henri_slack_perso","channel":"general","text":"hello dear team"}'` | | `-s, --silent` | | Do not ouput anything other then the final output. Useful for scripting. | ![CLI arguments](../../assets/cli/cli_arguments.png "CLI arguments") :::tip Inspecting flow runs after completion Use `wmill job get ` to see a hierarchical step tree with status and durations, or `wmill job logs ` to see aggregated logs from all steps. See [Jobs](./job.md) for details. ::: ## Update flow inline scripts lockfile Flows inline script [lockfiles](../6_imports/index.mdx) can be updated locally using the [`wmill generate-metadata`](./generate-metadata.md) command: ```bash wmill generate-metadata ``` This command handles scripts, flows and apps in a single pass. To only update flow lockfiles, use: ```bash wmill generate-metadata --skip-scripts --skip-apps ``` :::info Legacy command Prior to the unified command, this was done with `wmill flow generate-locks`. This command is now deprecated but still works. ::: ## Flow specification You can find the definition of the flow file structure [here](../../openflow/index.mdx). ## Remote path format ```js //... ``` --- ## Folder Source: https://www.windmill.dev/docs/advanced/cli/folder # Folder ## Listing folders The `wmill folder` list command is used to list all folders in the remote workspace. ```bash wmill folder ``` ## Push The `wmill folder push` command is used to push a local folder specification to a remote location, overriding any existing remote versions. ```bash wmill folder push ``` ### Arguments | Argument | Description | | ------------- | ---------------------------------------------------------------- | | `folder_path` | The path to the local folder. | | `remote_path` | The path to the remote location where the folder will be pushed. | ## Adding missing folders When you create scripts, flows, or apps under `f//` without a corresponding `f//folder.meta.yaml` file, `wmill sync push` will either warn (admins) or fail (non-admins, due to RLS) because the remote folder does not exist. The `wmill folder add-missing` command scans every `f//` subdirectory and creates a default `folder.meta.yaml` for any that are missing one. ```bash wmill folder add-missing [-y] ``` ### Options | Option | Description | | ------ | ---------------------------------- | | `-y` | Skip the confirmation prompt. | ### Example ```bash # Dry scan, then create the missing files after confirmation wmill folder add-missing # Non-interactive (useful in scripts) wmill folder add-missing -y ``` `wmill sync push` runs the same detection automatically and suggests `wmill folder add-missing` when folders are missing. ## Stale script detection `wmill generate-metadata` builds a dependency tree across scripts, flows, apps, and [workspace dependencies](../../core_concepts/55_workspace_dependencies/index.mdx), then propagates staleness along that graph. This means if script `C` changes, any scripts `A` and `B` that transitively import `C` (including via relative imports like `./helper` or `../shared/utils`) are correctly detected as stale and regenerated. When you run `wmill generate-metadata f/lib`, scripts **outside** `f/lib` that import from it are included in the run by default. Use `--strict-folder-boundaries` to restrict the run to items physically inside the folder — excluded items that would otherwise have been regenerated are printed as warnings. See [`wmill generate-metadata`](./generate-metadata.md) for the full option list. --- ## Generate metadata Source: https://www.windmill.dev/docs/advanced/cli/generate-metadata # Generate metadata The `wmill generate-metadata` command generates metadata (locks, schemas) for all scripts, flows and apps. It replaces the previous separate commands (`wmill script generate-metadata`, `wmill flow generate-locks`, `wmill app generate-locks`). ## Usage ```bash wmill generate-metadata [folder] [options] ``` ## Options | Option | Description | | ------ | ----------- | | `--yes` | Skip confirmation prompt | | `--dry-run` | Show what would be updated without making changes | | `--lock-only` | Regenerate only lock files | | `--schema-only` | Regenerate only script schemas (skips flows and apps) | | `--skip-scripts` | Skip processing scripts | | `--skip-flows` | Skip processing flows | | `--skip-apps` | Skip processing apps | | `--strict-folder-boundaries` | Only update items inside the specified folder (requires folder argument) | | `-i, --includes ` | Comma-separated patterns to specify which files to include | | `-e, --excludes ` | Comma-separated patterns to specify which files to exclude | The `rehash` subcommand (see [Rehash](#rehash) below) rewrites lockfile hashes from on-disk content without any backend round-trip. ## Arguments | Argument | Description | | -------- | ----------- | | `folder` | Optional folder path to filter metadata generation | ## Examples ### Generate metadata for entire workspace ```bash wmill generate-metadata ``` ### Preview changes without applying ```bash wmill generate-metadata --dry-run ``` ### Auto-confirm all updates ```bash wmill generate-metadata --yes ``` ### Generate only for a specific folder ```bash wmill generate-metadata f/my_folder ``` ### Strict folder boundaries ```bash wmill generate-metadata f/my_folder --strict-folder-boundaries ``` ### Only update lockfiles ```bash wmill generate-metadata --lock-only ``` ### Only update schemas ```bash wmill generate-metadata --schema-only ``` ### Include specific patterns ```bash wmill generate-metadata -i "f/production/*,f/shared/*" ``` ### Exclude specific patterns ```bash wmill generate-metadata -e "f/test/*,f/drafts/*" ``` ## Rehash `wmill generate-metadata rehash [folder]` rewrites `wmill-lock.yaml` hashes directly from on-disk content. It does not contact the backend, does not regenerate scripts/flows/apps, and does not rewrite YAML files — it only recomputes the staleness hashes. Use it when: - **`wmill generate-metadata` flags items as stale that you didn't change.** Common causes: an older CLI wrote the lockfile with a different YAML library or unsorted hash keys, leaving entries that no longer match what the current CLI computes. `rehash` rebuilds those entries from disk so they line up again. - **Your `wmill-lock.yaml` has duplicate `./`-prefixed entries** (e.g. `./f/foo+__script_hash` next to `f/foo+__script_hash`). These come from past `wmill generate-metadata ./folder` invocations on older CLIs. `rehash` deduplicates them by writing canonical (non-prefixed) keys. - **You bootstrapped a long-lived `wmill sync`-managed repo** and want to fill in lockfile entries for items that pre-date the lockfile, without redeploying everything. ### Options | Option | Description | | ------ | ----------- | | `--skip-scripts` | Skip processing scripts | | `--skip-flows` | Skip processing flows | | `--skip-apps` | Skip processing apps | | `-i, --includes ` | Comma-separated patterns to specify which files to include | | `-e, --excludes ` | Comma-separated patterns to specify which files to exclude | ### Examples ```bash # Rebuild every lockfile entry from disk wmill generate-metadata rehash # Scope to a single folder wmill generate-metadata rehash f/billing # Rehash scripts only wmill generate-metadata rehash --skip-flows --skip-apps ``` `rehash` is non-destructive: it never touches your scripts, flows, apps, or YAML files. It only writes to `wmill-lock.yaml`. :::caution Trust, don't verify `rehash` accepts the on-disk content as canonical without checking it against the backend. Don't use it as a substitute for a regular `wmill generate-metadata` run when you actually want to validate that locks/schemas are up to date. Use it only when you've already confirmed the disk content is what you want and just need the lockfile to stop reporting it as stale. ::: `wmill sync pull` runs an automatic, lighter version of this for items that exist on disk but have no lockfile entry — so for typical pull-then-push workflows you don't need to invoke `rehash` manually. The auto-fill is skipped under `--json` and `--dry-run` so non-interactive scripts and previews don't mutate `wmill-lock.yaml`. ## Migration from legacy commands The following commands are now deprecated: | Deprecated command | Replacement | | ------------------ | ----------- | | `wmill script generate-metadata` | `wmill generate-metadata --skip-flows --skip-apps` | | `wmill flow generate-locks` | `wmill generate-metadata --skip-scripts --skip-apps` | | `wmill app generate-locks` | `wmill generate-metadata --skip-scripts --skip-flows` | The legacy commands will show deprecation warnings but continue to work. For centralized dependency management, see [workspace dependencies](../../core_concepts/55_workspace_dependencies/index.mdx). --- ## Gitsync settings Source: https://www.windmill.dev/docs/advanced/cli/gitsync-settings # Git sync settings Managing git-sync configuration between your local `wmill.yaml` file and the Windmill workspace backend is made easy using the wmill CLI. Git-sync settings operations are behind the `wmill gitsync-settings` subcommand. The `gitsync-settings` command allows you to synchronize filter settings, path configurations, and branch-specific overrides without affecting the actual workspace content (scripts, flows, apps, etc.). Git-sync settings management is done using `wmill gitsync-settings pull` & `wmill gitsync-settings push`. Settings operations are configuration-only and work with your `wmill.yaml` file structure. The command will show you the list of changes and ask for confirmation before applying them. When pulling, the source is the remote workspace git-sync configuration and the target is your local `wmill.yaml` file, and when pushing, the source is your local `wmill.yaml` and the target is the remote workspace configuration. ## Pull API The `wmill gitsync-settings pull` command is used to pull remote git-sync settings and apply them to your local `wmill.yaml` file. It synchronizes your local configuration with the workspace git-sync settings. ```bash wmill gitsync-settings pull [options] ``` ### Options | Option | Parameter | Description | | ----------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-h, --help` | None | Show help options. | | `--repository` | `` | Specify repository path (e.g., u/user/repo). If not specified, will auto-select if only one repository exists or prompt for selection. | | `--default` | None | Write settings to top-level defaults instead of branch-specific overrides. | | `--replace` | None | Replace existing settings (non-interactive mode). Overwrites top-level wmill.yaml settings. | | `--override` | None | Add workspace-specific override (non-interactive mode). Creates or updates an entry under the `workspaces:` section. | | `--diff` | None | Show differences without applying changes. Preview what would be modified in your local configuration. | | `--json-output` | None | Output in JSON format. Useful for scripting and automation. | | `--with-backend-settings` | `` | Use provided JSON settings instead of querying backend (primarily for testing purposes). | | `--yes` | None | Skip interactive prompts and use default behavior. The command proceeds automatically without user intervention. | | `--promotion` | `` | Use promotionOverrides from the specified branch instead of regular overrides. | | `--workspace` | `` | Specify the target workspace. This overrides the default workspace. | | `--debug`, `--verbose` | None | Show debug/verbose logs. | | `--token` | `` | Specify an API token. This will override any stored token. | | `--base-url` | `` | Specify the base URL of the API. If used, `--token` and `--workspace` are required and no local remote/workspace will be used. | ## Push API The `wmill gitsync-settings push` command is used to push local git-sync settings and apply them to the remote workspace configuration. It synchronizes the workspace git-sync settings with your local `wmill.yaml`. ```bash wmill gitsync-settings push [options] ``` ### Options | Option | Parameter | Description | | ----------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-h, --help` | None | Show help options. | | `--repository` | `` | Specify repository path (e.g., u/user/repo). If not specified, will auto-select if only one repository exists or prompt for selection. | | `--diff` | None | Show what would be pushed without applying changes. Preview the modifications that would be made to the workspace configuration. | | `--json-output` | None | Output in JSON format. Useful for scripting and automation. | | `--with-backend-settings` | `` | Use provided JSON settings instead of querying backend (primarily for testing purposes). | | `--yes` | None | Skip interactive prompts and use default behavior. The command proceeds automatically without user intervention. | | `--promotion` | `` | Use promotionOverrides from the specified branch instead of regular overrides. | | `--workspace` | `` | Specify the target workspace. This overrides the default workspace. | | `--debug`, `--verbose` | None | Show debug/verbose logs. | | `--token` | `` | Specify an API token. This will override any stored token. | | `--base-url` | `` | Specify the base URL of the API. If used, `--token` and `--workspace` are required and no local remote/workspace will be used. | ## Configuration management The `gitsync-settings` command manages git-sync filter settings and configuration options defined in your `wmill.yaml` file and the workspace backend. ### Settings managed The command handles these git-sync configuration fields (see [wmill.yaml](./sync.mdx#wmillyaml) for complete details): - **Path filters**: `includes`, `excludes`, `extraIncludes` - control which files are synced based on path patterns - **Type filters**: Control which types of resources are synced: - `skipScripts`, `skipFlows`, `skipApps`, `skipFolders` - resource type filters - `skipVariables`, `skipResources`, `skipResourceTypes`, `skipSecrets` - additional type filters - `includeSchedules`, `includeTriggers`, `includeUsers`, `includeGroups` - optional inclusions - `includeSettings`, `includeKey` - system resource inclusions ### Configuration modes The pull command supports different write modes when conflicts exist: - **Replace mode** (`--replace`): Overwrites top-level wmill.yaml settings - **Override mode** (`--override`): Creates workspace-specific overrides in the `workspaces:` section - **Default mode** (`--default`): Writes to top-level defaults - **Interactive mode** (default): Prompts user to choose between replace/override when conflicts exist ## Usage examples ### Basic operations ```bash # Pull git-sync settings from workspace to local wmill.yaml wmill gitsync-settings pull # Push local wmill.yaml git-sync settings to workspace wmill gitsync-settings push # Work with specific repository wmill gitsync-settings pull --repository u/user/my-repo wmill gitsync-settings push --repository u/user/my-repo ``` ### Preview changes ```bash # Show what would change in local configuration wmill gitsync-settings pull --diff # Show what would be pushed to workspace wmill gitsync-settings push --diff # JSON output for scripting wmill gitsync-settings pull --diff --json-output ``` ### Configuration modes ```bash # Replace top-level settings (non-interactive) wmill gitsync-settings pull --replace # Add workspace-specific override (non-interactive) wmill gitsync-settings pull --override # Write to defaults section wmill gitsync-settings pull --default # Skip prompts, use default behavior wmill gitsync-settings pull --yes ``` ### Branch-specific operations ```bash # Use promotion-specific overrides from main branch wmill gitsync-settings pull --promotion main # Push promotion settings to workspace wmill gitsync-settings push --promotion production ``` When using the `--promotion` flag, the command will use `promotionOverrides` instead of regular `overrides` from the specified branch in your `wmill.yaml`. This should be used for [Git Promotion](../9_deploy_gh_gl/index.mdx) connections. ## Differences from sync command The `gitsync-settings` command is specifically for managing configuration, not workspace content: | Feature | `gitsync-settings` | `sync` | |---------|-------------------|---------| | **Purpose** | Manages git-sync configuration settings | Synchronizes actual workspace content (scripts, flows, etc.) | | **Scope** | Configuration metadata only | Workspace resources and files | | **Target** | `wmill.yaml` git-sync settings | Workspace scripts, flows, apps, resources | | **File Operations** | Modifies `wmill.yaml` structure | Creates/updates resource files in sync directory | | **Safety** | Configuration changes only | Can create/delete workspace resources | For more details on the `wmill.yaml` configuration structure, see [wmill.yaml](./sync.mdx#wmillyaml). --- ## Installation Source: https://www.windmill.dev/docs/advanced/cli/installation # Installation To install the wmill CLI: ```bash npm install -g windmill-cli ``` Node version must greater than v20. Also, to punch through some networking layers like Cloudflare Tunnel, you might need some custom headers. You just need to use the HEADERS env variable: ``` export HEADERS=header_key:header_value,header_key2:header_value2 ``` Verify that the installation was successful by running the following command: ```bash wmill --version ``` If the installation was successful, you should see the version of wmill that you just installed. ## Upgrade wmill To upgrade your wmill installation to the latest version, run the following command: ```bash wmill upgrade ``` ## Completion The CLI comes with built-in completions for various shells. Use the following instructions to enable completions for your preferred shell. ### Bash To enable bash completions, add the following line to your ~/.bashrc: ```bash source <(wmill completions bash) ``` ### Zsh To enable zsh completions, add the following line to your ~/.zshrc: ```bash source <(wmill completions zsh) ``` ### Fish To enable fish completions, add the following line to your ~/.config/fish/config.fish: ```bash source (wmill completions fish | psub) ``` --- ## Job Source: https://www.windmill.dev/docs/advanced/cli/job # Jobs The `wmill job` commands let you list, inspect, and manage jobs from the CLI. For flow jobs, `job get` shows a hierarchical step tree and `job logs` aggregates logs from all steps. ## Listing jobs List recent jobs in the workspace. ```bash wmill job list [options] ``` ### Options | Option | Parameters | Description | | ------------------- | ------------- | --------------------------------------------------------------------------- | | `--json` | | Output as JSON (for piping to jq). | | `--script-path` | `path` | Filter by exact script or flow path. | | `--created-by` | `username` | Filter by creator username. | | `--running` | | Show only running jobs. | | `--failed` | | Show only failed jobs. | | `--limit` | `number` | Number of jobs to return (default 30, max 100). | | `--all` | | Include sub-jobs (flow steps). By default only top-level jobs are shown. | | `--parent` | `id` | Show only sub-jobs of a specific flow job. | | `--is-flow-step` | | Show only flow step jobs. | ### Examples 1. List recent failed jobs: ```bash wmill job list --failed ``` 2. List jobs for a specific flow: ```bash wmill job list --script-path f/production/etl_pipeline ``` 3. List sub-jobs of a flow run: ```bash wmill job list --parent 019d447b-114f-a018-0b72-9e541fb77c02 ``` ## Getting job details Get details about a specific job. For flow jobs, this displays a hierarchical step tree showing each module's status, label, duration, and sub-job ID. ```bash wmill job get [options] ``` ### Options | Option | Description | | -------- | -------------------------------- | | `--json` | Output as JSON (for piping to jq). | ### Example ```bash wmill job get 019d447b-114f-a018-0b72-9e541fb77c02 ``` For a flow job, the output includes a step tree: ``` ID: 019d447b-114f-a018-0b72-9e541fb77c02 Type: flow Status: success ... Steps: ✓ a: Generate list (019d447b-2a3f-...) 1.2s ✓ b: Process items (019d447b-3b4c-...) 3.4s ✓ iteration 0 (019d447b-4c5d-...) 1.1s ✓ iteration 1 (019d447b-5d6e-...) 1.2s ✓ iteration 2 (019d447b-6e7f-...) 1.1s ✓ c: Aggregate (019d447b-7f80-...) 0.8s ``` ## Getting job results Get the result of a completed job as JSON. Useful for scripting and piping to other commands. ```bash wmill job result ``` ## Getting job logs Get logs for a job. For flow jobs, this aggregates logs from all steps with labeled headers. For-loop iterations are shown individually. ```bash wmill job logs ``` ### Example For a flow job: ```bash wmill job logs 019d447b-114f-a018-0b72-9e541fb77c02 ``` ``` ====== a: Generate list ====== generating 3 items... ====== b: Process items (iteration 0) ====== processing item 1... ====== b: Process items (iteration 1) ====== processing item 2... ====== c: Aggregate ====== aggregating results... ``` For a specific step, use the sub-job ID from `job get`: ```bash wmill job logs 019d447b-2a3f-... ``` ## Cancelling a job Cancel a running or queued job. ```bash wmill job cancel [options] ``` ### Options | Option | Parameters | Description | | ---------- | ---------- | ------------------------ | | `--reason` | `reason` | Reason for cancellation. | ## Flow debugging workflow A typical workflow for debugging a failed flow run: ```bash # 1. Find the flow job wmill job list --script-path f/production/etl_pipeline --failed # 2. Inspect the step tree to see which step failed wmill job get # 3. See all step logs at once wmill job logs # 4. Or dive into a specific step's logs wmill job logs ``` --- ## Lint Source: https://www.windmill.dev/docs/advanced/cli/lint # Lint The `wmill lint` command validates Windmill YAML files (flows, schedules, and triggers) against their schemas. It scans a directory tree, infers each file's type from its filename (`.flow.yaml`, `.schedule.yaml`, `.http_trigger.yaml`, etc.), and reports missing required fields, unknown properties, or invalid values. ## Usage ```bash wmill lint [directory] [options] ``` If no directory is provided, the current directory is used. The command respects the `includes`/`excludes` patterns from your `wmill.yaml`. ## Options | Option | Description | | ------------------- | ------------------------------------------------------------------------------------------------ | | `--json` | Output machine-readable JSON instead of formatted text. | | `--fail-on-warn` | Exit non-zero on warnings (e.g. skipped native triggers). | | `--locks-required` | Fail if any script or inline script that needs a lock is missing one. | Native triggers (triggers whose schema depends on a dynamic `service_config`) are skipped with a warning — no static schema is available for them. ## Examples Lint every YAML file under the current directory: ```bash wmill lint ``` Lint a specific folder and emit JSON for CI tooling: ```bash wmill lint f/my_project --json ``` Fail the build if any script/flow/app inline script is missing its lockfile: ```bash wmill lint --locks-required ``` ## `--locks-required` `--locks-required` checks standalone scripts, flow inline scripts, normal app inline scripts, and raw app backend scripts. It applies to languages that need a lock: `bun`, `python3`, `php`, `go`, `deno`, `rust`, and `ansible`. The flag can also be enabled globally in `wmill.yaml`: ```yaml locksRequired: true ``` When set, `wmill sync push` runs the same verification before pushing, failing fast if anything is missing. ## CI integration A typical GitHub Actions step: ```yaml - name: Validate Windmill YAML files run: | npm install -g windmill-cli wmill lint --fail-on-warn --locks-required ``` Pair `wmill lint` with [`wmill generate-metadata`](./generate-metadata.md) to (re)generate any missing lockfiles before linting. --- ## Resource Source: https://www.windmill.dev/docs/advanced/cli/resource # Resources ## Listing resources The `wmill resource` list command is used to list all resources in the remote workspace. ```bash wmill resource ``` ## Pushing a resource The `wmill resource push` command is used to push a local resource, overriding any existing remote versions. ```bash wmill resource push ``` ### Arguments | Argument | Description | | ------------- | ---------------------------------------------------- | | `file_path` | The path to the resource file to push. | | `remote_path` | The remote path where the resource should be pushed. | ### Resource specification We support both JSON and YAML files. The structure of the file is as follows: ```yaml value: description: resource_type: is_oauth: ``` ```JSON { "value": "", "description": "", "resource_type": "", "is_oauth": "" } ``` - value (required): Represents the actual content or value of the resource. - description (optional): A string providing additional information or a description of the resource. - resource_type (required): A resource type - is_oauth (optional, deprecated): This property is deprecated and should not be used. See the [resource types](./../../core_concepts/3_resources_and_types/index.mdx) section for a list of supported resource types. --- ## Script Source: https://www.windmill.dev/docs/advanced/cli/script # Scripts ## Listing scripts The `wmill script` list command is used to list all scripts in the remote workspace. ```bash wmill script ``` ## Pushing a script The wmill script push command is used to push a local script to the remote server, overriding any existing remote versions of the script. This command allows you to manage and synchronize your scripts across different environments. This command support .ts, .js, .py, .go and .sh files. ```bash wmill script push ``` ### Arguments | Argument | Description | | -------- | ---------------------------------------------------------- | | `path` | The path to the local script file that needs to be pushed. | ### Examples 1. Push the script located at `/path/to/script.js` ```bash wmill script push /path/to/script.js ``` ## Creating a new script The wmill script bootstrap command is used to create a new script locally in the desired language. ```bash wmill script bootstrap [--summary ] [--description ] ``` ### Arguments | Argument | Description | | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `path` | The path of the script to be created. | | `language` | The language of the new script. It should be one of `ansible`, `bash`, `bigquery`, `bun`, `csharp`, `deno`, `duckdb`, `go`, `graphql`, `java`, `mysql`, `mssql`, `nativets`, `nu`, `oracledb`, `php`, `postgresql`, `powershell`, `python3`, `ruby`, `rust`, `snowflake` | ### Examples 1. Create a new python script `f/scripts/hallowed_script` ```bash wmill script bootstrap f/scripts/hallowed_script python3 ``` 2. Create a new deno script `f/scripts/auspicious_script` with a summary and a description ```bash wmill script bootstrap --summary 'Great script' --description 'This script does this and that' f/scripts/auspicious_script deno ``` ## (Re-)Generating a script metadata file The [`wmill generate-metadata`](./generate-metadata.md) command is used to read all the files that have been edited and gracefully update the metadata file and lockfile accordingly, inferring the script schema from the script content and generating the locks. Only the schema and the locks part of the metadata file will be updated. If a change was made to other fields like description or summary, it will be kept. This command can only be run at the same level of the `wmill-lock.yaml` of your workspace, so by default at top level of the repository. ```bash wmill generate-metadata ``` You can also generate metadata for a single script with `wmill generate-metadata `: ```bash wmill generate-metadata [--lock-only] [--schema-only] [] ``` Note that you can explicitly exclude (or include) specific files or folders to be taken into account by this command, with a [`wmill.yaml` file](https://github.com/windmill-labs/windmill-sync-example/blob/main/wmill.yaml). For centralized dependency management, see [workspace dependencies](../../core_concepts/55_workspace_dependencies/index.mdx). :::info Legacy command Prior to the unified command, this was done with `wmill script generate-metadata`. This command is now deprecated but still works. ::: ### Arguments | Argument | Description | | -------- | ------------------------------------ | | `path` | The path of the script content file. | ### Examples 1. After update the of the script `f/scripts/hallowed_script.py`, re-generate its schema and its locks: ```bash wmill generate-metadata f/scripts/hallowed_script.py ``` ## Showing a script The wmill script show command is used to show the contents of a script on the remote server. ```bash wmill script show ``` ### Arguments | Argument | Description | | -------- | ---------------------------------------------------------- | | `path` | The path to the remote script file that needs to be shown. | ### Examples 1. Show the script located at `f/scripts/test` ```bash wmill script show f/scripts/test ``` ## Running a script Running a script by its path is done using the `wmill script run` command. ```bash wmill script run [options] ``` ### Arguments | Argument | Description | | ------------- | --------------------------------- | | `remote_path` | The path of the script to be run. | ### Options | Option | Parameters | Description | | -------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-d, --data` | `data` | Inputs specified as a JSON string or a file using @filename or stdin using @- . Resources and variables must be passed using "$res:..." or "$var:..." For example `wmill script run u/henri/message_to_slack -d '{"slack":"$res:u/henri/henri_slack_perso","channel":"general","text":"hello dear team"}'` | | `-s, --silent` | | Do not ouput anything other then the final output. Useful for scripting. | ![CLI arguments](../../assets/cli/cli_arguments.png 'CLI arguments') ## Remote path format ```js //... ``` --- ## Sync Source: https://www.windmill.dev/docs/advanced/cli/sync # Sync Synchronizing folders & git repositories to a Windmill instance is made easy using the wmill CLI. Syncing operations are behind the `wmill sync` subcommand. The CLI supports [workspace-specific items](./environment-specific-items.mdx) that allow different versions of resources and variables per workspace (dev / staging / prod). Having a Windmill instance synchronized to folders & git repositories allows for local development through the [VS Code extension](../../cli_local_dev/1_vscode-extension/index.mdx). Syncing is done using `wmill sync pull` & `wmill sync push`. Syncing is a one-off operation with no state maintained. It will override any item that is within the scope: remove those that are in the target and not in the source, and it will create new items that are in source but not in target. It is a _dangerous_ operation, that we recommend doing on a version-controlled folder to be able to revert any undesired changes made by mistake if necessary. It will however, show you the list of changes and ask you for confirmation before applying them. To specify the scopes of files, you should modify the includes and excludes (array of path matchers) part of the `wmill.yaml` file generated with `wmill init`. We recommend looking at all the settings of the `wmill.yaml` file to understand the different options available. Those are the default ones: ``` defaultTs: bun includes: - f/** excludes: [] codebases: [] skipVariables: true skipResources: true skipResourceTypes: true skipSecrets: true includeSchedules: false includeTriggers: false ``` See more details in [wmill.yaml](#wmillyaml). Note that here, the variables, resources, secrets, schedules, and triggers are all skipped by default. You can change that by setting the corresponding options. When pulling, the source is the remote workspace and the target is the local folder, and when pushing, the source is the local folder and the target is the remote workspace. To add a remote workspace, use `wmill workspace add`, and to switch workspace after having added multiple, use `wmill workspace switch `. The workspaces can be on completely different instances. ```bash wmill workspace switch wmill sync pull wmill workspace switch wmill sync push ``` It can be used for bi-directional sync using Windmill EE and [Git sync](../11_git_sync/index.mdx). ### Pulling `wmill sync pull` will simply pull all files from the currently [selected workspace](./workspace-management.md#selected-workspace) and store them in the current folder. Overwrites will not prompt the user. Make sure you are in the correct folder or you may loose data. ### Pushing `wmill sync push` will simply push all local files to the currently [selected workspace](./workspace-management.md#selected-workspace), creating or updating the remote equivalents. ## Pull API The `wmill sync pull` command is used to pull remote changes and apply them locally. It synchronizes the local workspace with the remote workspace by downloading any remote changes and updating the corresponding local files. ```bash wmill sync pull [options] ``` ### Options | Option | Parameter | Description | | ---------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-h, --help` | None | Show help options. | | `--yes` | None | Pull without needing confirmation. The command proceeds automatically without user intervention. | | `--plain-secrets` | None | Pull secrets as plain text. Secrets are downloaded without encryption or obfuscation. | | `--json` | None | Use JSON instead of YAML. The downloaded files are in JSON format instead of YAML. | | `--workspace` | `` | Select the target workspace. Matches a key in `workspaces:` (applying its `overrides` / `specificItems`) and falls back to a local profile name if no entry matches. | | `--debug`, `--verbose` | None | Show debug/verbose logs. | | `--show-diffs` | None | Show diff information when syncing (may show sensitive information). | | `--token` | `` | Specify an API token. This will override any stored token. | | `--base-url` | `` | Specify the base URL of the API. If used, `--token` and `--workspace` are required and no local remote/workspace will be used. | | `--skip-variables` | None | Skip syncing variables (including secrets). | | `--skip-secrets` | None | Skip syncing only secret variables. | | `--skip-resources` | None | Skip syncing resources. | | `--include-schedules` | None | Include syncing schedules. | | `--include-users` | None | Include syncing users. | | `--include-groups` | None | Include syncing groups. | | `--include-triggers` | None | Include syncing triggers (HTTP routes, WebSocket, Postgres, Kafka, NATS, SQS, GCP Pub/Sub, MQTT). | | `--include-settings` | None | Include syncing workspace settings. | | `--include-key` | None | Include workspace encryption key. | | `--skip-branch-validation` | None | Skip git branch validation and prompts. Useful for temporary feature branches that don't need to be added to wmill.yaml. | | `--branch` | `` | **Deprecated — use `--workspace` instead.** Overrides the detected git branch when resolving a `workspaces:` entry. | | `--env` | `` | **Deprecated — use `--workspace` instead.** Kept for single-branch setups that used the old `environments:` key. | | `-i, --includes` | `` | Comma-separated patterns to specify which files to take into account (among files compatible with Windmill). Overrides `wmill.yaml` includes. Patterns can include `*` (any string until '/') and `**` (any string). | | `-e, --excludes` | `` | Comma-separated patterns to specify which files to NOT take into account. Overrides `wmill.yaml` excludes. | | `--extra-includes` | `` | Comma-separated patterns to specify which files to take into account (among files compatible with Windmill). Useful to still take `wmill.yaml` into account and act as a second pattern to satisfy. | ## Push API The `wmill sync push` command is used to push local changes and apply them remotely. It synchronizes the remote workspace with the local workspace by uploading any local changes and updating the corresponding remote files. ```bash wmill sync push [options] ``` ### Options | Option | Parameter | Description | | ---------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-h, --help` | None | Show help options. | | `--workspace` | `` | Specify the target workspace. This overrides the default workspace. | | `--debug`, `--verbose` | None | Show debug/verbose logs. | | `--show-diffs` | None | Show diff information when syncing (may show sensitive information). | | `--token` | `` | Specify an API token. This will override any stored token. | | `--base-url` | `` | Specify the base URL of the API. If used, `--token` and `--workspace` are required and no local remote/workspace will be used. | | `--yes` | None | Push without needing confirmation. | | `--plain-secrets` | None | Push secrets as plain text. | | `--json` | None | Use JSON instead of YAML. | | `--skip-variables` | None | Skip syncing variables (including secrets). | | `--skip-secrets` | None | Skip syncing only secret variables. | | `--skip-resources` | None | Skip syncing resources. | | `--include-schedules` | None | Include syncing schedules. | | `--include-users` | None | Include syncing users. | | `--include-groups` | None | Include syncing groups. | | `--include-triggers` | None | Include syncing triggers (HTTP routes, WebSocket, Postgres, Kafka, NATS, SQS, GCP Pub/Sub, MQTT). | | `--include-settings` | None | Include syncing workspace settings. | | `--include-key` | None | Include workspace encryption key. | | `--skip-branch-validation` | None | Skip git branch validation and prompts. Useful for temporary feature branches that don't need to be added to wmill.yaml. | | `--branch` | `` | **Deprecated — use `--workspace` instead.** Overrides the detected git branch when resolving a `workspaces:` entry. | | `--env` | `` | **Deprecated — use `--workspace` instead.** Kept for single-branch setups that used the old `environments:` key. | | `-i, --includes` | `` | Comma-separated patterns to specify which files to take into account (among files compatible with Windmill). Patterns can include `*` (any string until '/') and `**` (any string). | | `-e, --excludes` | `` | Comma-separated patterns to specify which files to NOT take into account. | | `--extra-includes` | `` | Comma-separated patterns to specify which files to take into account (among files compatible with Windmill). Useful to still take `wmill.yaml` into account and act as a second pattern to satisfy. | | `--message` | `` | Include a message that will be added to all scripts/flows/apps updated during this push. | ## wmill.yaml Note that you can set the default TypeScript language and explicitly exclude (or include) specific files or folders to be taken into account with a [`wmill.yaml` file](https://github.com/windmill-labs/windmill-sync-example/blob/main/wmill.yaml). ### Generating and inspecting the config `wmill init` generates a fully commented `wmill.yaml` with every option documented inline and grouped by category. Advanced / opt-in options are included as commented-out examples, so you can discover and enable them without having to look them up. `wmill config` prints a structured reference table of all config options with their types, defaults, and descriptions. Pair it with `--json` for programmatic consumption (for example, from an AI agent that edits `wmill.yaml`). For a human-readable walkthrough of every `wmill.yaml` option — with defaults, effects, and deprecated keys — see the [`wmill.yaml` reference](./wmill-yaml-reference.mdx). ```bash wmill config # human-readable reference wmill config --json # JSON reference, one entry per option ``` A JSON Schema is also committed at [`cli/wmill.schema.json`](https://github.com/windmill-labs/windmill/blob/main/cli/wmill.schema.json) for editor autocomplete and validation of your `wmill.yaml`. ### Non-dotted paths (`nonDottedPaths`) By default, local files for flows, apps, and raw apps are stored under folders named `__flow/`, `__app/`, and `__raw_app/` (using `__flow`/`__app`/`__raw_app` suffixes rather than dotted extensions). This avoids filesystem edge-cases on Windows and with tooling that assumes a `.flow` extension means a file. ```yaml # wmill.yaml nonDottedPaths: true # default for new workspaces created with `wmill init --use-default` ``` Set `nonDottedPaths: false` to fall back to legacy `.flow`/`.app`/`.raw_app` folder names. ### Basic configuration ```yaml defaultTs: bun # TypeScript runtime: 'bun' or 'deno' # File path filtering includes: - f/** # Include patterns (glob format) [Use - "**" to include everything] excludes: [] # Exclude patterns extraIncludes: [] # Additional include patterns # Codebase bundling configuration codebases: [] # Skip flags (what to exclude from sync) skipVariables: false skipResources: false skipResourceTypes: false skipSecrets: true # Secrets are skipped by default for security skipScripts: false skipFlows: false skipApps: false skipFolders: false # Include flags (what to explicitly include) includeSchedules: false includeTriggers: false includeUsers: false includeGroups: false includeSettings: false includeKey: false # Git branch validation skipBranchValidation: false # Skip validation for feature branches ``` ### Workspaces Multi-target setups live under a single `workspaces` key in `wmill.yaml`. Each entry maps a human-friendly workspace name to a Windmill instance, optional per-workspace sync overrides, and optional workspace-specific items. ```yaml workspaces: staging: baseUrl: https://staging.windmill.dev # workspaceId defaults to "staging" (the key) # gitBranch defaults to "staging" (the key) overrides: includeSchedules: true production: baseUrl: https://app.windmill.dev workspaceId: prod-ws # explicit: differs from the key gitBranch: main # explicit: maps to git branch "main" overrides: skipSecrets: false promotionOverrides: # applied when pushing with --promotion skipApps: true skipFlows: true specificItems: # workspace-specific items (see below) resources: - "u/alex/prod_*" variables: - "u/alex/env_*" # Items that are workspace-specific across ALL workspaces commonSpecificItems: resources: - "u/alex/config/**" variables: - "u/alex/database_*" folders: - "f/env_*" settings: true ``` Key properties: - **Key = workspace name.** It's a human-friendly identifier, not the git branch. - **`workspaceId` defaults to the key** — only set it when the Windmill workspace id differs from the name. - **`gitBranch` defaults to the key** — only set it when the git branch name differs. - **`baseUrl`** points at the Windmill instance; the CLI resolves it to a local [profile](./workspace-management.md) (baseUrl + workspaceId match), so the same `wmill.yaml` works on any machine as long as a matching profile is logged in. - **`overrides`** override any top-level sync option when this workspace is the current one. - **`promotionOverrides`** apply when pushing to this workspace with `--promotion`. - **`specificItems`** declare items that are stored per-workspace on disk (see [workspace-specific items](./environment-specific-items.mdx)). ### Selecting a workspace The CLI picks the active workspace in this order: 1. `--workspace ` — explicit override, matches a key in `workspaces:` (or a local profile name as a fallback). 2. **Current git branch** — the first entry whose effective `gitBranch` matches `git rev-parse --abbrev-ref HEAD`. 3. **Local profile** — the active profile set via `wmill workspace switch` (used when no `workspaces:` entry matches). All sync commands (`wmill sync pull`, `wmill sync push`, `wmill gitsync-settings`, etc.) accept `--workspace` and apply the matching workspace's `overrides` / `specificItems`. ### Binding a workspace to wmill.yaml `wmill workspace bind` interactively creates or updates a `workspaces:` entry from the active local profile. It prompts for the workspace name, git branch (optional), and handles the `workspaceId` / `baseUrl` fields for you. ```bash wmill workspace bind # interactive: pick profile, name, branch wmill workspace bind --workspace staging # non-interactive: use "staging" as the key wmill workspace unbind --workspace staging # remove baseUrl/workspaceId from the entry ``` `wmill init` also runs the bind flow automatically for fresh projects. ### Configuration resolution priority Settings are resolved in this order (highest priority first): 1. **CLI flags** (highest priority) 2. **Promotion overrides** (if `--promotion` flag is used) 3. **Workspace overrides** (from the resolved workspace's `overrides` block) 4. **Top-level settings** (base configuration) ### Migrating from `gitBranches` / `environments` Before this change, the CLI had three separate keys — `gitBranches`, `environments`, and `git_branches` — with the branch name as the map key and `workspaceId` always required. Unifying everything under `workspaces:` removes that duplication: one key, one flag (`--workspace`), and sensible defaults so most entries drop to two lines. Legacy configs keep working. On the first read of a legacy `wmill.yaml`, the CLI normalizes it to `workspaces:` in memory and prints a one-time deprecation warning. `--branch` and `--env` on sync commands are still accepted with a deprecation warning — prefer `--workspace`. To migrate the file on disk in one command: ```bash wmill config migrate # ✅ Migrated 'gitBranches' to 'workspaces' in wmill.yaml # Workspace entries: main, staging ``` The migration preserves every field — `baseUrl`, `workspaceId`, `overrides`, `promotionOverrides`, `specificItems`, `commonSpecificItems`. Since legacy keys were branch names, the resulting workspace name equals the old branch name and `gitBranch` is left implicit (it defaults to the key). [Workspace-specific item files](./environment-specific-items.mdx) keep the same suffix — the file `database.main.resource.yaml` stays valid because the workspace is still named `main`. ### Inspecting the config `wmill config` prints a structured reference of every option with its default, type, and description. Pair with `--json` for machine-readable output. ```bash wmill config # human-readable reference wmill config --json # JSON reference, one entry per option wmill config migrate # legacy → workspaces ``` ### Codebase configuration Advanced bundling configuration for TypeScript/JavaScript projects: ```yaml codebases: - relative_path: './my-lib' includes: ['**/*.ts'] excludes: ['**/*.test.ts'] assets: - from: 'assets/' to: 'static/' customBundler: 'esbuild' external: ['lodash', 'react'] define: NODE_ENV: 'production' inject: ['./polyfills.js'] ``` All sync options for your workspace can be found on this [file](https://github.com/windmill-labs/windmill/blob/main/cli/src/core/conf.ts): ```ts yes?: boolean; skipPull?: boolean; failConflicts?: boolean; plainSecrets?: boolean; json?: boolean; // File filtering options skipVariables?: boolean; skipResources?: boolean; skipResourceTypes?: boolean; skipSecrets?: boolean; skipScripts?: boolean; skipFlows?: boolean; skipApps?: boolean; skipFolders?: boolean; // Include options includeSchedules?: boolean; includeTriggers?: boolean; includeUsers?: boolean; includeGroups?: boolean; includeSettings?: boolean; includeKey?: boolean; skipBranchValidation?: boolean; // Path and message options message?: string; includes?: string[]; extraIncludes?: string[]; excludes?: string[]; defaultTs?: 'bun' | 'deno'; codebases?: Codebase[]; // Workspace bindings — the single unified config key workspaces?: { commonSpecificItems?: { variables?: string[]; resources?: string[]; triggers?: string[]; folders?: string[]; settings?: boolean; }; [workspaceName: string]: SyncOptions & { // Defaults to the key (workspace name) when omitted gitBranch?: string; workspaceId?: string; baseUrl?: string; overrides?: Partial; promotionOverrides?: Partial; specificItems?: { variables?: string[]; resources?: string[]; triggers?: string[]; folders?: string[]; settings?: boolean; }; }; }; // Deprecated — still read, normalized to `workspaces` in memory gitBranches?: unknown; environments?: unknown; git_branches?: unknown; promotion?: string; } ``` ## Example repo for syncing with Windmill in git We provide an example repo for syncing with Windmill: ## Cloning an instance Instances can be cloned with Windmill CLI. This is useful for instance migration or for setting up a new (prod) instance with the same configuration as an existing one. ### Pulling an instance To pull instance users, groups, settings and configs (=worker groups+SMTP) from the instance use `wmill instance pull`. You can skip any of those using `--skip-XXX`. You can also pull all workspaces at the same time by adding the `--include-workspaces` option. It will setup for each a local workspace (including a folder in the current directory) with a prefix specific to the instance (based on the name you've given to the instance). ### Pushing an instance To push to an instance use `wmill instance push`. It takes the same options as pulling an instance. When using `--include-workspaces`, you will also have to select the instance prefix of the local workspaces you want to push (make sure you're in its parent folder). During the push process, you will be prompted to decide whether to [re-encrypt](./workspace-management.md#managing-encryption-keys) the secrets of the workspace on the remote instance. In the case of instance migration, it is recommended to select "no" as the secrets are already encrypted with the correct key. ### Instance-level Slack The CLI also supports connecting Slack at the instance level (global bot token used for critical alerts) non-interactively: ```bash wmill instance connect-slack \ --bot-token \ --team-id \ --team-name "" ``` Super-admin only. Writes the `global_settings.slack` row and the encrypted variable + resource at `f/slack_bot/global_bot_token` in the `admins` workspace — same artifacts the UI's "Connect instance to Slack" button produces. See the [Slack integration doc](../../integrations/slack.mdx#action-on-windmill-from-slack) for the full flow, including configuring the OAuth client (`client_id`/`client_secret`) via `wmill instance push`. --- ## User Source: https://www.windmill.dev/docs/advanced/cli/user # Users management ## Adding a user The `wmill user add` command is used to add a new user to the remote server. ```bash wmill user add [password:string] [--superadmin] [--company ] [--name ] ``` ### Arguments | Argument | Description | | ---------- | ---------------------------------------------- | | `email` | The email address of the user to be added. | | `password` | The password of the user to be added. Optional | ### Options | Option | Parameters | Description | | -------------- | ---------- | ------------------------------------------- | | `--superadmin` | | Specify to make the new user superadmin. | | `--company` | `company` | Specify to set the company of the new user. | | `--name` | `name` | Specify to set the company of the new user. | ### Examples 1. Create a new user with the email "example@example.com" and automatically generate a password: ```bash wmill user add example@example.com ``` 2. Create a new user with the email "example@example.com" and specify a password: ```bash wmill user add example@example.com mypassword123 ``` 3. Create a new superadmin user with the email "example@example.com", a specified password, and additional information: ```bash wmill user add example@example.com mypassword123 --superadmin --company "Acme Inc." --name "John Doe" ``` ## Removing a user The `wmill user remove` command is used to remove a user from the remote server. ```bash wmill user remove ``` ### Arguments | Argument | Description | | -------- | -------------------------------------------- | | `email` | The email address of the user to be removed. | ### Examples 1. Remove the user with the email "example@example.com" ```bash wmill user remove example@example.com ``` ## Creating a token The wmill user create-token command allows you to create an authentication token for a user. This token can be used for subsequent authenticated requests to the API server. ```bash wmill user create-token [--email --password ] ``` There are two ways to create a token: - Option 1: Specify email and password for authentication: Use the --email option to specify the email address of the user. Use the --password option to specify the password of the user. The command will exchange the provided credentials for a token with the API server and display the generated token. - Option 2: Already logged in: If you are already logged in, you can run the command without providing email and password. The command will use your existing authentication credentials to create a token and display it. The command will display the generated token, which can be used for subsequent authenticated requests. Note that the token is not stored locally. ## Groups The `wmill group` commands manage [workspace groups](../../core_concepts/8_groups_and_folders/index.mdx) and their members. ```bash wmill group list wmill group get wmill group create [--summary ] wmill group delete wmill group add-user wmill group remove-user ``` ### Examples ```bash # List all groups and create a new one wmill group list wmill group create data-team --summary "Data engineering" # Manage membership wmill group add-user data-team alice wmill group remove-user data-team bob ``` ## Audit logs The `wmill audit` commands read from the workspace [audit log](../../core_concepts/14_audit_logs/index.mdx) ([Enterprise Edition](/pricing)). ```bash wmill audit list [options] wmill audit get ``` ### Options | Option | Description | | ----------------- | -------------------------------------------------------- | | `--username` | Filter by username. | | `--operation` | Filter by operation (e.g. `scripts.create`). | | `--action-kind` | Filter by action kind (`create`, `update`, `delete`, `execute`). | | `--before` | Return entries before this timestamp. | | `--after` | Return entries after this timestamp. | ### Examples ```bash # Recent activity by a user wmill audit list --username alice --after 2026-01-01 # Inspect a single event wmill audit get 1234 ``` ## Tokens The `wmill token` commands manage API tokens for the current user. ```bash wmill token list wmill token create [--label