Check out what we shared during KubeCon + CloudNativeCon North America 2021 keynote!
Carvel Logo

Take control of your Kubernetes resources

Deploy and view groups of Kubernetes resources as "applications". Apply changes safely and predictably, watching resources as they converge.

Lightweight

kapp is a CLI that does not require server-side components, elevated privileges nor any custom resources. It works well in RBAC-constrained clusters.

Explicit

kapp calculates changes between your configuration and live cluster state (a set of create, update, and delete operations) and applies changes you approve. It reports how resources are converging to their desired state providing insight into progress of the deployment.

Dependency-aware

kapp orders certain resources (e.g. CRDs and Namespaces are installed before the resources that need them). Add your own rules to declare dependencies (e.g. a Job running DB migrations must finish before the Deployment is updated).

Features

Sorts and waits as needed.

Deploys dependencies first (like CRDs and Namespaces) and supports custom change ordering. Waits for resources to "reconcile."

Plays well with others.

Focuses exclusively on deployment procedure and works equally well with configuration tools such as ytt, kustomize, helm template, and any other tool that can produce standard Kubernetes YAML configuration.

GitOps friendly

Include in your GitOps flow via CLI command or kapp-controller

Basic Usage

# Configurations picked up from a directory
$ kapp deploy -a my-app -f ./examples/simple-app-example/config-1.yml
# Can be used with helm charts, removing need for Tiller
$ kapp -y deploy -a my-chart -f <(helm template my-chart --values my-vals.yml)
# ... and with kustomize
$ kapp -y deploy -a my-app -f <(kustomize build ./my-app)
# ... or templated with ytt
$ kapp -y deploy -a my-app -f <(ytt -f ./examples/simple-app-example/config-1.yml)

Playground and Example Workflow

You can follow along in Katacoda Kubernetes playground while going through below workflow. To set up playground:

  1. Open Katacoda Playground
  2. Click "Start scenario" button
  3. Focus in "Terminal Host 1" prompt
  4. Click "launch.sh". It will set up Kubernetes cluster
  5. Click "kubectl cluster-info" to ensure that kubernetes cluster is set up successfully
  6. Install kapp in playground before continuing with the workflow
$ wget -O- https://carvel.dev/kapp-install-katacoda.sh | bash
$ cd carvel-kapp/
$ kapp version

And now onto using kapp:

In this example, we are installing an example app from the kapp repository.

Note that in the changes summary we see that all the resources to be deployed are new.

$ kapp deploy -a app1 -f examples/simple-app-example/config-1.yml

Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-pool-1-d01bcd06-bgie, 4+)

Changes

Namespace  Name        Kind        Conds.  Age  Op      Op st.  Wait to    Rs  Ri
default    simple-app  Deployment  -       -    create  -       reconcile  -   -
^          simple-app  Service     -       -    create  -       reconcile  -   -

Op:      2 create, 0 delete, 0 update, 0 noop
Wait to: 2 reconcile, 0 delete, 0 noop
2:12:59PM: ---- applying 2 changes [0/2 done] ----
2:13:00PM: create service/simple-app (v1) namespace: default
2:13:01PM: create deployment/simple-app (apps/v1) namespace: default
2:13:01PM: ---- waiting on 2 changes [0/2 done] ----
2:13:01PM: ok: reconcile service/simple-app (v1) namespace: default
2:13:01PM: ongoing: reconcile deployment/simple-app (apps/v1) namespace: default
2:13:01PM:  ^ Waiting for 1 unavailable replicas
2:13:01PM:  L ok: waiting on replicaset/simple-app-7f9c78ccd8 (apps/v1) namespace: default
2:13:01PM:  L ongoing: waiting on pod/simple-app-7f9c78ccd8-h7wlc (v1) namespace: default
2:13:01PM:     ^ Pending: ContainerCreating
2:13:01PM: ---- waiting on 1 changes [1/2 done] ----
2:13:03PM: ok: reconcile deployment/simple-app (apps/v1) namespace: default
2:13:03PM: ---- applying complete [2/2 done] ----
2:13:03PM: ---- waiting complete [2/2 done] ----

Succeeded

The ls command shows a list of all apps stored in the current namespace. Apps themselves can span multiple namespaces and cluster scope.

$ kapp ls

Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-pool-1-d01bcd06-bgie, 4+)

Apps in namespace 'default'

Name  Namespaces  Lcs   Lca
app1  default     true  25s

Lcs: Last Change Successful
Lca: Last Change Age

1 apps

Succeeded

Now let's update from one app configuration to another using the deploy command.

In this example, since --diff-changes is used, kapp will show a detailed diff against versions of the resources that exist in the cluster. The first two resource diffs indicate they are brand new and will be created. The next resource in the diff will be updated according to the changes shown. Finally, the last resource diff shows it will be deleted.

As above, the deploy will then display a list of changes before continuing. Once the user has chosen to continue, we see a series of progress logs and finally a success message.

$ kapp deploy -a app1 -f examples/simple-app-example/config-2.yml --diff-changes

Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-pool-1-d01bcd06-bgie, 4+)

