Migrating Packages from v1alpha1 to v1beta1
This guide migrates a ZarfPackageConfig definition from zarf.dev/v1alpha1 to zarf.dev/v1beta1. Existing v1alpha1 definitions remain supported. A definition without apiVersion is treated as v1alpha1, whereas v1beta1 requires apiVersion: zarf.dev/v1beta1.
Replace removed features
Before converting, migrate these features while keeping the definition on v1alpha1. zarf dev upgrade errors if any of the following removed features are still in use:
| v1alpha1 field | Migration |
|---|---|
variables, constants, components[].charts[].variables, Action setVariables | Follow Variables to Values. Replace references in manifests, files, chart values files, and actions as well as declarations. |
components[].dataInjections | Follow Data Injections Migration. |
metadata.yolo | Remove it and deploy with --connected. See Connected Mode. |
components[].import.name | See Component imports. |
components[].group | Use only.flavor in v1alpha1, converted to selector.flavor in v1beta1. This replaces deploy-time group selection with create-time filtering. |
components[].default | Remove it. Default group members are not applicable and there is no way to set the default prompt answer. |
components[].only.cluster.distros | Remove it. This field was unused and has no replacement. |
Convert the definition
The command below performs all the automatic field mappings.
zarf dev upgrade zarf.yaml > zarf-v1beta1.yamlYAML comments and formatting are not preserved. Imported definitions must be migrated separately.
Component imports
Imports now reference ZarfComponentConfig files containing one component. A package or directory can no longer be imported directly, and skeleton packages are replaced by published component configs.
- Convert the source package, then extract each imported component into its own file.
- Move its name and description into
metadata, and its resource and action fields undercomponent. Keepoptionalandselectoron the consuming package component. - Move flavor and architecture restrictions for the imported component into top-level
variant.flavorandvariant.architecture. - Copy required
valuesdeclarations. - Replace
import.pathwithimport.local[].path, orimport.urlwithimport.remote[].url. Removeimport.name.
apiVersion: zarf.dev/v1beta1kind: ZarfComponentConfigmetadata: name: app version: 1.0.0component: images: - name: ghcr.io/example/app:1.0.0# In the consuming package's components list- name: app import: local: - path: components/app.yamlWhen listing multiple imports, exactly one must match the create-time flavor and architecture. Empty variant fields match any target, so avoid overlapping variants.
For remote imports, set metadata.version and publish the component:
zarf component publish components/app.yaml oci://ghcr.io/example/componentsThen replace the local import with:
import: remote: - url: oci://ghcr.io/example/components/app:1.0.0Remote components cannot contain onCreate actions or use kustomize.allowAnyDirectory: true. Move create actions into the consuming package. Render package templates before publishing, as described below.
Package templates to zarf dev template
In v1beta1, package templates are rendered before zarf package create using a separate zarf dev template step.
- Rename the migrated package definition from
zarf.yamltozarf.tpl.yaml. - Replace each
###ZARF_PKG_TMPL_KEY###with[[ .KEY ]]. Keys are case-sensitive and accessed directly, without a.Valuesprefix. - Move template inputs from
zarf package create --settozarf dev template --set KEY=value, or supply a YAML mapping with--set-file template-values.yaml.--setoverrides file values. Missing template values cause an error instead of an interactive prompt.
For example, a migrated zarf.tpl.yaml:
apiVersion: zarf.dev/v1beta1kind: ZarfPackageConfigmetadata: name: app version: "[[ .VERSION ]]"components: - name: app images: - name: "[[ .IMAGE ]]"Render it, then create the package:
zarf dev template --set VERSION=1.0.0 --set IMAGE=ghcr.io/example/app:1.0.0zarf package create zarf.gen.yamlRendering writes zarf.gen.yaml alongside the template and validates the generated schema. Re-render whenever template inputs change.
For local component templates, render each <name>.tpl.yaml separately and set import.local[].path to <name>.gen.yaml. Remote components must be fully rendered before publishing and cannot be templated during import.
Readiness checks
healthChecks becomes wait actions appended to actions.onDeploy.onSuccess:
# After: component fieldsactions: onDeploy: onSuccess: - wait: cluster: kind: Deployment.v1.apps name: app namespace: appFor a health check with apiVersion: apps/v1 and kind: Deployment, the wait kind is Deployment.v1.apps. Core resources such as Pod keep their kind unchanged.
In v1beta1, omitting wait.cluster.condition waits for resource readiness using kstatus. Set condition: exists to wait only for existence. Conversion adds exists to old conditionless wait actions to preserve their behavior; converted health checks omit the condition to retain readiness checks.
Schema Changes and Field Mappings
These conversions are automatic. Paths below are relative to each component unless prefixed with metadata or build. [] denotes a list entry.
| v1alpha1 | v1beta1 |
|---|---|
required | optional, with the boolean inverted. |
only.flavor | selector.flavor |
only.cluster.architecture | selector.architecture |
only.localOS | target.os |
charts[].noWait, manifests[].noWait | skipWait on the same entry. |
manifests[].template, files[].template, action template | enableTemplating on the same entry. |
files[].target | files[].destination |
files[].shasum | files[].checksum, prefixed with sha256:. Unprefixed checksums also mean SHA-256. |
manifests[].kustomizations | manifests[].kustomize.files |
manifests[].kustomizeAllowAnyDirectory | manifests[].kustomize.allowAnyDirectory |
manifests[].enableKustomizePlugins | manifests[].kustomize.enablePlugins |
Action/default maxRetries, mute | retries, silent |
actions.*.after | Prepended to actions.*.onSuccess, before existing success actions. |
charts[].schemaValidation | charts[].skipSchemaValidation, with the boolean inverted. |
charts[] source fields and values files | See Helm charts. |
images[] | See Images. |
repos[] | repositories[]. See Git repositories. |
metadata.allowNamespaceOverride | metadata.preventNamespaceOverride, with the boolean inverted. |
metadata.image, source, documentation, url, authors, vendor | Same keys under metadata.annotations. Existing annotation values take precedence. |
metadata.aggregateChecksum | build.aggregateChecksum |
build.terminal | build.hostname |
New v1beta1 components are required by default (optional: false). However, conversion keeps current behavior (i.e., an omitted or false required becomes optional: true). Namespace overrides and chart schema validation remain enabled by default.
selector filters components during create; target filters them during deploy.
Helm charts
Each chart must have exactly one source: helmRepository, git, oci, or local. Conversion moves the old source fields into that object:
| v1alpha1 chart source | v1beta1 chart source |
|---|---|
Helm repository url, repoName, version | helmRepository.url, helmRepository.name, helmRepository.version |
Git url, gitPath, version or URL @ref | git.url, git.path, git.ref |
OCI url, version | oci.url, oci.ref.tag; digest references use oci.ref.digest. |
localPath | local.path; remove version and use the chart’s Chart.yaml version. |
Git charts require ref with exactly one of tag, branch, or commit. Conversion treats refs/heads/<name> as a branch, a full SHA as a commit, and other refs as tags. If the URL has no ref, the old chart version supplies it. Git URLs must no longer contain an @ref suffix.
OCI charts require exactly one of ref.tag or ref.digest. The URL must not contain a reference.
Chart values files become objects. Combine valuesFiles and templatedValuesFiles into valuesFiles:
# Before: chart entryname: appnamespace: appurl: oci://ghcr.io/example/charts/appversion: 1.0.0valuesFiles: - chart-values.yamltemplatedValuesFiles: - runtime-values.yaml# After: chart entryname: appnamespace: appoci: url: oci://ghcr.io/example/charts/app ref: tag: 1.0.0valuesFiles: - path: chart-values.yaml - path: runtime-values.yaml enableTemplating: truePackage-value mappings in charts[].values keep their sourcePath and targetPath fields.
Images
images entries are objects:
# Component fieldsimages: - name: ghcr.io/example/app:1.0.0 source: registryImages default to source: registry. For images stored in the Docker daemon, set source: daemon. v1beta1 does not fall back to the local daemon after a registry pull fails.
Git repositories
repos becomes repositories, with the Git ref separated from the URL:
# Component fieldsrepositories: - url: https://github.com/example/app.git ref: branch: mainOmit ref to mirror all branches and tags.