Bazel k8s expert
Skill kinhluan/rules-quarkus-skills/.agent-skills/bazel-k8s-expert
π€ Complete AI expert ecosystem for Modern Java, Quarkus & Bazel development βοΈβ‘οΈ Coverage for Vert.x, GraalVM, Maven/Gradle migration, and more π
npx -y skills add kinhluan/rules-quarkus-skills --skill bazel-k8s-expertAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Expert knowledge for deploying Quarkus/Java applications to Kubernetes using Bazel. Covers rules_k8s, Helm, Kustomize, ConfigMaps, Secrets, and health probes.
SKILL.md
13.9 KB, as published. Nobody here has run it
bazel-k8s-expert
Keyword: k8s | Platforms: gemini,claude,codex
Bazel Kubernetes Expert Skill - Deploying, managing, and operating Quarkus and Java applications on Kubernetes using Bazel-native tooling.
Core Mandates
- GitOps-Ready: All K8s manifests must be generated from Bazel targets, never hand-edited YAML in production.
- Hermetic Deployments: Use
rules_k8sorhelmvia Bazel to ensure deployment artifacts match built images exactly. - Health Probes: Every deployment must define
@Livenessand@Readinessprobes (seemicroprofile-expert). - Resource Limits: Always set CPU/memory requests and limits to prevent cluster starvation.
- Config Externalization: Use ConfigMaps and Secrets for environment-specific values; never bake config into images.
Setup (Bzlmod)
MODULE.bazel
bazel_dep(name = "rules_k8s", version = "0.7")
bazel_dep(name = "rules_helm", version = "0.1")
# For Kustomize support
bazel_dep(name = "rules_kustomize", version = "0.1")
rules_k8s Deployment
Basic Deployment + Service
# services/user/k8s/BUILD.bazel
load("@rules_k8s//k8s:defs.bzl", "k8s_deploy")
k8s_deploy(
name = "user-service_deploy",
template = ":deployment.yaml",
images = {
"user-service": "//services/user:user-service_image",
},
)
# services/user/k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: user-service # Replaced by rules_k8s
ports:
- containerPort: 8080
env:
- name: QUARKUS_HTTP_PORT
value: "8080"
- name: JAVA_TOOL_OPTIONS
value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /q/health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /q/health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Deploy to Cluster
# Apply deployment
bazel run //services/user/k8s:user-service_deploy.apply
# Delete deployment
bazel run //services/user/k8s:user-service_deploy.delete
# Describe deployment
bazel run //services/user/k8s:user-service_deploy.describe
ConfigMaps & Secrets
ConfigMap from Properties File
# services/order/k8s/BUILD.bazel
load("@rules_k8s//k8s:object.bzl", "k8s_object")
genrule(
name = "order-config",
srcs = ["application.properties"],
outs = ["order-config.properties"],
cmd = "cp $(SRCS) $@",
)
k8s_object(
name = "order-configmap",
template = "configmap.yaml",
substitutions = {
"{CONFIG_DATA}": "$(location :order-config)",
},
)
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
application.properties: |
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=jdbc:postgresql://postgres:5432/orders
quarkus.hibernate-orm.database.generation=update
Secret from Bazel Secrets
# k8s/BUILD.bazel
load("@rules_k8s//k8s:object.bzl", "k8s_object")
k8s_object(
name = "db-secret",
template = "secret.yaml",
substitutions = {
"{DB_PASSWORD}": "$(location //secrets:db_password)",
},
)
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
password: "{DB_PASSWORD}"
Mounting in Deployment
# deployment.yaml
spec:
template:
spec:
containers:
- name: order-service
volumeMounts:
- name: config
mountPath: /app/config
- name: secrets
mountPath: /app/secrets
volumes:
- name: config
configMap:
name: order-service-config
- name: secrets
secret:
secretName: db-credentials
Helm Charts
Chart Structure
services/user/helm/
βββ BUILD.bazel
βββ Chart.yaml
βββ values.yaml
βββ values-dev.yaml
βββ values-prod.yaml
βββ templates/
βββ deployment.yaml
βββ service.yaml
βββ ingress.yaml
βββ configmap.yaml
βββ _helpers.tpl
BUILD.bazel for Helm
# services/user/helm/BUILD.bazel
load("@rules_helm//helm:defs.bzl", "helm_chart", "helm_push", "helm_template")
helm_chart(
name = "user-service-chart",
srcs = glob(["templates/**"]),
chart_yaml = "Chart.yaml",
values_yaml = "values.yaml",
)
helm_template(
name = "user-service-template",
chart = ":user-service-chart",
values = "values-dev.yaml",
namespace = "dev",
)
helm_push(
name = "push-chart",
chart = ":user-service-chart",
repository = "oci://ghcr.io/myorg/charts",
version = "1.0.0",
)
Template with Image Reference
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "user-service.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.port }}
env:
- name: QUARKUS_PROFILE
value: {{ .Values.quarkus.profile }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
values.yaml
# values.yaml
replicaCount: 3
image:
repository: ghcr.io/myorg/user-service
pullPolicy: IfNotPresent
tag: ""
quarkus:
profile: prod
service:
type: ClusterIP
port: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /q/health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /q/health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Deploy Helm Chart
# Template (dry run)
bazel run //services/user/helm:user-service-template
# Install/Upgrade
helm upgrade --install user-service \
$(bazel cquery --output=files //services/user/helm:user-service-chart) \
--values services/user/helm/values-prod.yaml \
--namespace prod
# Push to OCI registry
bazel run //services/user/helm:push-chart
Kustomize
Base + Overlay Pattern
services/payment/k8s/
βββ base/
β βββ BUILD.bazel
β βββ kustomization.yaml
β βββ deployment.yaml
β βββ service.yaml
βββ overlays/
βββ dev/
β βββ BUILD.bazel
β βββ kustomization.yaml
β βββ replica-patch.yaml
βββ prod/
βββ BUILD.bazel
βββ kustomization.yaml
βββ resource-patch.yaml
Base Configuration
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: payment-service
images:
- name: payment-service
newTag: latest
Dev Overlay
# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
resources:
- ../../base
replicas:
- name: payment-service
count: 1
configMapGenerator:
- name: payment-config
literals:
- QUARKUS_PROFILE=dev
- LOG_LEVEL=DEBUG
Prod Overlay
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: prod
resources:
- ../../base
replicas:
- name: payment-service
count: 5
patches:
- path: resource-patch.yaml
configMapGenerator:
- name: payment-config
literals:
- QUARKUS_PROFILE=prod
- LOG_LEVEL=INFO
# overlays/prod/resource-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
spec:
template:
spec:
containers:
- name: payment-service
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
Bazel Kustomize Build
# overlays/dev/BUILD.bazel
load("@rules_kustomize//kustomize:defs.bzl", "kustomize_build")
kustomize_build(
name = "dev-manifests",
srcs = glob(["*.yaml"]),
kustomization = "kustomization.yaml",
)
# Apply
# kubectl apply -k $(bazel cquery --output=files //services/payment/k8s/overlays/dev:dev-manifests)
Ingress
Basic Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: "letsencrypt"
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
Horizontal Pod Autoscaler (HPA)
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 15
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
Pod Disruption Budget
# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: user-service-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: user-service
Troubleshooting
Pod CrashLoopBackOff
# Check logs
kubectl logs -l app=user-service --tail=100
# Check events
kubectl describe pod user-service-xxx
# Common causes:
# 1. Missing ConfigMap/Secret β Create missing resources
# 2. Wrong image tag β Verify image was pushed
# 3. Port conflict β Check containerPort matches app
# 4. Resource limits too low β Increase memory/CPU limits
# 5. Liveness probe failing β Check /q/health/live endpoint
Service Not Reachable
# Check service endpoints
kubectl get endpoints user-service
# Port-forward for testing
kubectl port-forward svc/user-service 8080:80
# Common causes:
# 1. Selector mismatch β Check labels on pod and service
# 2. Wrong targetPort β Must match containerPort
# 3. Network policies blocking β Check NetworkPolicy rules
ImagePullBackOff
# Check image exists
kubectl describe pod user-service-xxx | grep -i "failed to pull"
# Common causes:
# 1. Image not pushed β bazel run //services/user:push_user_service
# 2. Wrong imagePullSecrets β Add registry credentials
# 3. Wrong image tag β Check tag in deployment
Decision Trees
Choosing Deployment Strategy
Single service?
βββ YES β rules_k8s k8s_deploy (simplest)
βββ NO (multiple services)
βββ Need templating? β Helm charts
βββ Need environment overlays? β Kustomize
βββ Need both? β Helm + Kustomize (Helm chart + Kustomize patches)
Config Management
Environment-specific values?
βββ Simple key-value pairs β ConfigMap literals
βββ Complex config files β ConfigMap from file
βββ Sensitive data β Secret (base64 encoded)
βββ Many environments β Kustomize configMapGenerator
βββ Dynamic values β External Secrets Operator
References
- rules_k8s GitHub
- Kubernetes Documentation
- Helm Documentation
- Kustomize Documentation
- Quarkus Kubernetes Guide
Skill Interoperability
The bazel-k8s-expert βΈοΈ skill deploys containers built by:
- bazel-oci-expert π³: OCI container images.
- rules-quarkus π§: Quarkus applications built with Bazel.
- microprofile-expert π: Health probes, metrics, and config.
- bazel-expert π: Core Bazel build infrastructure.