@@ create service/simple-app-node (v1) namespace: default @@
    0 + apiVersion: v1
    1 + kind: Service
    2 + metadata:
    3 +   labels:
    4 +     kapp.k14s.io/app: "1594663909283037000"
    5 +     kapp.k14s.io/association: v1.6e43c1cd2c7173a969d84f77ad6a72c2
    6 +   name: simple-app-node
    7 +   namespace: default
    8 + spec:
    9 +   ports:
    10 +   - nodePort: 31111
    11 +     port: 80
    12 +     targetPort: 80
    13 +   selector:
    14 +     kapp.k14s.io/app: "1594663909283037000"
    15 +     simple-app: ""
    16 +   type: NodePort
    17 +
@@ create configmap/simple-app (v1) namespace: default @@
    0 + apiVersion: v1
    1 + data:
    2 +   hello_msg: friend
    3 + kind: ConfigMap
    4 + metadata:
    5 +   labels:
    6 +     kapp.k14s.io/app: "1594663909283037000"
    7 +     kapp.k14s.io/association: v1.afcdfc989e2a0b3f3e526dc1c4176060
    8 +   name: simple-app
    9 +   namespace: default
    10 +
@@ update deployment/simple-app (apps/v1) namespace: default @@
...
29, 29           - name: HELLO_MSG
30     -           value: stranger
    30 +           valueFrom:
    31 +             configMapKeyRef:
    32 +               key: hello_msg
    33 +               name: simple-app
31, 34           image: docker.io/dkalinin/k8s-simple-app@sha256:4c8b96d4fffdfae29258d94a22ae4ad1f...
32, 35           name: simple-app
@@ delete service/simple-app (v1) namespace: default @@
    0     - apiVersion: v1
    1     - kind: Service
    2     - metadata:
    3     -   creationTimestamp: "2020-07-13T18:12:59Z"
    4     -   labels:
    5     -     kapp.k14s.io/app: "1594663909283037000"
    6     -     kapp.k14s.io/association: v1.462a95c01b2a0bdc4d5acadc4a22dd74
    7     -   name: simple-app
    8     -   namespace: default
    9     -   resourceVersion: "69009086"
    10     -   selfLink: /api/v1/namespaces/default/services/simple-app
    11     -   uid: 4e8bb148-2de4-46b3-a2e1-53dacbaaff34
    12     - spec:
    13     -   clusterIP: 10.76.6.94
    14     -   ports:
    15     -   - port: 80
    16     -     targetPort: 80
    17     -   selector:
    18     -     kapp.k14s.io/app: "1594663909283037000"
    19     -     simple-app: ""
    20     - status:
    21     -   loadBalancer: {}
    22     -
Changes

Namespace  Name             Kind        Conds.  Age  Op      Op st.  Wait to    Rs  Ri
default    simple-app       ConfigMap   -       -    create  -       reconcile  -   -
^          simple-app       Deployment  2/2 t   2m   update  -       reconcile  ok  -
^          simple-app       Service     -       2m   delete  -       delete     ok  -
^          simple-app-node  Service     -       -    create  -       reconcile  -   -

Op:      2 create, 1 delete, 1 update, 0 noop
Wait to: 3 reconcile, 1 delete, 0 noop
Continue? [yN]:

2:21:15PM: ---- applying 2 changes [0/4 done] ----
2:21:15PM: delete service/simple-app (v1) namespace: default
2:21:15PM: create configmap/simple-app (v1) namespace: default
2:21:15PM: ---- waiting on 2 changes [0/4 done] ----
2:21:15PM: ok: delete service/simple-app (v1) namespace: default
2:21:15PM: ok: reconcile configmap/simple-app (v1) namespace: default
2:21:15PM: ---- applying 2 changes [2/4 done] ----
2:21:15PM: create service/simple-app-node (v1) namespace: default
2:21:17PM: update deployment/simple-app (apps/v1) namespace: default
2:21:17PM: ---- waiting on 2 changes [2/4 done] ----
2:21:17PM: ok: reconcile service/simple-app-node (v1) namespace: default
2:21:17PM: ongoing: reconcile deployment/simple-app (apps/v1) namespace: default
2:21:17PM:  ^ Waiting for 1 unavailable replicas
2:21:17PM:  L ok: waiting on replicaset/simple-app-8558f78495 (apps/v1) namespace: default
2:21:17PM:  L ok: waiting on replicaset/simple-app-7f9c78ccd8 (apps/v1) namespace: default
2:21:17PM:  L ok: waiting on pod/simple-app-8558f78495-zqwxt (v1) namespace: default
2:21:17PM:  L ongoing: waiting on pod/simple-app-7f9c78ccd8-h7wlc (v1) namespace: default
2:21:17PM:     ^ Deleting
2:21:17PM: ---- waiting on 1 changes [3/4 done] ----
2:21:18PM: ok: reconcile deployment/simple-app (apps/v1) namespace: default
2:21:18PM: ---- applying complete [4/4 done] ----
2:21:18PM: ---- waiting complete [4/4 done] ----

Succeeded

The inspect command can be used to view a list of live objects associated with given app. --tree view optionally shows simple parent-child resource relationships.

