A practical, step‑by‑step guide to containerizing, publishing, and running a lightweight Kubernetes Web UI on EKS using ECR, eksctl and standard Kubernetes manifests.
Why this guide?
Working with Kubernetes purely from the CLI is powerful, but sometimes you need a small, secure web UI to help teams quickly inspect resources, tail logs, and run quick in‑pod commands without giving full kubectl access. This blog post walks through building kubectl-webui—a production‑aware Flask UI packaged as a container, pushed to ECR, and deployed to an AWS EKS cluster with RBAC, sample apps for testing, and a short troubleshooting section.
Prerequisites
Make sure you have the following installed and configured:
- AWS CLI (
aws --version) and credentials (runaws configure). - Docker (
docker --version). - kubectl (
kubectl version --client). - eksctl (
eksctl version).
Verify AWS identity with:
aws sts get-caller-identity
Project overview
Repository structure (important files):
kubectl-webui/
├── app.py # Flask application
├── Dockerfile # Docker build configuration
├── requirements.txt # Python dependencies
├── templates/ # HTML templates
├── static/ # CSS / assets
├── scripts/ # helper shell scripts
├── nginx.conf
└── supervisord.conf
Build the Docker image
Dockerfile highlights:
- Base image:
python:3.9-slim. - Installs
kubectland AWS CLI inside the image for cluster interactions. - Uses
supervisord+nginxto serve the app alongside process supervision. - Adds an unprivileged
appuser and copies application files.
Build command (AMD64 for EKS node compatibility):
docker build --platform linux/amd64 -t kubectl-webui-professional:amd64 .
Verify the image exists:
docker images | grep kubectl-webui
Push image to Amazon ECR
Create the repository, login, tag and push:
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION="us-west-2"
REPO_NAME="kubectl-webui"
aws ecr create-repository --repository-name $REPO_NAME --region $REGION
aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com
docker tag kubectl-webui-professional:amd64 $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPO_NAME:professional
docker push $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPO_NAME:professional
Confirm the image with:
aws ecr describe-images --repository-name $REPO_NAME --region $REGION
Create an EKS cluster
Create a managed cluster for testing or production readiness:
eksctl create cluster \
--name kubectl-webui-cluster \
--region us-west-2 \
--nodegroup-name kubectl-webui-nodes \
--node-type t3.medium \
--nodes 2 --nodes-min 1 --nodes-max 3 --managed
aws eks update-kubeconfig --region us-west-2 --name kubectl-webui-cluster
kubectl get nodes
Deploy the Web UI and RBAC manifests
A single YAML (kubectl-webui-k8s.yaml) contains Deployment, Service (LoadBalancer), ServiceAccount, ClusterRole and ClusterRoleBinding. Key points:
- Deployment references the ECR image URI (replace
ACCOUNT_IDandREGION). - Container listens on
5000(Flask) and is exposed through an ELB on port80. - RBAC is scoped to read resources (
get,list,watch) for pods, services, nodes, deployments and related objects.
Apply the manifests and watch rollout:
kubectl apply -f kubectl-webui-k8s.yaml
kubectl rollout status deployment/kubectl-webui
kubectl get svc kubectl-webui-service
Note: the LoadBalancer hostname takes a couple of minutes to provision.
Deploy sample applications (for testing)
sample-apps.yaml includes simple test workloads:
-
nginx-app+ ClusterIP service -
redis-app+ ClusterIP service -
busybox-app(sleeping pod) to exec into -
log-collectorDaemonSet (fluentd)
kubectl apply -f sample-apps.yaml
kubectl get all
Testing the dashboard
Get LoadBalancer hostname:
kubectl get svc kubectl-webui-service -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
- Open the returned URL in your browser (http). Example placeholder used in the repo is an ELB hostname.
- Try the UI features:
- List pods, services, deployments
- Describe and view pod logs
- Exec into the
busyboxpod to run commands - View events and basic resource usage
Troubleshooting (common patterns)
ImagePullBackOff
- Ensure the image exists in ECR and the image URI used in the Deployment is correct.
- Use
aws ecr describe-imagesandkubectl get deployment -o yaml | grep imageto verify.
RBAC permission errors
- Confirm the ServiceAccount exists:
kubectl get sa kubectl-webui-sa. - Test permissions from the service account context:
kubectl auth can-i list pods --as=system:serviceaccount:default:kubectl-webui-sa
LoadBalancer stuck in Pending
- Confirm cluster nodes are in public subnets with proper tagging or your cloud provider setup allows ELB provisioning.
- Check
kubectl describe svc kubectl-webui-serviceand your cloud console for ELB status.
Pod fails to start
- Inspect logs:
kubectl logs -l app=kubectl-webui. - Describe the pod for events:
kubectl describe pod -l app=kubectl-webui.
Architecture (high level)
Developer browser → ELB (LoadBalancer) → kubectl-webui Pod → Kubernetes API Server → cluster workloads (nginx, redis, busybox)
This pattern keeps the UI stateless (single replica acceptable for small teams) and relies on EKS IAM + Kubernetes RBAC to control access.
Security & Production Notes
- Do not give this UI cluster‑wide admin rights. Keep RBAC read‑only or narrowly scoped.
- If exposing publicly, place the service behind an HTTPS ALB with authentication (OIDC, Cognito or a reverse proxy with auth).
- Use image scanning and automated CI to build/push images, and use immutable tags (e.g.
:sha-<commit>) rather than:latest.
Conclusion
The kubectl‑webui project demonstrates how a compact, secure web UI can simplify everyday cluster operations while preserving Kubernetes best practices. By containerizing the app, publishing images to ECR, and deploying on EKS with scoped RBAC, you get a reproducible, auditable workflow that teams can trust. For production use, tighten access with HTTPS and authentication, use private networking where possible, and adopt CI/CD with immutable image tags.
Happy learning
Prithiviraj Rengarajan
DevOps Engineer









Top comments (0)