pluginDefaults Removed
For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append.mdto anykestra.io/docs/*URL for plain Markdown.
The pluginDefaults keyword is removed in Kestra 2.0 in all editions and at all scopes: flow level, namespace level (EE), and global server configuration. The older taskDefaults alias (used in Kestra versions before pluginDefaults was introduced) is also removed.
Flows that contain a pluginDefaults block fail to parse after upgrading to 2.0.0. Remove or migrate all pluginDefaults entries before upgrading.
What changed
In Kestra 1.x, plugin defaults could be defined at three levels:
| Before (1.x) | After (2.x) |
|---|---|
Flow-level pluginDefaults: block | Values inlined onto tasks (OSS), or a REFERENCE Policy attached via policyRefs: (EE) |
| Namespace-level Plugin Defaults (EE) | Namespace-scoped Policy (UI / API); existing namespace-level defaults are migrated automatically |
kestra.plugins.defaults in server config | Static policy under kestra.policies in server config |
forced: false (fill only when unset) | Add rule with override: false (the default) |
forced: true (policy value always wins) | Add rule with override: true |
Tenant-scoped governance did not exist in 1.x. Kestra 2.0 introduces tenant-scoped Policies as a new capability — you can now apply defaults and enforcement rules across an entire tenant without touching individual namespaces.
All pluginDefaults constructs are removed in 2.0.0. In Enterprise Edition, they are replaced by Policies — a governance layer that covers injection, enforcement, and validation in one place. Policies also extend pluginDefaults with the ability to target flow-level properties (retry, concurrency, labels) in addition to plugin properties. In OSS, there is no direct replacement and no automated migration tooling — move default values inline onto each task or use flow-level variables.
Precedence is preserved. Policies apply along a scope chain STATIC → INSTANCE → TENANT → NAMESPACE:
- Non-overriding (
override: false) values fill properties the flow author left unset; the innermost scope wins — the same “task > flow > namespace > global” precedence as 1.x non-forced defaults. - Overriding (
override: true) values replace the author’s value; the outermost scope wins — the same “global beats namespace” precedence as 1.x forced defaults.
Before you migrate — build your inventory
Collect every place plugin defaults are defined before upgrading. The 1.x UI for namespace and tenant defaults does not exist in 2.x.
- Server configuration: every entry under
kestra.plugins.defaultsin yourapplication.ymlor Helm values. - Namespace defaults (EE): export them from each namespace’s Plugin Defaults settings page, or via the 1.x API.
- Flow-level blocks: search your flow sources for
pluginDefaults::
grep -rl "pluginDefaults:" flows/Migration — Enterprise Edition
Each pluginDefaults entry maps to an Add rule inside a Policy.
Step 1 — global server config → static policy
Entries from kestra.plugins.defaults map to a static policy declared under kestra.policies in server configuration — not a tenant-scoped or namespace-scoped Policy. Static policies are cross-tenant, form the outermost scope, and are read-only through the API.
Before (application.yml):
kestra: plugins: defaults: - type: io.kestra.plugin.scripts.shell.Commands values: containerImage: ubuntu:24.04 - type: io.kestra.plugin.aws forced: true values: region: eu-west-1After (application.yml):
kestra: policies: instance-plugin-defaults: description: "Migrated from kestra.plugins.defaults." rules: - type: io.kestra.plugin.ee.rules.Add on: PLUGIN where: - field: type operator: EQUAL_TO value: io.kestra.plugin.scripts.shell.Commands values: containerImage: ubuntu:24.04 - type: io.kestra.plugin.ee.rules.Add on: PLUGIN override: true where: - field: type operator: STARTS_WITH value: io.kestra.plugin.aws values: region: eu-west-1kestra.policiesis a map. The key (instance-plugin-defaultsabove) is the policy identity — do not add anidfield inside the body.- A malformed static policy prevents server startup. Validate in staging first.
Step 2 — namespace-level Plugin Defaults → namespace Policy
Recreate each entry as an Add rule in a namespace-scoped Policy, via the Policies UI or the API:
POST /api/v1/{tenant}/namespaces/{namespace}/policiesNamespace policies apply to the namespace and all its child namespaces, matching 1.x namespace-default inheritance.
Existing namespace-level Plugin Defaults are migrated to Policies automatically during the 2.0 upgrade. Review the migrated Policies and adjust if needed.
Before (1.x namespace Plugin Default):
- type: io.kestra.plugin.aws values: accessKeyId: "{{ secret('AWS_ACCESS_KEY_ID') }}" secretKeyId: "{{ secret('AWS_SECRET_ACCESS_KEY') }}" region: "us-east-1"After (2.0 namespace-scoped Policy):
id: aws-credentialsdescription: "Central AWS credentials for all AWS plugin tasks."enforcement: ACTIVErules: - type: io.kestra.plugin.ee.rules.Add on: PLUGIN where: - field: type operator: STARTS_WITH value: io.kestra.plugin.aws values: accessKeyId: "{{ secret('AWS_ACCESS_KEY_ID') }}" secretKeyId: "{{ secret('AWS_SECRET_ACCESS_KEY') }}" region: "us-east-1"Scope, tenant, and namespace come from the URL — not from the policy body.
If multiple namespaces had identical Plugin Defaults, you can consolidate them into a single tenant-scoped Policy with a target.namespaces list instead of maintaining one policy per namespace:
id: aws-credentialsdescription: "Central AWS credentials — applies to analytics and data namespaces."enforcement: ACTIVEtarget: namespaces: - analytics - datarules: - type: io.kestra.plugin.ee.rules.Add on: PLUGIN where: - field: type operator: STARTS_WITH value: io.kestra.plugin.aws values: accessKeyId: "{{ secret('AWS_ACCESS_KEY_ID') }}" secretKeyId: "{{ secret('AWS_SECRET_ACCESS_KEY') }}" region: "us-east-1"Create this via POST /api/v1/{tenant}/policies (tenant level). The target.namespaces list uses ancestor-chain matching — listing analytics covers analytics and all its children. See Policies for the full target reference.
Step 3 — flow-level pluginDefaults
Flow-level pluginDefaults never supported forced in 1.x (it was stripped with a warning). Two migration options:
Option A — inline the values (recommended for one or two flows). Copy each default’s values onto the matching tasks and delete the pluginDefaults: block.
Option B — reference Policy (recommended when many flows share the same block). Create a Policy with enforcement: REFERENCE at the namespace or tenant scope. Each flow opts in with policyRefs:.
Before (1.x flow):
id: daily-reportnamespace: company.team
pluginDefaults: - type: io.kestra.plugin.scripts.python.Script values: containerImage: ghcr.io/kestra-io/pydata:latest
tasks: - id: transform type: io.kestra.plugin.scripts.python.Script script: ...After — the shared reference Policy (created once, on company.team):
id: pydata-defaultsdescription: "Python data science container image default."enforcement: REFERENCErules: - type: io.kestra.plugin.ee.rules.Add on: PLUGIN where: - field: type operator: EQUAL_TO value: io.kestra.plugin.scripts.python.Script values: containerImage: ghcr.io/kestra-io/pydata:latestAfter — the flow:
id: daily-reportnamespace: company.team
policyRefs: - pydata-defaults
tasks: - id: transform type: io.kestra.plugin.scripts.python.Script script: ...A REFERENCE Policy applies only to flows that list it in policyRefs. ACTIVE Policies apply automatically to every flow in scope.
Forced defaults
forced: true on a pluginDefaults entry becomes override: true on the corresponding Add rule.
Before:
pluginDefaults: - type: io.kestra.plugin.scripts.runner.docker.Docker forced: true values: pullPolicy: NEVERAfter:
id: docker-pull-policydescription: "Force Docker pull policy to NEVER across all script tasks."enforcement: ACTIVErules: - type: io.kestra.plugin.ee.rules.Add on: PLUGIN where: - field: type operator: EQUAL_TO value: io.kestra.plugin.scripts.runner.docker.Docker override: true values: pullPolicy: NEVERMigration — OSS
OSS does not include Policies. There is no centralized replacement for pluginDefaults in OSS.
Your options are:
- Inline values — move the default values directly onto each task that used them.
- Flow variables — define a variable at the flow level and reference it in each task with
{{ vars.myVariable }}. - Upgrade to Enterprise Edition — use Policies for centralized, enforced defaults across namespaces.
Behavioral differences
A few behaviors differ from 1.x pluginDefaults:
- Lists are replaced, not merged. Map-valued properties deep-merge as before, but if a Policy injects a list (for example, a list of environment variables), it replaces the author’s list entirely when
override: true. Check any default whosevaluescontain lists. - Plugin aliases are not resolved. 1.x resolved deprecated plugin aliases through the plugin registry. Policy conditions match the
typestring literally. If your flows use an alias, cover both the alias and the canonical name — or migrate all flows to the canonical name first. EVALUATEmode does not inject values. OnlyACTIVEand attachedREFERENCEPolicies mutate flows. Do not leave a migrated Policy inEVALUATEin production — tasks run without their former defaults.Add+Deleteconflicts are rejected at save time. 1.x had noDeleteconcept, so this only matters if you also adopt new validation rules. If one Policy injects a property and another removes it, saving any affected flow fails with an error citing both policies.
Verify the migration
Preview the effective policy chain for a flow:
POST /api/v1/{tenant}/flows/policies/previewSend the flow source; the response shows the mutated source with per-property attribution of which Policy injected what.
Dry-run a single policy against existing flows:
GET /api/v1/{tenant}/policies/{id}/evaluateGET /api/v1/{tenant}/namespaces/{namespace}/policies/{id}/evaluateSmoke test: run one execution per critical flow and inspect the task configuration used (container image, region, task runner) in the execution view.
Migration checklist
- Inventory all plugin defaults before upgrading — server config, namespace/tenant UI, and flow YAML files.
- Remove
kestra.plugins.defaultsfrom server config and add equivalent static policies underkestra.policies. - For each namespace that had Plugin Defaults, review the auto-migrated Policies and adjust if needed.
- For each flow with a
pluginDefaults:block, either inline the values onto tasks or create a reference Policy and addpolicyRefs:to the flow. - Verify flows parse correctly:
kestra flow validate /path/to/flow.yml - Grant teams that managed namespace defaults the
POLICYpermission (VIEW,CREATE,UPDATE,DELETE) — it is separate from namespace edit rights.
See Policies for the full Policy DSL reference, enforcement modes, inheritance behavior, and examples.
Was this page helpful?