Skip to main content

Workspace-specific items

Workspace-specific items let you store different versions of resources, variables, 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.

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 guide. The single-workspace form of workspaces: is used starting at stage 3.

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.

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.

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 <name> — 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.

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.

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.

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:

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:

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
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 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 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.