Skip to content

Quick Start Guide

This guide walks you through installing QHx on a Kubernetes cluster and deploying your first workload with automated workload identity and mTLS communication.

Time to complete: 30 minutes
What you’ll learn:

  • Install QHx on Kubernetes
  • Deploy a workload with automatic SPIFFE identity
  • Verify mTLS communication between workloads
  • View workload identity statements

Before you begin, ensure you have:

  • Kubernetes cluster (1.28 or later)

    • Local: minikube, kind, or k3s
    • Cloud: EKS, AKS, GKE
    • Minimum: 2 CPU cores, 4GB RAM
  • kubectl configured to access your cluster

    Terminal window
    kubectl cluster-info
    # Should show your cluster endpoint
  • Helm 3.x installed

    Terminal window
    helm version
    # Version should be v3.x.x
Terminal window
helm repo add messier42 https://charts.messier42.com
helm repo update
Terminal window
helm install qhx messier42/qhx \
--namespace qhx-system \
--create-namespace \
--wait

What this does:

  • Creates qhx-system namespace
  • Deploys QHx Manager (admission controller)
  • Deploys PKI Server (SPIRE server for ML-DSA-65)
  • Deploys PKI Agents as DaemonSet (one per node)
  • Creates default QHxClusterPolicy

Installation takes: ~2-3 minutes

Check that all QHx components are running:

Terminal window
kubectl -n qhx-system get pod

Expected output:

NAME READY STATUS RESTARTS AGE
qhx-manager-5d8c7b9f6d-xyz 1/1 Running 0 2m
pki-server-0 2/2 Running 0 2m
pki-agent-abc123 1/1 Running 0 2m
pki-agent-def456 1/1 Running 0 2m

All pods should show Running status with READY matching total containers.

Troubleshooting:

Terminal window
# If pods are not ready, check logs
kubectl -n qhx-system logs -l app=qhx-manager
kubectl -n qhx-system logs pki-server-0 -c pki-server
# Check events
kubectl -n qhx-system get events --sort-by='.lastTimestamp'

Create a namespace for your application with MLS labels:

Terminal window
kubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: demo
annotations:
mls.qhx.dev/level: "us:s"
mls.qhx.dev/compartment: "us:demo"
EOF

