Add-Ons
Upbound's Add-Ons feature lets you build and deploy control plane software from the Kubernetes ecosystem. With the Add-Ons feature, you're not limited to just managing resource types defined by Crossplane. Now you can create resources from CustomResourceDefinitions defined by other Kubernetes ecosystem tooling.
Add-Ons are a package type that's only available for Upbound Crossplane v2 (UXP v2)
Benefits​
The Add-Ons feature provides the following benefits:
- Deploy Model Context Protocol (MCP) servers to deliver more powerful intelligent control in compositions and operations.
 - Deploy control plane software from the Kubernetes ecosystem.
 - Use your control plane's package manager to handle the lifecycle of the control plane software and define dependencies between package.
 - Build powerful compositions that combine both Crossplane and Kubernetes CustomResources.
 
How it works​
An AddOn is a package type that bundles control plane software from the Kubernetes ecosystem. Examples of such software includes:
- Kubernetes policy engines
 - CI/CD tooling
 - Your own private custom controllers defined by your organization
 
You can discover Add-Ons in the Upbound Marketplace or build your own package.
You build an AddOn package by wrapping a helm chart along with its requisite CustomResourceDefinitions. Your AddOn package gets pushed to an OCI registry. From there you can apply it to a control plane like you would any other Crossplane package. Your control plane's package manager is responsible for managing the lifecycle of the software once applied.
Build your own AddOn​
Prerequisites​
Packaging an AddOn requires up CLI  v0.40.0 or later.
Build an AddOn package​
AddOns are a package type that get administered by your control plane's package manager.
Prepare the package​
To define an AddOn, you need a Helm chart. This guide assumes the control plane software you want to build into an AddOn already has a Helm chart available.
Start by making a working directory to assemble the necessary parts:
mkdir addon-package
cd addon-package
Inside the working directory, pull the Helm chart
export CHART_REPOSITORY=<helm-chart-repo>
export CHART_NAME=<helm-chart-name>
export CHART_VERSION=<helm-chart-version>
helm pull $CHART_NAME --repo $CHART_REPOSITORY --version $CHART_VERSION
Move the Helm chart into it's own folder:
mkdir helm
mv $CHART_NAME-$CHART_VERSION.tgz helm/chart.tgz
Unpack the CRDs from the Helm chart into their own directory:
export RELEASE_NAME=<helm-release-name>
export RELEASE_NAMESPACE=<helm-release-namespace>
mkdir crds
helm template $RELEASE_NAME helm/chart.tgz -n $RELEASE_NAMESPACE --include-crds | \
  yq e 'select(.kind == "CustomResourceDefinition")' - | \
  yq -s '("crds/" + .metadata.name + ".yaml")' -
The preceding instructions assume your CRDs get deployed as part of your Helm chart. If they're deployed another way, you need to manually copy your CRDs instead.
Create a crossplane.yaml with your AddOn metadata:
cat <<EOF > crossplane.yaml
apiVersion: meta.pkg.upbound.io/v1beta1
kind: AddOn
metadata:
  annotations:
    friendly-name.meta.crossplane.io: Add-On <your-add-on>
    meta.crossplane.io/description: |
      A brief description of what the Add-On does.
    meta.crossplane.io/license: Apache-2.0
    meta.crossplane.io/maintainer: <your-email>
    meta.crossplane.io/readme: |
      An explanation of your Add-On.
      meta.crossplane.io/source: <url-for-your-add-on-source>
  name: <add-on-name>
spec:
  packagingType: Helm
  helm:
    releaseName: <release-name>
    releaseNamespace: <release-namespace>
    # Value overrides for the helm release can be provided below.
    # values:
    #   foo: bar
EOF
Your Add-On's file structure should look like this:
.
├── crds
│   ├── your-crd.yaml
│   ├── second-crd.yaml
│   └── another-crd.yaml
├── crossplane.yaml
└── helm
    └── chart.tgz
Create a ClusterRole or Role for your AddOn​
To deploy AddOns in your cluster, you must configure the
upbound-controller-manager service account with the necessary permissions.
The specific RBAC requirements vary depending on your AddOn and the Kubernetes resources it manages.
To create the correct permissions, you need to:
- Define your RBAC permissions with a 
RoleorClusterRolethat grants access to the resources your AddOn manages - Bind permissions to the 
upbound-controller-managerServiceAccount in thecrossplane-systemnamespace 
An example ClusterRoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: upbound-controller-manager-addons
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: <your_addon_role>
subjects:
  - kind: ServiceAccount
    name: upbound-controller-manager
    namespace: crossplane-system
Replace <your_addon_role> with the name of the ClusterRole or Role with
permissions your AddOn requires.
Package and push the AddOn​
At the root of your Add-On's working directory, build the contents into an xpkg:
up xpkg build
This causes an xpkg to get saved to your current directory with a name like addon-f7091386b4c0.xpkg.
Push the package to your desired OCI registry:
export UPBOUND_ACCOUNT=<org-account-name>
export ADD_ON_NAME=<add-on-name>
export ADD_ON_VERSION=<add-on-version>
export XPKG_FILENAME=<addon-f7091386b4c0.xpkg>
up xpkg push xpkg.upbound.io/$UPBOUND_ACCOUNT/$ADD_ON_NAME:$ADD_ON_VERSION -f $XPKG_FILENAME
Deploy an AddOn package​
AddOns are only installable on control planes running Upbound Crossplane v1.20.0 or later.
Set your kubecontext to the desired control plane in Upbound. Change the package path to the OCI registry you pushed it to. Then, deploy the AddOn directly:
export ADD_ON_NAME=<add-on-name>
export ADD_ON_VERSION=<add-on-version>
cat <<EOF | kubectl apply -f -
apiVersion: pkg.upbound.io/v1beta1
kind: AddOn
metadata:
  name: $ADD_ON_NAME