$ kapp inspect -a app1 --tree

Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-pool-1-d01bcd06-bgie, 4+)

Resources in app 'app'

Namespace  Name                              Kind        Owner    Conds.  Rs  Ri  Age
default    simple-app                        Deployment  kapp     2/2 t   ok  -   8m
default     L simple-app-7f9c78ccd8          ReplicaSet  cluster  -       ok  -   8m
default     L simple-app-8558f78495          ReplicaSet  cluster  -       ok  -   28s
default     L.. simple-app-8558f78495-zqwxt  Pod         cluster  4/4 t   ok  -   28s
default    simple-app-node                   Service     kapp     -       ok  -   28s
default     L simple-app-node                Endpoints   cluster  -       ok  -   28s
default    simple-app                        ConfigMap   kapp     -       ok  -   28s

Rs: Reconcile state
Ri: Reconcile information

7 resources

Succeeded

The logs command starts streaming logs from all Pods associated with the given app.

$ kapp logs -f -a app1

Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-pool-1-d01bcd06-bgie, 4+)

# starting tailing 'simple-app-8558f78495-zqwxt &gt; simple-app' logs
simple-app-8558f78495-zqwxt &gt; simple-app | 2020/07/13 18:21:16 Server started
^C

The delete command finds all resources in the cluster associated with the given app, issues delete operation, and waits for them be be deleted.

$ kapp delete -a app1

Target cluster 'https://x.x.x.x' (nodes: gke-dk-jan-9-pool-1-d01bcd06-bgie, 4+)

Changes

Namespace  Name                         Kind        Conds.  Age  Op      Op st.  Wait to  Rs  Ri
default    simple-app                   ConfigMap   -       1m   delete  -       delete   ok  -
^          simple-app                   Deployment  2/2 t   10m  delete  -       delete   ok  -
^          simple-app-7f9c78ccd8        ReplicaSet  -       10m  -       -       delete   ok  -
^          simple-app-8558f78495        ReplicaSet  -       1m   -       -       delete   ok  -
^          simple-app-8558f78495-zqwxt  Pod         4/4 t   1m   -       -       delete   ok  -
^          simple-app-8558f78495-zqwxt  PodMetrics  -       0s   -       -       delete   ok  -
^          simple-app-node              Endpoints   -       1m   -       -       delete   ok  -
^          simple-app-node              Service     -       1m   delete  -       delete   ok  -

Op:      0 create, 3 delete, 0 update, 5 noop
Wait to: 0 reconcile, 8 delete, 0 noop

Continue? [yN]: y

2:23:24PM: ---- applying 8 changes [0/8 done] ----
2:23:24PM: noop endpoints/simple-app-node (v1) namespace: default
2:23:24PM: noop replicaset/simple-app-7f9c78ccd8 (apps/v1) namespace: default
2:23:24PM: noop pod/simple-app-8558f78495-zqwxt (v1) namespace: default
2:23:24PM: noop podmetrics/simple-app-8558f78495-zqwxt (metrics.k8s.io/v1beta1) namespace: default
2:23:24PM: noop replicaset/simple-app-8558f78495 (apps/v1) namespace: default
2:23:24PM: delete deployment/simple-app (apps/v1) namespace: default
2:23:24PM: delete service/simple-app-node (v1) namespace: default
2:23:24PM: delete configmap/simple-app (v1) namespace: default
2:23:24PM: ---- waiting on 8 changes [0/8 done] ----
2:23:24PM: ongoing: delete replicaset/simple-app-7f9c78ccd8 (apps/v1) namespace: default
2:23:24PM: ok: delete endpoints/simple-app-node (v1) namespace: default
2:23:24PM: ok: delete configmap/simple-app (v1) namespace: default
2:23:24PM: ongoing: delete replicaset/simple-app-8558f78495 (apps/v1) namespace: default
2:23:24PM: ongoing: delete pod/simple-app-8558f78495-zqwxt (v1) namespace: default
2:23:24PM: ok: delete service/simple-app-node (v1) namespace: default
2:23:24PM: ok: delete deployment/simple-app (apps/v1) namespace: default
2:23:24PM: ongoing: delete podmetrics/simple-app-8558f78495-zqwxt (metrics.k8s.io/v1beta1) namespa...
2:23:24PM: ---- waiting on 4 changes [4/8 done] ----
2:23:26PM: ok: delete replicaset/simple-app-8558f78495 (apps/v1) namespace: default
2:23:26PM: ok: delete replicaset/simple-app-7f9c78ccd8 (apps/v1) namespace: default
2:23:26PM: ---- waiting on 2 changes [6/8 done] ----
2:23:27PM: ok: delete podmetrics/simple-app-8558f78495-zqwxt (metrics.k8s.io/v1beta1) namespace: d...
2:23:27PM: ok: delete pod/simple-app-8558f78495-zqwxt (v1) namespace: default
2:23:27PM: ---- applying complete [8/8 done] ----
2:23:27PM: ---- waiting complete [8/8 done] ----

Succeeded

Getting started

To help you get started, see the documentation.