Skip to content

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 fieldMigration
variables, constants, components[].charts[].variables, Action setVariablesFollow Variables to Values. Replace references in manifests, files, chart values files, and actions as well as declarations.
components[].dataInjectionsFollow Data Injections Migration.
metadata.yoloRemove it and deploy with --connected. See Connected Mode.
components[].import.nameSee Component imports.
components[].groupUse only.flavor in v1alpha1, converted to selector.flavor in v1beta1. This replaces deploy-time group selection with create-time filtering.
components[].defaultRemove it. Default group members are not applicable and there is no way to set the default prompt answer.
components[].only.cluster.distrosRemove it. This field was unused and has no replacement.

Convert the definition

The command below performs all the automatic field mappings.

Terminal window
zarf dev upgrade zarf.yaml > zarf-v1beta1.yaml

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

  1. Convert the source package, then extract each imported component into its own file.
  2. Move its name and description into metadata, and its resource and action fields under component. Keep optional and selector on the consuming package component.
  3. Move flavor and architecture restrictions for the imported component into top-level variant.flavor and variant.architecture.
  4. Copy required values declarations.
  5. Replace import.path with import.local[].path, or import.url with import.remote[].url. Remove import.name.
components/app.yaml
apiVersion: zarf.dev/v1beta1
kind: ZarfComponentConfig
metadata:
name: app
version: 1.0.0
component:
images:
- name: ghcr.io/example/app:1.0.0
# In the consuming package's components list
- name: app
import:
local:
- path: components/app.yaml

When 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:

Terminal window
zarf component publish components/app.yaml oci://ghcr.io/example/components

Then replace the local import with:

import:
remote:
- url: oci://ghcr.io/example/components/app:1.0.0

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

  1. Rename the migrated package definition from zarf.yaml to zarf.tpl.yaml.
  2. Replace each ###ZARF_PKG_TMPL_KEY### with [[ .KEY ]]. Keys are case-sensitive and accessed directly, without a .Values prefix.
  3. Move template inputs from zarf package create --set to zarf dev template --set KEY=value, or supply a YAML mapping with --set-file template-values.yaml. --set overrides file values. Missing template values cause an error instead of an interactive prompt.

For example, a migrated zarf.tpl.yaml:

apiVersion: zarf.dev/v1beta1
kind: ZarfPackageConfig
metadata:
name: app
version: "[[ .VERSION ]]"
components:
- name: app
images:
- name: "[[ .IMAGE ]]"

Render it, then create the package:

Terminal window
zarf dev template --set VERSION=1.0.0 --set IMAGE=ghcr.io/example/app:1.0.0
zarf package create zarf.gen.yaml

Rendering 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 fields
actions:
onDeploy:
onSuccess:
- wait:
cluster:
kind: Deployment.v1.apps
name: app
namespace: app

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

v1alpha1v1beta1
requiredoptional, with the boolean inverted.
only.flavorselector.flavor
only.cluster.architectureselector.architecture
only.localOStarget.os
charts[].noWait, manifests[].noWaitskipWait on the same entry.
manifests[].template, files[].template, action templateenableTemplating on the same entry.
files[].targetfiles[].destination
files[].shasumfiles[].checksum, prefixed with sha256:. Unprefixed checksums also mean SHA-256.
manifests[].kustomizationsmanifests[].kustomize.files
manifests[].kustomizeAllowAnyDirectorymanifests[].kustomize.allowAnyDirectory
manifests[].enableKustomizePluginsmanifests[].kustomize.enablePlugins
Action/default maxRetries, muteretries, silent
actions.*.afterPrepended to actions.*.onSuccess, before existing success actions.
charts[].schemaValidationcharts[].skipSchemaValidation, with the boolean inverted.
charts[] source fields and values filesSee Helm charts.
images[]See Images.
repos[]repositories[]. See Git repositories.
metadata.allowNamespaceOverridemetadata.preventNamespaceOverride, with the boolean inverted.
metadata.image, source, documentation, url, authors, vendorSame keys under metadata.annotations. Existing annotation values take precedence.
metadata.aggregateChecksumbuild.aggregateChecksum
build.terminalbuild.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 sourcev1beta1 chart source
Helm repository url, repoName, versionhelmRepository.url, helmRepository.name, helmRepository.version
Git url, gitPath, version or URL @refgit.url, git.path, git.ref
OCI url, versionoci.url, oci.ref.tag; digest references use oci.ref.digest.
localPathlocal.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 entry
name: app
namespace: app
url: oci://ghcr.io/example/charts/app
version: 1.0.0
valuesFiles:
- chart-values.yaml
templatedValuesFiles:
- runtime-values.yaml
# After: chart entry
name: app
namespace: app
oci:
url: oci://ghcr.io/example/charts/app
ref:
tag: 1.0.0
valuesFiles:
- path: chart-values.yaml
- path: runtime-values.yaml
enableTemplating: true

Package-value mappings in charts[].values keep their sourcePath and targetPath fields.

Images

images entries are objects:

# Component fields
images:
- name: ghcr.io/example/app:1.0.0
source: registry

Images 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 fields
repositories:
- url: https://github.com/example/app.git
ref:
branch: main

Omit ref to mirror all branches and tags.