Note: The mls.qhx.dev/* annotations define the Multi-Level Security classification for workloads in this namespace. With default policy, only users with appropriate clearance can deploy to this namespace.

Verify the namespace:

Terminal window
kubectl get namespace demo -o yaml | grep mls.qhx.dev

Deploy a simple HTTP server that will receive an identity:

Terminal window
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: http-server
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: http-server
template:
metadata:
labels:
app: http-server
spec:
serviceAccountName: default
containers:
- name: server
image: hashicorp/http-echo:latest
args:
- "-text=Hello from QHx!"
- "-listen=:8080"
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: http-server
namespace: demo
spec:
selector:
app: http-server
ports:
- port: 8080
targetPort: 8080
EOF

Wait for the deployment to be ready:

Terminal window
kubectl -n demo wait --for=condition=available --timeout=60s deployment/http-server

QHx automatically injects SPIFFE identities into workloads. Let’s examine what was created:

Terminal window
# Get the pod name
POD=$(kubectl -n demo get pod -l app=http-server -o jsonpath='{.items[0].metadata.name}')
# Check for SPIFFE identity files
kubectl -n demo exec $POD -- ls -la /run/secrets/qhx.dev/

Expected output:

total 12
-rw-r--r-- 1 root root 1234 ... svid.pem # X.509 certificate
-rw------- 1 root root 456 ... svid-key.pem # Private key
-rw-r--r-- 1 root root 2345 ... bundle.pem # Trust bundle
Terminal window
kubectl -n demo exec $POD -- \
cat /run/secrets/qhx.dev/svid.pem | \
openssl x509 -text -noout | grep URI

Expected output:

URI:spiffe://qhx.dev/ns/demo/sa/default/pod/http-server-abc123/...

This SPIFFE ID uniquely identifies this workload and includes:

  • Trust domain: qhx.dev
  • Namespace: demo
  • Service account: default
  • Pod name: http-server-abc123
  • Pod UID: (unique identifier)

Deploy a client workload to test communication:

Terminal window
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: http-client
namespace: demo
spec:
serviceAccountName: default
containers:
- name: client
image: curlimages/curl:latest
command: ["sleep", "3600"]
EOF

Wait for the pod to be ready:

Terminal window
kubectl -n demo wait --for=condition=ready --timeout=60s pod/http-client

First, try connecting directly to the HTTP server:

Terminal window
kubectl -n demo exec http-client -- \
curl -s http://http-server:8080

Expected output:

Hello from QHx!

This works because both workloads are in the same namespace and default Kubernetes networking allows it.

For production deployments, you would deploy QHx proxy sidecars to enable automatic mTLS. The proxy sidecars:

  • Intercept traffic
  • Authenticate using SPIFFE identities
  • Establish mTLS connections
  • Enforce policy

Note: Proxy deployment is covered in the Proxy Architecture Guide.

QHx PKI Server maintains a registry of all attested workloads. You can query it:

Terminal window
kubectl -n qhx-system exec pki-server-0 -c pki-server -- \
/opt/spire/bin/spire-server entry show

Sample output:

Entry ID : abc123...
SPIFFE ID : spiffe://qhx.dev/ns/demo/sa/default
Parent ID : spiffe://qhx.dev/agent/k8s/node-1
Selectors : k8s:ns:demo
k8s:sa:default
TTL : 3600

This shows the registration entries that allow workloads in the demo namespace with service account default to receive identities.

QHx PKI Agents on each node attest to the PKI Server. Check agent status:

Terminal window
kubectl -n qhx-system exec pki-server-0 -c pki-server -- \
/opt/spire/bin/spire-server agent list

Sample output:

SPIFFE ID : spiffe://qhx.dev/agent/k8s/node-1
Attestation type : k8s_psat
Expiration time : 2026-02-03 10:00:00 +0000 UTC
Serial number : 123456789

This shows the agents that have successfully attested and are authorized to request SVIDs for workloads.

QHx components expose Prometheus metrics. Port-forward to view them:

Terminal window
# PKI Server metrics
kubectl -n qhx-system port-forward pki-server-0 9090:9090
# In another terminal, query metrics
curl http://localhost:9090/metrics | grep spire_server_server_ca_sign_x509_svid

Sample metrics:

spire_server_server_ca_sign_x509_svid_count{status="OK"} 42
spire_server_server_ca_sign_x509_svid_elapsed_time{status="OK"} 0.045

This shows successful SVID signing operations and their latency.

Symptom: QHx pods remain in Pending state

Diagnosis:

Terminal window
kubectl -n qhx-system describe pod <pod-name>

Common causes:

  • Insufficient cluster resources (CPU/memory)
  • Node selector constraints
  • PersistentVolumeClaim not bound

Solution:

Terminal window
# Check resource availability
kubectl top nodes
# Check PVC status
kubectl -n qhx-system get pvc

Symptom: Error: admission webhook "qhx-manager.qhx.dev" denied the request

Common causes:

  • MLS labels not set on namespace
  • User lacks required Kubernetes groups
  • Policy violation (incompatible labels)

Solution:

Terminal window
# Check policy
kubectl get qhxclusterpolicy qhx-cluster-policy -o yaml
# Verify namespace has MLS labels
kubectl get namespace demo -o yaml | grep mls.qhx.dev

Symptom: /run/secrets/qhx.dev/ directory empty or missing

Common causes:

  • PKI Agent not running on node
  • Workload attestation failed
  • No registration entry for workload

Diagnosis:

Terminal window
# Check PKI Agent logs
kubectl -n qhx-system logs -l app=pki-agent --tail=50
# Check for registration entries
kubectl -n qhx-system exec pki-server-0 -c pki-server -- \
/opt/spire/bin/spire-server entry show -parentID spiffe://qhx.dev/agent/k8s/<node-name>

To remove the demo resources:

Terminal window
# Delete demo namespace (removes all workloads)
kubectl delete namespace demo
# Uninstall QHx (if desired)
helm uninstall qhx -n qhx-system
kubectl delete namespace qhx-system

Now that you have QHx running, explore these topics:

You’ve successfully:

  • ✅ Installed QHx on Kubernetes
  • ✅ Deployed workloads with automatic SPIFFE identities
  • ✅ Verified workload attestation
  • ✅ Checked node attestation status
  • ✅ Explored metrics and monitoring

QHx is now managing workload identities for your cluster. Workloads automatically receive:

  • SPIFFE IDs (X.509-SVIDs)
  • Short-lived certificates (1 hour TTL, auto-rotated)
  • Trust bundles for verification
  • MLS labels for policy enforcement

You’re ready to explore advanced features like mTLS proxies, request notarization, and multi-cluster federation!