
September 09, 2026
14 min read
By Kokil Thapa | Last reviewed: September 2026
A single slow checkout on a Laravel eCommerce site can touch five services, two queues, and a payment gateway. Logs show fragments, but they never stitch the full story together. Jaeger: Distributed Tracing Explained is the reference most teams need when they outgrow grep and start asking which hop actually burned 800 ms. Jaeger is a CNCF graduated tracing backend that stores traces as trees of spans and renders them in a UI you can search by trace ID, service, or latency. This guide covers how Jaeger works, how to deploy it, and how to wire it into PHP and Laravel stacks you already run in production.
If you already run microservices or a modular monolith with async jobs, start with our companion walkthrough on OpenTelemetry and Jaeger instrumentation. For API-heavy platforms, pairing traces with structured logging and API development practices gives you faster root-cause analysis than either tool alone.
What is Jaeger and why do teams adopt distributed tracing?
Jaeger originated at Uber and is now a graduated project under the Cloud Native Computing Foundation. It answers one question logs struggle with: where did time go across service boundaries? A trace is the full journey of one logical request. Each unit inside that trace is a span — a timed operation with a name, start time, duration, and optional tags.
On production Laravel applications I've maintained, the pattern repeats. A booking API returns 504 after 30 seconds. Nginx, PHP-FPM, MySQL, Redis, and an external SMS API each look fine in isolation. Distributed tracing shows the SMS call blocked for 28 seconds while everything else finished in under 200 ms. That single span saves hours of guesswork.
Tracing differs from metrics and logs in scope. Metrics tell you error rates climbed. Logs tell you one service threw an exception. Traces show the causal chain — parent span to child span — across process and network boundaries. For platforms like Adventure Third Pole Trek, where booking, supplier CRM, and payment flows interleave, that chain is operational gold.
Core Jaeger components
- Client libraries / OpenTelemetry SDK — create spans inside your application code or auto-instrumentation agents.
- Agent — optional sidecar that receives spans over UDP and forwards them to the collector; useful in Kubernetes.
- Collector — validates, batches, and writes spans to storage.
- Query — API layer that serves trace lookups to the UI.
- UI — search traces by service, operation, tags, min/max duration, or trace ID.
- Storage — Cassandra, Elasticsearch, or Badger (dev only) hold span data with configurable retention.
Modern deployments rarely use Jaeger's legacy client libraries directly. The recommended path in 2026 is OpenTelemetry for instrumentation and OTLP export to Jaeger's collector. The official Jaeger documentation describes this as the supported integration model going forward.
How does Jaeger distributed tracing work end to end?
Every span carries a trace_id shared across the request and a unique span_id. When service A calls service B, A injects trace context into HTTP headers — typically traceparent per the W3C Trace Context standard. Service B extracts that context and creates a child span linked to the same trace. Without propagation, you get orphaned spans that look like unrelated requests.
Sampling controls cost. Head-based sampling decides at trace start whether to keep the entire trace. Tail-based sampling (often handled upstream in the OpenTelemetry Collector) can keep slow or errored traces while dropping happy paths. For a booking portal handling thousands of reads per minute, sampling at 1–5% is usually enough to catch regressions without filling Elasticsearch.
Span attributes worth setting in production
Tags turn a pretty timeline into a searchable debugger. Standard semantic conventions from OpenTelemetry include http.method, http.status_code, db.system, and net.peer.name. Add business tags sparingly — order.id, tenant.slug, payment.provider — when support teams need to correlate traces with tickets.
Events inside spans mark point-in-time occurrences: "cache miss", "retry attempt 2", "webhook signature verified". They appear as markers on the span bar in Jaeger's UI. I've found them more useful than stuffing everything into span names.
Reading the Jaeger UI
- Open the Search tab and filter by service plus minimum duration to surface slow paths quickly.
- Click a trace to view the Gantt-style timeline; wider bars mean longer operations.
- Switch to the Trace Timeline JSON view when you need raw span data for a ticket or a JSON formatter review.
- Use Compare traces when validating a deploy: same endpoint, before and after release.
- Copy the trace ID into log queries so logs and traces align on one request.
Pair Jaeger with metrics from Prometheus and alerts via Alertmanager workflows. Traces explain individual slow requests; metrics tell you the blast radius.
How do you deploy Jaeger for production in 2026?
For local development, the all-in-one container is enough. Production needs separated collector, query, and storage with retention policies and backups. Elasticsearch 8.x or Cassandra remain common backends; object storage adapters exist for long-term archival in larger shops.
A minimal Docker Compose stack for staging on Ubuntu 24 with OpenTelemetry Collector forwarding OTLP to Jaeger looks like this:
services:
jaeger:
image: jaegertracing/all-in-one:1.64
ports:
- "16686:16686"
- "4317:4317"
- "4318:4318"
environment:
COLLECTOR_OTLP_ENABLED: "true"
otel-collector:
image: otel/opentelemetry-collector-contrib:0.120.0
volumes:
- ./otel-collector.yaml:/etc/otelcol-contrib/config.yaml
depends_on:
- jaeger Collector config receiving OTLP and exporting to Jaeger:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
probabilistic_sampler:
sampling_percentage: 5
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [probabilistic_sampler, batch]
exporters: [otlp/jaeger] On Kubernetes, run the collector as a DaemonSet or sidecar and Jaeger components via the official Helm chart. Pin image tags in Git, wire retention on Elasticsearch indices, and monitor collector queue depth. If spans pile up, your storage or network is the bottleneck — not PHP.
For teams without a dedicated platform group, managed options or Grafana Tempo may reduce ops load. See our comparison with Tempo and Grafana tracing when you already run the LGTM stack. Either way, treat tracing infrastructure like any other production dependency covered under Linux system administration and backup routines.
How do you instrument PHP and Laravel applications for Jaeger?
PHP tracing matured through OpenTelemetry PHP auto-instrumentation and manual span APIs. On Laravel 12 or 13 with PHP 8.3+, install the OpenTelemetry extension and SDK packages via Composer 2.10:
composer require open-telemetry/opentelemetry
composer require open-telemetry/exporter-otlp
composer require open-telemetry/sdk Register a service provider that bootstraps the tracer and exports over OTLP HTTP to your collector:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory;
use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
class TracingServiceProvider extends ServiceProvider
{
public function boot(): void
{
$transport = (new OtlpHttpTransportFactory())
->create('http://otel-collector:4318/v1/traces');
$provider = TracerProvider::builder()
->addSpanProcessor(new BatchSpanProcessor($transport))
->build();
app()->instance('otel.tracer', $provider->getTracer('laravel-app'));
}
} Wrap critical paths manually when auto-instrumentation misses domain logic:
$tracer = app('otel.tracer');
$span = $tracer->spanBuilder('booking.create')->startSpan();
$scope = $span->activate();
try {
$span->setAttribute('booking.destination', $destination);
$booking = $this->bookingService->create($payload);
$span->setAttribute('booking.id', $booking->id);
} catch (\Throwable $e) {
$span->recordException($e);
$span->setStatus(\OpenTelemetry\API\Trace\StatusCode::STATUS_ERROR);
throw $e;
} finally {
$scope->detach();
$span->end();
} For outbound HTTP calls to Khalti, Stripe, or SMS gateways, ensure the Guzzle middleware propagates trace context. Without it, external latency appears as a gap in your trace. Queue workers need separate instrumentation — a dispatched job starts a new trace unless you pass context in the job payload. On client portals like Mijar Law Associates, document upload and payment spans often live in the worker, not the web request.
Do not trace everything on day one. Start with checkout, auth, webhooks, and third-party integrations. Those paths fail under load and generate support tickets. Expand coverage after you confirm collector and storage sizing. For broader quality gates, fold trace IDs into your testing and optimization checklist alongside load tests.
Common Laravel tracing mistakes
- Logging trace IDs but never passing them to the frontend error report.
- Running the all-in-one Jaeger image in production without persistent storage.
- 100% sampling on high-traffic read endpoints — Elasticsearch bills grow fast.
- Forgetting opcache reload after deploy; old code paths still emit spans without new attributes.
- Missing propagation on internal microservice calls behind Apache reverse proxies.
I've hit the opcache issue on Deployer 7 releases where PHP-FPM was not reloaded. Traces looked healthy while users hit stale controllers. Reload PHP-FPM after symlink swap, same as any Laravel deploy.
How does Jaeger compare to Zipkin, Tempo, and other tracing backends?
All tracing backends ingest spans; they differ in ops model, query UX, and ecosystem fit. Jaeger's UI remains one of the most approachable for engineers debugging latency. Zipkin is lighter but shows its age in large deployments. Grafana Tempo pairs naturally if you already run Loki and Prometheus.
| Criteria | Jaeger | Zipkin | Grafana Tempo |
|---|---|---|---|
| Primary protocol | OTLP (native), legacy Jaeger format | Zipkin HTTP / JSON | OTLP, Jaeger, Zipkin |
| Storage options | Elasticsearch, Cassandra, Badger | In-memory, Elasticsearch, MySQL | Object storage (S3, GCS, MinIO) |
| UI strengths | Trace search, comparison, dependency graph | Simple timeline | Embedded in Grafana dashboards |
| Ops complexity | Medium — separate collector/query/storage | Low for small installs | Low at scale with object storage |
| Best fit | Polyglot microservices, dedicated trace UI | Legacy Java shops, quick POC | Teams already on Grafana LGTM stack |
Pick Jaeger when you want a focused tracing UI and OpenTelemetry-native ingestion without tying traces to a single vendor dashboard. Pick Tempo when metrics, logs, and traces should live in one Grafana workspace. Many teams run OpenTelemetry Collector once and fan out to both during migration.
OpenTelemetry is the instrumentation layer regardless of backend. The OpenTelemetry project documents semantic conventions and SDK setup per language. Jaeger's own docs describe storage tuning and production deployment patterns — cite both when onboarding a team.
Security matters. Spans can carry user IDs, email fragments, or payment metadata. Scrub PII at the collector processor before storage. Restrict Jaeger UI behind VPN or SSO. Treat trace data with the same retention policy as application logs on legal-tech portals where document access is audited.
For enterprise rollouts spanning multiple Laravel apps and WordPress frontends, plan tracing in the architecture phase of enterprise application development. Retrofitting propagation across ad hoc cURL calls is painful. Standardize on Guzzle or Laravel HTTP client with shared middleware early.
Tracing also complements API rate limiting work — spikes in 429 responses show up in span tags while traces reveal which upstream partner triggered the storm. Connect trace IDs to your AIOps alerting pipeline when you automate anomaly detection on latency percentiles.
After you stabilise tracing, use flamegraphs to prioritise speed optimisation work with evidence instead of assumptions. The widest span bar is your next ticket. On eCommerce builds like Quick And Easy Nepalese Grocery, checkout traces often expose delivery-zone calculation or inventory locks that profilers miss.
Maintenance contracts should include collector health checks and storage growth reviews. Tracing that silently stops exporting is worse than no tracing — you trust data that frozen at last month's deploy. Include OTLP endpoint checks in the same runbook as database backups under support and maintenance procedures.
If you build custom observability wrappers, keep JSON span exports compatible with standard tools. A quick pass through a formatter catches malformed attributes before they hit the collector. For greenfield services, custom software development sprints should define span naming conventions in the same document as API versioning rules.
Server provisioning playbooks — whether Ansible or manual — should open OTLP ports only on internal networks. Public exposure of collectors has led to span injection and data leaks in the wild. Harden the host the same way you would for Redis or MySQL admin ports. Our Ansible playbooks for PHP servers pattern applies: group vars for collector URLs, not hard-coded IPs in .env files.
External references worth bookmarking: the Jaeger documentation for storage and deployment, and the OpenTelemetry documentation for SDK and semantic conventions. The CNCF graduated project listing confirms Jaeger's long-term maintenance trajectory independent of any single vendor.
Key Takeaways
- Jaeger stores distributed traces as nested spans; OpenTelemetry is the standard instrumentation layer in 2026.
- Propagate W3C
traceparentheaders on every internal and outbound HTTP call or traces will break at service boundaries. - Start sampling at 1–5% on high-traffic endpoints; expand after storage and collector capacity are proven.
- Instrument checkout, auth, webhooks, and third-party APIs first — that is where production time disappears.
- Scrub PII in collector processors and restrict Jaeger UI access; spans often contain identifiers you cannot log publicly.
- Pair Jaeger traces with metrics and structured logs via shared trace IDs for full-stack incident response.
People Also Ask
Is Jaeger still maintained in 2026?
Yes. Jaeger remains an active CNCF graduated project with regular releases, OTLP-native collectors, and updated Helm charts. Development focus sits on OpenTelemetry compatibility rather than legacy Jaeger client libraries.
Do I need Kubernetes to run Jaeger?
No. Docker Compose on a single Ubuntu server is fine for small teams and staging. Kubernetes simplifies agent sidecars at scale, but many Laravel shops run collector and Jaeger on the same VM as the app until traffic demands separation.
What is the difference between a trace and a span in Jaeger?
A trace represents one logical request end to end and shares a single trace ID. Spans are the individual timed operations inside that trace — database query, HTTP call, queue job — each with its own span ID and parent reference.
Can Jaeger trace Laravel queue workers?
Yes, but workers do not inherit web request context automatically. Pass trace context in the job payload or create linked spans explicitly. Otherwise worker operations appear as unrelated traces and you lose the booking-to-notification story.
Ship observability that survives production traffic
Jaeger: Distributed Tracing Explained boils down to three moves: instrument with OpenTelemetry, export OTLP to a sampled collector, and read traces before you restart services blindly. The payoff is faster incidents, clearer API contracts, and optimisation work grounded in span timings instead of hunches. If you want tracing wired into your Laravel platform, payment flows, or multi-service deploy pipeline, contact us or review how we approach production systems on the home page and about page.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