spec:
  package: xpkg.upbound.io/$UPBOUND_ACCOUNT/$ADD_ON_NAME:$ADD_ON_VERSION
EOF
Example usage​
The example below demonstrates step-by-step how to package and deploy Argo CD to a control plane in Upbound.
Prepare to package ArgoCD​
Start by making a working directory to assemble the necessary parts:
mkdir argo-package
cd argo-package
Inside the working directory, pull the Helm chart
export CHART_REPOSITORY=https://argoproj.github.io/argo-helm
export CHART_NAME=argo-cd
export CHART_VERSION=7.8.8
helm pull $CHART_NAME --repo $CHART_REPOSITORY --version $CHART_VERSION
Move the Helm chart into it's own folder:
mkdir helm
mv $CHART_NAME-$CHART_VERSION.tgz helm/chart.tgz
Unpack the CRDs from the Helm chart into their own directory:
export RELEASE_NAME=argo-cd
export RELEASE_NAMESPACE=argo-system
mkdir crds
helm template $RELEASE_NAME helm/chart.tgz -n $RELEASE_NAMESPACE --include-crds | \
  yq e 'select(.kind == "CustomResourceDefinition")' - | \
  yq -s '("crds/" + .metadata.name + ".yaml")' -
Create a crossplane.yaml with the AddOn metadata:
cat <<EOF > crossplane.yaml
apiVersion: meta.pkg.upbound.io/v1beta1
kind: AddOn
metadata:
  annotations:
    friendly-name.meta.crossplane.io: Add-On ArgoCD
    meta.crossplane.io/description: |
      The ArgoCD Add-On enables continuous delivery and declarative configuration
      management for Kubernetes applications using GitOps principles.
    meta.crossplane.io/license: Apache-2.0
    meta.crossplane.io/maintainer: Upbound Maintainers <info@upbound.io>
    meta.crossplane.io/readme: |
      ArgoCD is a declarative GitOps continuous delivery tool for Kubernetes that
      follows the GitOps methodology to manage infrastructure and application
      configurations.
      meta.crossplane.io/source: https://github.com/argoproj/argo-cd
  name: argocd
spec:
  packagingType: Helm
  helm:
    releaseName: argo-cd
    releaseNamespace: argo-system
    # values:
    #   foo: bar
EOF
Your Add-On's file structure should look like this:
.
├── crds
│   ├── applications.argoproj.io.yaml
│   ├── applicationsets.argoproj.io.yaml
│   └── appprojects.argoproj.io.yaml
├── crossplane.yaml
└── helm
    └── chart.tgz
Package and push addon-argocd​
At the root of your Add-On's working directory, build the contents into an xpkg:
up xpkg build
This causes an xpkg to get saved to your current directory with a name like argocd-f7091386b4c0.xpkg.
Push the package to your desired OCI registry:
export UPBOUND_ACCOUNT=<org-account-name>
export ADD_ON_NAME=addon-argocd
export ADD_ON_VERSION=v7.8.8
export XPKG_FILENAME=<addon-f7091386b4c0.xpkg>
up xpkg push --create xpkg.upbound.io/$UPBOUND_ACCOUNT/$ADD_ON_NAME:$ADD_ON_VERSION -f $XPKG_FILENAME
Deploy addon-argocd to a control plane​
Set your kubecontext to the desired control plane in Upbound. Change the package path to the OCI registry you pushed it to. Then, deploy the AddOn directly:
cat <<EOF | kubectl apply -f -
apiVersion: pkg.upbound.io/v1beta1
kind: AddOn
metadata:
  name: addon-argocd
spec:
  package: xpkg.upbound.io/$UPBOUND_ACCOUNT/addon-argocd:v7.8.8
EOF
Wait for the package to become ready:
watch kubectl get addons.pkg
Check the pods in the argo-system namespace:
kubectl -n argo-system get pods
You can now use the CustomResource types defined by Argo CD in your control plane.
Frequently asked questions​
How can I package my software as an AddOn?
Currently, we support Helm charts as the underlying package format for AddOns. As long as you have a Helm chart, you can package it as an AddOn.
If you don't have a Helm chart, you can't deploy the software. We only support Helm charts as the underlying package format for AddOns. We may extend this to support other packaging formats like Kustomize in the future.
Can I package Crossplane XRDs/Compositions as a Helm chart to deploy as an AddOn?
This is not recommended. For packaging Crossplane XRDs/ and Compositions, we recommend using the Configuration package format. A helm chart only with Crossplane XRDs/Compositions does not qualify as an AddOn.
How can I override the Helm values when deploying an AddOn?
Overriding the Helm values is possible at two levels:
- During packaging time, in the package manifest file.
 - At runtime, using a 
AddOnRuntimeConfigresource (similar to CrossplaneDeploymentRuntimeConfig). 
How can I configure the helm release name and namespace for the AddOn?
Right now, it is not possible to configure this at runtime. The package author configures release name and namespace during packaging, so it is hardcoded inside the package. Unlike a regular application that is deployed by a Helm chart, AddOns can only be deployed once in a given control plane, so, we hope it should be ok to rely on predefined release names and namespaces. We may consider exposing these in AddOnRuntimeConfig later, but, we would like to keep it opinionated unless there are strong reasons to do so.
Do I need a specific Crossplane version to run AddOns?
AddOn API is available in Upbound Crossplane (UXP) v2 and later.
Can I deploy AddOns outside of Upbound Crossplane?
No, AddOns are a proprietary package format and are only available for Upbound Crossplane (UXP) v2 and later. They are not compatible with upstream Crossplane.