AWS CLI v2 + kubectl on Ubuntu 26.04. Multi-arch (amd64/arm64), kubectl checksum-verified.
1M+
This image bundles AWS CLI v2 (aws) and kubectl on Ubuntu 26.04. It also includes jq, curl, unzip, and envsubst (from gettext-base). Perfect for CI/CD steps, automation, and reproducible local scripting.
π³ Docker Hub: heyvaldemar/aws-kubectlβ
| Need | This image | amazon/aws-cli | bitnami/kubectl | Alpine + scripts |
|---|---|---|---|---|
| AWS CLI v2 | β | β | β | manual |
| kubectl | β | β | β | manual |
| jq, envsubst, curl, unzip | β | β | β | manual |
| Multi-arch (amd64/arm64) | β | β | β | depends |
| Cosign signatures | β | β | β | β |
| SBOM (SPDX) | β | β | β | β |
| SLSA build provenance | β | β | β | β |
| OpenSSF Scorecard | 8.1/10 (as of 2026-10-06) | N/A | N/A | N/A |
| Non-root default (UID 10001) | β (v2.0+) | β | β | depends |
| Weekly base rebuild | β | β | β | manual |
One image instead of three. Full supply-chain attestations. OpenShift-compatible out of the box.
docker version. The image ships AWS CLI v2 and kubectl inside, so you don't need either installed on your host. Multi-arch (amd64 + arm64): works on Linux, macOS (Intel + Apple Silicon), Windows + WSL2.~/.aws/ if you want to run AWS commands. Create with aws configure from any machine that has aws-cli, or copy from an existing setup. The image mounts the dir read-only by convention.kubeconfig in ~/.kube/ if you want to run kubectl commands against an existing cluster. Create with aws eks update-kubeconfig --name <cluster> or your tool of choice.You can also run the container with no mounts for aws --version, kubectl version --client, or any tool that doesn't need cloud/cluster credentials.
# List S3 buckets (requires ~/.aws)
docker run --rm --user "$(id -u):0" \
-v ~/.aws:/home/app/.aws \
heyvaldemar/aws-kubectl aws s3 ls
# Get Kubernetes nodes (requires ~/.kube)
docker run --rm --user "$(id -u):0" \
-v ~/.kube:/home/app/.kube \
heyvaldemar/aws-kubectl kubectl get nodes
# Interactive shell with both
docker run -it --user "$(id -u):0" \
-v ~/.aws:/home/app/.aws \
-v ~/.kube:/home/app/.kube \
heyvaldemar/aws-kubectl bash
Runs as non-root by default (UID 10001). See Mounting credentialsβ for permission details.
π¨ Existing v1.x user? The
v1-maintenancetag reached end of support on 2026-07-20 and was last rebuilt on 2026-04-22; it gets no further updates. Migration details ββ
For production use, pin to immutable semver tags:
heyvaldemar/aws-kubectl:2.2.1, immutable on Docker Hub, never purged, cosign-signed2.2.0, v2.2.0 and kube-v1.37.1 (one digest) were published without a cosign signature and, being immutable, stay unsigned. Use 2.2.1 instead; see the CHANGELOGβ .heyvaldemar/aws-kubectl:sha-1dfda81. Short-SHA tags are deleted after 90 daysIf you pin by manifest digest (recommended for maximum supply chain integrity), make sure the digest is also referenced by a semver tag. Otherwise the digest may become unpullable once short-SHA cleanup runs. To resolve a tag to its current digest:
docker buildx imagetools inspect heyvaldemar/aws-kubectl:2.0.0 \
--format '{{.Manifest.Digest}}'
jq, curl, unzip, envsubst, and ca-certificates preinstalled.kubectl during build.buildx.org.opencontainers.image.*) on every published image./etc/kube-version inside the image.Default user is non-root (UID 10001, GID 0) as of v2.0. If you need root (e.g. to install additional
aptpackages at runtime), override with--user 0:0. See Breaking Changes in v2.0β for migration details.
restricted namespaces: the non-root default (UID 10001, GID 0) meets runAsNonRoot; the pod spec sets the rest of the restricted profile (allowPrivilegeEscalation: false, drop ALL capabilities, RuntimeDefault seccomp)curl | bash. kubectl is checksum-verified at build time; the AWS CLI installer is downloaded from AWS over TLS without a signature checksha256 digest (ubuntu:26.04@sha256:β¦). The digest moves only when a pull request changes it. Dependabot's docker updates were switched off on 2026-09-06, so the base is bumped by hand; the pinned digest dates from 2026-09-02.Dockerfile keeps build-only intermediate artefacts (the downloaded AWS CLI archive, extracted tree, kubectl tarball, and checksum file) out of the published image.kubectl binaries are verified against the upstream sha256 checksum published at dl.k8s.io during build.cron: "0 6 * * 1") reinstall the packages the image adds on top of the base from the current Ubuntu archive, and fetch the current stable kubectl and AWS CLI. They do not move the pinned base layers; that takes a digest bump.hadolint and shell scripts with shellcheck before any build runs.release-assets.yml calls the SLSA generator's reusable workflow by tag (@v2.1.0), because the generator verifies its own reference at run time and refuses a SHA. OpenSSF Scorecard counts that line against Pinned-Dependencies.VCS_REF and BUILD_DATE are stamped into org.opencontainers.image.revision and org.opencontainers.image.created, and the resolved kubectl release is exposed via io.heyvaldemar.kubectl.version and /etc/kube-version.2.2.0, v2.2.0, kube-v1.37.1), whose run stopped before signing. See the CHANGELOG.provenance: mode=max) are attached to every published image.actions/attest-build-provenance and stored in GitHub Attestationsβ . Registry push is disabled because Docker Hub's OCI referrer credential handoff proved unreliable in early Phase 2 hotfix testing. See CHANGELOGβ [2.0.0] CI section for context.cosign verify heyvaldemar/aws-kubectl:latest \
--certificate-identity-regexp "https://github.com/heyvaldemar/aws-kubectl-docker/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
Tags fall into five categories:
:2.0.0, :v2.0.0): immutable on Docker Hub; the digest under these tags never changes after first push. Recommended for production pins.:2.0, :2): mutable; re-targets to the newest patch (and minor) within the major on each release. Kept forever.:latest, :edge): updated on every main build and by the weekly rebuild; kept forever. :v1-maintenance is frozen at its 2026-04-22 build (end of support 2026-07-20).:kube-v1.36.0): generated only on semver releases. Tracks the kubectl release packaged into the image at the time of that release. Immutable on Docker Hub; kept forever. kube-v1.37.1 is unsigned (see Pinning guidance).:sha-<7char>): produced by CI for every commit to main. Immutable while live; retained for 90 days, then automatically deleted by the Docker Hub Tag Cleanup workflow.Cosign signatures (:sha256-<digest>.sig) are managed by Sigstore and are not deleted.
Starting with v2.0.0, this image runs as a non-root user (UID 10001, GID 0)
by default. This is how hardened container images are built today, and it is
required for compatibility with OpenShift, the Kubernetes Pod Security Standards
restricted profile (enforced by Pod Security Admission; PodSecurityPolicy was
removed in Kubernetes 1.25), and enterprise security scanners.
If you use this image for one-off CI commands (aws s3 sync, kubectl apply),
v2.0 works identically to v1.x.
If you mount a host directory or Kubernetes PVC, you may need to adjust file ownership or run the container with a matching UID:
Docker:
docker run --rm -v "$PWD:/home/app" --user "$(id -u):0" \
heyvaldemar/aws-kubectl:latest aws s3 ls
Kubernetes:
spec:
securityContext:
runAsUser: 10001
runAsGroup: 0
fsGroup: 0
If v2.0 breaks your workflow and you need time to migrate, pin to the v1 maintenance track:
docker pull heyvaldemar/aws-kubectl:v1-maintenance
The v1-maintenance tag reached end of support on July 20, 2026. Its last
rebuild was on April 22, 2026, so it received no security updates between that
date and the end of support, and it is now frozen. Pin it only while you migrate.
~/.aws β AWS credentials/config (credentials, config). Mount to /home/app/.aws inside the container.~/.kube β kubeconfig(s). Mount to /home/app/.kube inside the container.The container's default user is UID 10001 with
HOME=/home/app. Pass--user "$(id -u):0"when mounting host files so the container can read them.
Interactive shell with both configs (mount under /home/app, the non-root user's HOME, and match your host UID so the container can read the mounted files):
docker run -it \
--user "$(id -u):0" \
-v ~/.aws:/home/app/.aws \
-v ~/.kube:/home/app/.kube \
heyvaldemar/aws-kubectl bash
If you pulled an amd64-only tag on an ARM/M-series Mac:
docker run --platform linux/amd64 -it \
--user "$(id -u):0" \
-v ~/.aws:/home/app/.aws \
-v ~/.kube:/home/app/.kube \
heyvaldemar/aws-kubectl bash
# List S3 buckets
docker run --rm \
--user "$(id -u):0" \
-v ~/.aws:/home/app/.aws \
heyvaldemar/aws-kubectl aws s3 ls
# Get Kubernetes nodes
docker run --rm \
--user "$(id -u):0" \
-v ~/.kube:/home/app/.kube \
heyvaldemar/aws-kubectl kubectl get nodes
The Dockerfile accepts the following build arguments:
| ARG | Default | Purpose |
|---|---|---|
KUBE_VERSION | latest | Pin a specific kubectl release (e.g. v1.30.6). latest fetches the current stable from dl.k8s.io. |
VCS_REF | unknown | Commit SHA, stamped into org.opencontainers.image.revision. |
BUILD_DATE | unknown | ISO-8601 build timestamp, stamped into org.opencontainers.image.created. |
TARGETARCH | auto | Target architecture (amd64/arm64). Supplied automatically by buildx. |
# From the folder with the Dockerfile
docker build -t aws-kubectl:local .
docker build --build-arg KUBE_VERSION=v1.30.6 \
-t aws-kubectl:local .
If KUBE_VERSION is omitted, the build fetches the latest stable from dl.k8s.io.
docker build \
--build-arg KUBE_VERSION=v1.30.6 \
--build-arg VCS_REF="$(git rev-parse HEAD)" \
--build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
-t aws-kubectl:local .
docker buildx create --name x --use || docker buildx use x
# Generic tag (no OS name in the tag)
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg VCS_REF="$(git rev-parse HEAD)" \
--build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
-t heyvaldemar/aws-kubectl:latest \
--push .
# Or pin kubectl in a tag users can reason about
KUBE_VERSION=v1.30.6
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg KUBE_VERSION=$KUBE_VERSION \
--build-arg VCS_REF="$(git rev-parse HEAD)" \
--build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
-t heyvaldemar/aws-kubectl:kube-$KUBE_VERSION \
--push .
Verify:
docker buildx imagetools inspect heyvaldemar/aws-kubectl:latest
Optional supply-chain flags (if you want SBOM/provenance):
--sbom=true --provenance=true
This repo includes scripts/smoke-test.sh to validate the tools in the image. CI runs it on every push and pull request against an image built from the commit, before anything is published: the publish job waits for it. It also checks that the image runs as UID 10001 with a writable HOME, and tests/plant-violations.pyβ proves the test can fail by rebuilding the image with each promise broken, five ways listed in tests/plants.tsvβ : running as root, jq or envsubst left out, the version marker not matching kubectl, and HOME left unwritable.
docker build -t aws-kubectl:local .
chmod +x scripts/smoke-test.sh
./scripts/smoke-test.sh # defaults to aws-kubectl:local
./scripts/smoke-test.sh your/tag:dev # test any tag you pass
The script checks:
/etc/kube-version matches kubectl version --clientHOME=/home/appkubectl cluster calls if you mount ~/.aws / ~/.kubeIMG=aws-kubectl:local
# OS/arch
docker run --rm $IMG sh -lc 'uname -a; echo -n "Arch: "; uname -m'
# Core tools & versions
docker run --rm $IMG aws --version
docker run --rm $IMG kubectl version --client --output=yaml
docker run --rm $IMG jq --version
docker run --rm $IMG envsubst --version
docker run --rm $IMG sh -c 'curl --version | head -n1'
docker run --rm $IMG sh -c 'unzip -v | head -n2'
# Resolved kubectl release stamped at build time
docker run --rm $IMG cat /etc/kube-version
# Binaries present where expected
docker run --rm $IMG sh -c 'ls -l /usr/local/bin/kubectl; for b in aws jq envsubst curl unzip; do command -v "$b"; done'
# CA bundle present + HTTPS sanity
docker run --rm $IMG sh -c 'ls -lh /etc/ssl/certs/ca-certificates.crt'
docker run --rm $IMG sh -c 'curl -fsSI -o /dev/null -w "HTTPS OK (%{http_code})\n" https://kubernetes.io'
# AWS identity (requires valid creds)
docker run --rm --user "$(id -u):0" \
-v ~/.aws:/home/app/.aws \
aws-kubectl:local aws sts get-caller-identity
# Current k8s context & nodes (requires valid kubeconfig)
docker run --rm --user "$(id -u):0" \
-v ~/.kube:/home/app/.kube \
aws-kubectl:local kubectl config current-context
docker run --rm --user "$(id -u):0" \
-v ~/.kube:/home/app/.kube \
aws-kubectl:local kubectl get nodes -o wide
kubectl binaries are checksum-verified during build.--no-install-recommends) and lists are cleaned.KUBE_VERSION in CI for reproducibility.If a specific workflow requires root inside the container (e.g. installing additional apt packages at runtime, or restoring pre-v2.0 behaviour), override the user:
docker run --rm --user 0:0 heyvaldemar/aws-kubectl bash
The patterns in this image are codified as a reusable standard at heyvaldemar/self-host-repo-hardening-runbook β IMAGE-PUBLISHING-RUNBOOK.mdβ . If you maintain a Dockerfile-based repo and want to follow the same standard, the runbook covers:
org.opencontainers.image.*)provenance: mode=max)Six mandatory phases plus an optional non-root migration phase for repos with existing :latest audiences. Eight production-grounded pitfalls drawn from this repo's own PR history.
Maintained by Vladimir Mikhalevβ β Docker Captain Β· IBM Champion Β· AWS Community Builder
YouTubeβ Β· Blogβ Β· LinkedInβ
Content type
Image
Digest
sha256:fb5805688β¦
Size
134.8 MB
Last updated
about 1 hour ago
docker pull heyvaldemar/aws-kubectl