Skip to content

push

The push command uploads container images and Helm charts from a bundle to a registry.

Usage

capsailer push --bundle <bundle-or-archive> [--external-registry <host>] [options]
capsailer push --image <image> [--external-registry <host>] [options]

What Gets Pushed Where

--bundle accepts:

  • a Capsailer bundle (.tar.gz, or an unpacked directory)
  • a plain docker save archive (.tar or .tar.gz), such as the image tarballs vendors ship. See Pushing a vendor archive

The destination is one of:

Destination How to select Images Charts
A registry you already have --external-registry <host[/path]> Pushed Not pushed. Install from the extracted .tgz (unpack)
The registry from capsailer registry --namespace <ns> (default capsailer-registry) Pushed through a temporary kubectl port-forward Published to ChartMuseum

Pushing to an external registry needs only the capsailer binary. Pushing to the built-in registry needs kubectl.

Images are uploaded directly with a built-in registry client. No Docker or skopeo is involved.

Credentials come from --username with --password-stdin, or from ~/.docker/config.json (for example after docker login).

Options

Option Description
--bundle Path to a Capsailer bundle (file or directory) or a docker save .tar archive
--namespace Kubernetes namespace where the registry is deployed
--external-registry URL of an external registry to push to
--username Username for authentication with the registry. Without it, credentials come from ~/.docker/config.json
--password Password for authentication with the registry. Visible in the process list and shell history; prefer --password-stdin
--password-stdin Read the password from standard input. The CAPSAILER_PASSWORD environment variable is used when neither option is given
--kubeconfig Path to the kubeconfig file
--plain-http Contact the registry over HTTP instead of HTTPS
--jobs Number of images to push in parallel (default 1)
--skip-tls-verify Skip TLS certificate verification when pushing to the registry. TLS is verified by default
--image Push an image from the local Docker daemon (read through the Docker API; the docker command is not needed). The full repository path is kept: nutanix/nai-api:v2 becomes <registry>/nutanix/nai-api:v2
--target-prefix Repository path placed in front of every pushed image, for example airgap
--keep-source-registry Keep an explicit source registry as a path element (quay.io/foo/bar becomes <registry>/quay.io/foo/bar)
--dry-run Print the source -> destination mapping for --bundle and push nothing. Needs no cluster access when --external-registry is not given

Examples

# Push all images and charts from a bundle to the registry
capsailer push --bundle capsailer-bundle.tar.gz --namespace my-registry

# Push artifacts from an unpacked bundle directory
capsailer push --bundle ./unpacked-bundle --namespace my-registry

# Push to an external registry
echo "$REGISTRY_PASSWORD" | capsailer push --bundle capsailer-bundle.tar.gz --external-registry artifactory.example.com --username myuser --password-stdin --jobs 4

# Push a single image to the registry
capsailer push --image nginx:latest --namespace my-registry

# Push every image in a vendor docker save archive to an external registry
capsailer push --bundle nai-images.tar --external-registry 10.55.1.16:5000 --username admin --password-stdin < password.txt

# Preview what a vendor archive would be pushed as
capsailer push --bundle nai-images.tar --external-registry 10.55.1.16:5000 --dry-run

Pushing a vendor docker save archive

A tarball created with docker save contains a manifest.json that names each image in its RepoTags list. These are usually short names without a registry:

[
  {
    "Config": "b5b2b2...json",
    "RepoTags": ["nutanix/nai-inference-ui:v2.8.1", "nutanix/nai-api:v2.8.1"],
    "Layers": ["layer1.tar", "layer2.tar"]
  }
]

Tools built on go-containerregistry (such as crane) cannot push this as it is. A name like nutanix/nai-api does not say which registry to use, so it resolves to Docker Hub or is rejected under strict validation. An archive with more than one image is also rejected outright (tarball must contain only a single image).

When --bundle points to a .tar file with a root manifest.json, capsailer push reads every image in the archive and rewrites each RepoTag to the target registry before pushing:

RepoTag in the archive Pushed as (target 10.55.1.16:5000)
nutanix/nai-api:v2.8.1 10.55.1.16:5000/nutanix/nai-api:v2.8.1
nginx 10.55.1.16:5000/library/nginx:latest
quay.io/foo/bar:1 10.55.1.16:5000/foo/bar:1 (or .../quay.io/foo/bar:1 with --keep-source-registry)
host:5000/x/y:t 10.55.1.16:5000/x/y:t (or .../host_5000/x/y:t with --keep-source-registry)

With --target-prefix airgap, every destination gains that path: 10.55.1.16:5000/airgap/nutanix/nai-api:v2.8.1.

Notes:

  • An image that has several tags is uploaded once and then tagged under each name.
  • Entries without a RepoTag, and images that were pulled by digest, are skipped with a message because there is no name to push them under.
  • If two entries would end up at the same destination (for example quay.io/a/b:1 and docker.io/a/b:1), the second one is skipped. Use --keep-source-registry to keep both.
  • A failure on one image does not stop the others. The command exits with an error at the end if anything failed.
  • Archives compressed with gzip (.tar.gz) are accepted; they are decompressed to a temporary file during the push.
  • Chart publishing is skipped, because a docker save archive holds images only.
  • Images inside a Capsailer bundle are pushed the same way, using the RepoTags of each images/*.tar. Docker Hub official images are pushed without library/ (nginx:1.25 becomes <registry>/nginx:1.25), which is the name capsailer build --rewrite-image-references writes into chart values. See How image names are mapped.
  • If two images would end up under the same name, push stops before uploading anything and tells you which. --keep-source-registry keeps them apart.