Installing Cartographer

There are three recommended ways of installing Cartographer:

  1. with a single YAML file (quick!)
  2. using an imgpkg bundle, making easier for relocation
  3. using a carvel Packaging objects, for an opinionated package-based workflow

If all you want to do is get started quick and fetching images from DockerHub is not an issue, 1 is the way to go. Otherwise, check out the other approaches (especially if you want to have everything relocated to a registry of your own).

tl;dr (having cert-manager already installed):

kubectl create namespace cartographer-system
kubectl apply -f https://github.com/vmware-tanzu/cartographer/releases/latest/download/cartographer.yaml


Admin capabilities in a recent-enough Kubernetes cluster (v1.19+)

Currently, Cartographer doesn’t provide support for granular permissions (see [#51]), as such, to configure the supply chain (which is cluster-wide), you must have admin capabilities in the cluster you’re targetting.

cert-manager for managing the controller’s certificates

In order to have Cartographer’s validation webhooks up and running in the cluster, cert-manager is utilized to generate TLS certificates as well to keep them up to date.

First, verify if you have the dependency installed:

kubectl get crds certificates.cert-manager.io
NAME                           CREATED AT
certificates.cert-manager.io   2021-08-27T18:41:40Z

In case you don’t (i.e., you see ““certificates.cert-manager.io” not found”), proceed with installing it.


kapp deploy --yes -a cert-manager \
	-f https://github.com/jetstack/cert-manager/releases/download/v$CERT_MANAGER_VERSION/cert-manager.yaml
Target cluster '' (nodes: kind-control-plane)

Namespace     Name                     Kind               ...
(cluster)     cert-manager             Namespace          ...
^             cert-manager-cainjector  ClusterRole        ...
^             cert-manager-cainjector  ClusterRoleBinding ...
7:53:32AM: ok: reconcile customresourcedefinition/issuers.cert-manager.io (apiextensions.k8s.io/v1) cluster
7:53:32AM: ---- applying complete [6/6 done] ----
7:53:32AM: ---- waiting complete [6/6 done] ----


ps.: although we recommend using kapp as provided in the instructions you’ll see here, its use can be replaced by kubectl apply.

1. Single file, Quick install

Cartographer releases include a file (cartographer.yaml) that contains all necessary files for installing it in a cluster

Resources in file './bundle/cartographer.yaml'

Namespace            Name                              Kind
(cluster)            cartographer-cluster-admin        ClusterRoleBinding
^                    clusterconfigtemplates.carto.run  CustomResourceDefinition
^                    clusterimagetemplates.carto.run   CustomResourceDefinition
^                    clustersourcetemplates.carto.run  CustomResourceDefinition
^                    clustersupplychains.carto.run     CustomResourceDefinition
^                    clustersupplychainvalidator       ValidatingWebhookConfiguration
^                    clustertemplates.carto.run        CustomResourceDefinition
^                    pipelines.carto.run               CustomResourceDefinition
^                    runtemplates.carto.run            CustomResourceDefinition
^                    workloads.carto.run               CustomResourceDefinition

cartographer-system  cartographer-controller           Deployment
^                    cartographer-controller           ServiceAccount
^                    cartographer-webhook              Certificate
^                    cartographer-webhook              Secret
^                    cartographer-webhook              Service
^                    private-registry-credentials      Secret
^                    selfsigned-issuer                 Issuer

providing all the necessary objects that would entail a full installation of it.

To install it, first create the namespace where the controller will be installed:

kubectl create namespace cartographer-system
namespace/cartographer-system created

and then, submit the objects included in the release:

kubectl apply -f https://github.com/vmware-tanzu/cartographer/releases/latest/download/cartographer.yaml
customresourcedefinition.apiextensions.k8s.io/clusterconfigtemplates.carto.run configured
customresourcedefinition.apiextensions.k8s.io/clusterimagetemplates.carto.run configured
secret/cartographer-webhook created

2. Bundle tarball

This installation method is great for those that want to host Cartographer’s image and installation objects by themselves - either so they live in a public container image registry of their own, or for air-gapped scenarios where bits need to be moved in an offline fashion.

First, head to the releases page and download the bundle.tar file available for the release you want to install.

curl -SOL https://github.com/vmware-tanzu/cartographer/releases/latest/download/bundle.tar

This bundle contains everything we need to install Cartographer, from container images to Kubernetes manifests, it’s all in the bundle.

First, relocate it from the tarball you just downloaded to a container image registry that the cluster you’re willing to install Cartographer has access to:

# set the repository where images should be reloated to, for instance, to
# relocate to a project named "foo" in DockerHub: DOCKER_REPO=foo

# relocate
imgpkg copy \
  --tar bundle.tar \
  --to-repo ${DOCKER_REPO?:Required}/cartographer-bundle \
  --lock-output cartographer-bundle.lock.yaml
copy | importing 2 images...
copy | done uploading images

With the the bundle and all Cartographer-related images moved to the destination registry, we can move on to pulling the YAML files that we can use to submit to Kubernetes for installing Cartographer:

# pull to the directory `cartographer-bundle` the contents of the imgpkg
# bundle as specified by the lock file.
imgpkg pull \
  --lock cartographer-bundle.lock.yaml \
  --output ./cartographer-bundle
Pulling bundle ''
  Extracting layer 'sha256:0b93d1878c9be97b872f08da8d583796985919df345c39c874766142464d80e7' (1/1)

Locating image lock file images...
The bundle repo ( is hosting every image specified in the bundle's Images Lock file (.imgpkg/images.yml)


Create the namespace where Cartographer’s objects will be placed:

kubectl create namespace cartographer-system
namespace/cartographer-system created

If the registry you pushed the bundle to is accessible to the cluster without any extra authentication needs, skip this step. Otherwise, make sure you provide to the Deployment object that runs the controller the image pull secrets necessary for fetching the image.

By default, the controller’s ServiceAccount points at a placeholder secret called ‘private-registry-credentials’ to be used as the image pull secret, so, by creating such secret in the cartographer-system namespace Kubernetes will then be able to use that secret as the credenital provider for fetching the controller’s image:

# create a secret that will have .dockerconfigjson populated with the
# credentials for the image registry.
kubectl create secret -n cartographer-system \
  docker-registry private-registry-credentials \
  --docker-server=$DOCKER_REPO \
  --docker-username=admin \
secret/private-registry-credentials created

Now that we have the Kubernetes YAML under the ./cartographer-bundle directory, we can make use of a combination of kbld and kapp to submit the Kubernetes the final objects that will define the installation of Cartographer already pointing all the image references to your registry:

# submit to kubernetes the kubernetes objects that describe the installation of
# Cartographer already pointing all images to the registry we configured
kapp deploy -a cartographer -f <(kbld -f ./cartographer-bundle)
resolve | final: ->
Target cluster '' (nodes: cartographer-control-plane)


Namespace            Name                              Kind                      Conds.  Age  Op      Op st.  Wait to    Rs  Ri
(cluster)            clusterconfigtemplates.carto.run  CustomResourceDefinition  0/0 t   49s  update  -       reconcile  ok  -
^                    clusterimagetemplates.carto.run   CustomResourceDefinition  0/0 t   49s  update  -       reconcile  ok  -
^                    clustersourcetemplates.carto.run  CustomResourceDefinition  0/0 t   49s  update  -       reconcile  ok  -
^                    clustersupplychains.carto.run     CustomResourceDefinition  0/0 t   48s  update  -       reconcile  ok  -

6:34:57PM: ok: reconcile customresourcedefinition/clustersupplychains.carto.run (apiextensions.k8s.io/v1) cluster
6:34:57PM: ---- applying complete [11/11 done] ----
6:34:57PM: ---- waiting complete [11/11 done] ----


(see the Kubernetes official documentation on how to create a Secret to pull images from a private Docker registry or repository).

3. Installation using Carvel Packaging

Another way that you can go about installing Cartographer is with the use of carvel Packaging provided by [kapp controller]. These, when used alongside [secretgen controller], provide a great experience for, in a declarative way, installing Cartographer.

To make use of them, first, make sure those pre-requisites above are satified


  1. admin access to a Kubernetes Cluster and cert-manager

  2. kapp-controller is already installed in the cluster

kubectl get crd packageinstalls.packaging.carvel.dev
NAME                                   CREATED AT
packageinstalls.packaging.carvel.dev   2021-09-13T14:32:00Z

In case you don’t (i.e., you see “packageinstalls.packaging.carvel.dev” not found), proceed with installing it.


kapp deploy --yes -a kapp-controller \
  -f https://github.com/vmware-tanzu/carvel-kapp-controller/releases/download/v$KAPP_CONTROLLER_VERSION/release.yml
Target cluster '' (nodes: cartographer-control-plane)


Namespace        Name                                                    Kind
(cluster)        apps.kappctrl.k14s.io                                   CustomResourceDefinition
^                internalpackagemetadatas.internal.packaging.carvel.dev  CustomResourceDefinition
^                internalpackages.internal.packaging.carvel.dev          CustomResourceDefinition
^                kapp-controller                                         Namespace

2:56:08PM: ---- waiting on 1 changes [14/15 done] ----
2:56:13PM: ok: reconcile apiservice/v1alpha1.data.packaging.carvel.dev (apiregistration.k8s.io/v1) cluster
2:56:13PM: ---- applying complete [15/15 done] ----
2:56:13PM: ---- waiting complete [15/15 done] ----

  1. [secretgen-controller] installed
kubectl get crd secretexports.secretgen.carvel.dev
NAME                                 CREATED AT
secretexports.secretgen.carvel.dev   2021-09-20T18:10:10Z

In case you don’t (i.e., you see “secretexports.secretgen.carvel.dev” not found), proceed with installing it.


kapp deploy --yes -a secretgen-controller \
  -f https://github.com/vmware-tanzu/carvel-secretgen-controller/releases/download/v$SECRETGEN_CONTROLLER_VERSION/release.yml
Target cluster '' (nodes: cartographer-control-plane)


Namespace             Name                                       Kind                      Conds.  Age  Op      Op st.  Wait to    Rs  Ri
(cluster)             certificates.secretgen.k14s.io             CustomResourceDefinition  -       -    create  -       reconcile  -   -
^                     passwords.secretgen.k14s.io                CustomResourceDefinition  -       -    create  -       reconcile  -   -
^                     rsakeys.secretgen.k14s.io                  CustomResourceDefinition  -       -    create  -       reconcile  -   -
6:13:25PM: ok: reconcile deployment/secretgen-controller (apps/v1) namespace: secretgen-controller
6:13:25PM: ---- applying complete [11/11 done] ----
6:13:25PM: ---- waiting complete [11/11 done] ----

  1. the default service account has the capabilities necessary for installing submitting all those objects above to the cluster
kubectl create clusterrolebinding default-cluster-admin \
	--clusterrole=cluster-admin \
clusterrolebinding.rbac.authorization.k8s.io/default-cluster-admin created

Package installation

With the prerequisites satisfied, go ahead and download the package* files, as well as the imgpkg bundle:


curl -SOL https://github.com/vmware-tanzu/cartographer/releases/download/v$CARTOGRAPHER_VERSION/bundle.tar
curl -SOL https://github.com/vmware-tanzu/cartographer/releases/download/v$CARTOGRAPHER_VERSION/package.yaml
curl -SOL https://github.com/vmware-tanzu/cartographer/releases/download/v$CARTOGRAPHER_VERSION/package-metadata.yaml
curl -SOL https://github.com/vmware-tanzu/cartographer/releases/download/v$CARTOGRAPHER_VERSION/package-install.yaml

First, relocate the bundle:

# set the repository where images should be reloated to, for instance, to
# relocate to a project named "foo" in DockerHub: DOCKER_REPO=foo

# relocate
imgpkg copy \
  --tar bundle.tar \
  --to-repo ${DOCKER_REPO?:Required}/cartographer-bundle \
  --lock-output cartographer-bundle.lock.yaml

Now that the bundle is in our repository, update the Package object to point at it (use the image from cartographer-bundle.lock.yaml):

  apiVersion: data.packaging.carvel.dev/v1alpha1
  kind: Package
        - imgpkgBundle:
-           image: IMAGE
+           image:

That done, submit the packaging objects to Kubernetes so that kapp-controller will materialize them into an installation of Cartographer:

kapp deploy --yes -a cartographer \
  -f ./package-metadata.yaml \
  -f ./package.yaml \
  -f ./package-install.yaml
Target cluster '' (nodes: cartographer-control-plane)


Namespace  Name                              Kind             Conds.  Age  Op      Op st.  Wait to    Rs  Ri
default    cartographer.carto.run            PackageMetadata  -       -    create  -       reconcile  -   -
^          cartographer.carto.run.0.0.0-dev  Package          -       -    create  -       reconcile  -   -
^          cartographer.carto.run.0.0.0-dev  PackageInstall   -       -    create  -       reconcile  -   -


1:14:44PM: ---- applying 2 changes [0/3 done] ----
1:14:44PM: create packagemetadata/cartographer.carto.run (data.packaging.carvel.dev/v1alpha1) namespace: default
1:14:54PM: ok: reconcile packageinstall/cartographer.carto.run.0.0.0-dev (packaging.carvel.dev/v1alpha1) namespace: default
1:14:54PM: ---- applying complete [3/3 done] ----
1:14:54PM: ---- waiting complete [3/3 done] ----


if you relocated the images here to a private registry that requires authentication, make sure you create a Secret with the credentials to the registry as well as a SecretExport object to make those credentials available to other namespaces.

apiVersion: v1
kind: Secret
  name: shared-registry-credentials
type: kubernetes.io/dockerconfigjson # needs to be this type
  .dockerconfigjson: |
            "auths": {
                    "<registry>": {
                            "username": "<username>",
                            "password": "<password>"

apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretExport
  name: shared-registry-credentials
    - "*"