Companion material for the KubeCon EU 2026 talk on OpenTelemetry sampling strategies. Each directory contains a self-contained Kubernetes setup for a different approach.
| Strategy | Directory | When to use |
|---|---|---|
| Head sampling | head-sampling/ |
Simple, stateless volume reduction by a fixed percentage |
| Tail sampling | tail-sampling/ |
Keep errors and slow traces; drop only healthy, fast traces |
The sampling decision is made upfront, before the trace completes, based on the trace ID alone. Every Collector replica independently reaches the same decision — no coordination needed.
The sampling decision is made after the full trace is collected, so errors and slow requests can always be retained regardless of sampling rate. Requires a two-tier Collector setup.
- Kubernetes cluster with
kubectlconfigured
Each setup is self-contained and includes Jaeger as the trace backend. Jaeger uses in-memory storage — suitable for demos, not for production.
Depending on your cluster's setup, you may need to use port-forwarding or an ingress controller to expose Jaeger.
The two scenarios can run simultaneously in separate namespaces. The commands below substitute the namespace in every manifest on the fly, so no files need to be edited.
docker build -t node-frontend:latest apps/node-frontend
docker build -t go-backend:latest apps/go-backendLoad them into your local cluster if you are using kind:
kind load docker-image node-frontend:latest
kind load docker-image go-backend:latestkubectl create namespace head-sampling
for f in head-sampling/*.yaml; do
sed 's/namespace: otel/namespace: head-sampling/g
s/namespace: default/namespace: head-sampling/g
s/\.otel\.svc\.cluster\.local/.head-sampling.svc.cluster.local/g' "$f" \
| kubectl apply -f -
donekubectl create namespace tail-sampling
for f in tail-sampling/*.yaml; do
sed 's/namespace: otel/namespace: tail-sampling/g
s/namespace: default/namespace: tail-sampling/g
s/\.otel\.svc\.cluster\.local/.tail-sampling.svc.cluster.local/g' "$f" \
| kubectl apply -f -
done# Head sampling — http://localhost:16686
kubectl port-forward -n head-sampling svc/jaeger 16686:16686 &
# Tail sampling — http://localhost:16687
kubectl port-forward -n tail-sampling svc/jaeger 16687:16686 &The load generator sends one request per second to each scenario automatically.
After a few minutes, both Jaeger instances will show sampled traces from the node-frontend and go-backend services.
kubectl delete namespace head-sampling
kubectl delete namespace tail-sampling