Carvel Logo

Basic Workflow


You want to create an immutable artifact containing Kubernetes configuration and images used in that configuration. Later, you want to grab that artifact and deploy it to Kubernetes.


To complete this workflow you will need access to an OCI registry like Docker Hub, and optionally, a Kubernetes cluster. (If you would like to use a local registry and Kubernetes cluster, try using Kind)

If you would like to deploy the results of this scenario to your Kubernetes cluster, you will additionally need kbld and kubectl.

Step 1: Creating the bundle

  1. Prepare bundle contents

    The examples/basic-step-1/ directory has a config.yml file, which contains a very simple Kubernetes application. Your application may have as many configuration files as necessary in various formats such as plain YAML, ytt templates, Helm templates, etc.

    In our example config.yml includes an image reference to This reference does not point to an exact image (via digest) meaning that it may change over time. To ensure we get precisely the bits we expect, we will lock it down to an exact image next.

  2. Add .imgpkg/ directory

    examples/basic-step-2 shows what a .imgpkg/ directory may look like. It contains:

    • optional bundle.yml: a file which records informational metadata
    • required images.yml: a file which records image references used by the configuration
    ├── .imgpkg
    │   ├── bundle.yml
    │   └── images.yml
    └── config.yml

    Note that .imgpkg/images.yml contains a list of images, each with fully resolved digest reference (e.g and a little bit of additional metadata (e.g. annotations section). See ImagesLock configuration for details.

    kind: ImagesLock
    - image:

    This allows us to record the exact image that will be used by our Kubernetes configuration. We expect that .imgpkg/images.yml would be created either manually, or in an automated way. Our recommendation is to use kbld to generate .imgpkg/images.yml:

    $ cd examples/basic-bundle/
    $ kbld -f config.yml --imgpkg-lock-output .imgpkg/images.yml

Step 2: Pushing the bundle to a registry

  1. Authenticate with a registry where we will push our bundle

  2. Push the bundle to the registry

    You can push the bundle with our specified contents to an OCI registry using the following command:

    $ imgpkg push -b -f examples/basic-step-2
    dir: .
    dir: .imgpkg
    file: .imgpkg/bundle.yml
    file: .imgpkg/images.yml
    file: config.yml
    Pushed ''

    Flags used in the command:

    • -b (--bundle) refers to a location for a bundle within an OCI registry
    • -f (--file) indicates directory contents to include
  3. The pushed bundle is now available at

Step 3: Pulling the bundle from registry

Now that we have pushed a bundle to a registry, other users can pull it.

  1. Authenticate with the registry from which we’ll pull our bundle

  2. Download the bundle by running the following command:

    $ imgpkg pull -b -o  /tmp/simple-app-bundle
    Pulling image ''
    Extracting layer 'sha256:7906b9650be657359ead106e354f2728e16c8f317e1d87f72b05b5c5ec3d89cc' (1/1)
    Locating image lock file images...
    One or more images not found in bundle repo; skipping lock file update

    Flags used in the command:

    • -b (--bundle) refers to a location for a bundle within an OCI registry
    • -o (--output) indicates the destination directory on your local machine where the bundle contents will be placed

    Bundle contents will be extracted into /tmp/simple-app-bundle directory:

    ├── .imgpkg
    │   ├── bundle.yml
    │   └── images.yml
    └── config.yml

    Note: The message One or more images not found in bundle repo; skipping lock file update is expected, and indicates that /tmp/simple-app-bundle/.imgpkg/images.yml (ImagesLock configuration) was not modified.

    If imgpkg had been able to find all images that were referenced in the ImagesLock configuration in the registry where bundle is located, then it would update .imgpkg/images.yml file to point to the registry-local locations.

    See what happens to the lock file if you run the same pull command after copying the bundle to another registry!

Step 4: Use pulled bundle contents

  1. Now that we have have pulled bundle contents to a local directory, we can deploy Kubernetes configuration:

    Before we apply Kubernetes configuration, let’s use kbld to ensure that Kubernetes configuration uses exact image reference from .imgpkg/images.yml. (You can of course use other tools to take advantage of data stored in .imgpkg/images.yml).

    $ cd /tmp/simple-app-bundle/
    $ kbld -f ./config.yml -f .imgpkg/images.yml | kubectl apply -f-
    resolve | final: ->
    resolve | final: ->
    service/simple-app configured
    deployment/simple-app configured

    kbld found in Kubernetes configuration and replaced it with before forwarding configuration to kubectl.

Next steps

In this workflow we saw how to publish and download a bundle to distribute a Kubernetes application. Next, follow the Air-gapped workflow to see how we can use the imgpkg copy command to copy a bundle between registries.

(Help improve our docs: edit this page on GitHub)