Observability AWS

Jaeger 2.22.0 on AWS User Guide

| Product: Jaeger 2.22.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Jaeger 2.22.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg tracing and APM listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Jaeger is an open source distributed tracing platform, a graduated project of the Cloud Native Computing Foundation. Your services send it OpenTelemetry (OTLP) traces, and Jaeger stores them and shows how one request travels through your system: which services it touched, how long each step took and where errors happened. Jaeger 2 is built on the OpenTelemetry Collector.

What the cloudimg image adds:

  • No default login, for people and for applications. Jaeger has no authentication of its own, so this image never exposes it. nginx serves the web UI, the query API and OTLP/HTTP trace ingest over HTTPS with HTTP basic authentication, and the password is generated on your instance's first boot and written to /root/jaeger-credentials.txt (root only). The image ships no password, no password hash and no TLS key.
  • Jaeger on localhost only. Every port Jaeger opens (16686 query and web UI, 16685 query gRPC, 4317 OTLP gRPC, 4318 OTLP HTTP, 13133 health) is bound to 127.0.0.1; nginx on port 443 is the only way in. The unauthenticated /healthz path answers ok for load balancer health checks and reveals nothing else.
  • HTTPS from the first boot. The certificate is generated on your instance for its own addresses, port 80 redirects to HTTPS, and requests for a host name the instance does not serve are dropped. Cross-site posts and WebSocket upgrades from other web sites are refused.
  • Built from source with a patched toolchain. cloudimg compiles Jaeger 2.22.0 from the upstream source with Go 1.27.3 and a patched golang.org/x/net, so govulncheck finds no reachable vulnerability in the binary (the upstream release binary was built with Go 1.27.1). The build record, with every checksum, is in /usr/share/doc/jaeger/BUILD-PROVENANCE.txt; no compiler or Go toolchain is left on the image.
  • Fail closed. nginx and Jaeger start only after first boot has finished. If it cannot finish, nothing is served.
  • Nothing sent home, no self-telemetry. Jaeger does not trace itself into your store and does not serve its own metrics endpoint, so the database holds only the traces you send it and the instance makes no outbound calls.
  • No metadata access. jaeger.service, nginx.service and the first boot service are blocked from the instance metadata service, and the AMI requires IMDSv2.
  • Honest client addresses. nginx hands Jaeger a fixed host name and the real client address from the connection, never forwarding headers a client sends.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Jaeger 2.22.0 (one binary with the OTLP receiver, the query service and the web UI) storing traces in an embedded Badger database on the local disk, nginx 1.24 and OpenSSL 3.0 (from Ubuntu 24.04), cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

The Jaeger search page listing traces of the frontend service with their spans, services and durations

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH) from your admin network and TCP 443 (web UI and trace ingest) only from the people and the applications or collectors that send traces. Port 80 only redirects to HTTPS, so you can leave it closed. Never open 16686, 16685, 4317, 4318 or 13133: Jaeger listens on the loopback address only.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Jaeger 2.22.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above. Traces are stored on the root volume under /var/lib/jaeger/data, so give it room for your traffic and retention (48 hours by default); 30 GiB or more is a sensible start.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=30,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=jaeger}]'

First boot takes a few seconds after the instance is running, and Jaeger is ready a few seconds after that.

Step 2: Connect to your instance

Version SSH user
Jaeger 2.22.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot sets the password and the certificate, then nginx and Jaeger start:

systemctl is-active jaeger nginx jaeger-firstboot
sudo test -f /var/lib/cloudimg/jaeger-firstboot.done && echo FIRSTBOOT_DONE

All three units are active and the first boot marker is present:

active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on network ports; everything Jaeger opens is on the loopback address:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"),("nginx"),("nginx"))
127.0.0.1:13133 users:(("jaeger"))
127.0.0.1:16685 users:(("jaeger"))
127.0.0.1:16686 users:(("jaeger"))
127.0.0.1:4317 users:(("jaeger"))
127.0.0.1:4318 users:(("jaeger"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"),("nginx"),("nginx"))

Jaeger's own health endpoint, on the loopback address:

curl -s http://127.0.0.1:13133/status | jq -r .status
StatusOK

Step 4: Get your URL and password

sudo cat /root/jaeger-credentials.txt

The file is readable by root only:

# Jaeger 2.22.0 on Ubuntu 24.04 (cloudimg) - generated on this instance's first boot.
# This password is unique to this instance; none was baked into the image. Keep this file secret.

JAEGER_URL=https://<public-ip>/
JAEGER_USER=admin
JAEGER_PASSWORD=<JAEGER_PASSWORD>
OTLP_HTTP_TRACES_URL=https://<public-ip>/v1/traces

# Web UI + query API:  https://<host>/   (HTTP basic auth with the user and password above)
# Trace ingest:        OTLP/HTTP (protobuf or JSON) POST to https://<host>/v1/traces with the SAME basic auth, e.g.
#                      OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<host>/v1/traces
#                      OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Basic <base64 of admin:password>"
#                      OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE=<a copy of /etc/nginx/tls/jaeger.crt>
# OTLP gRPC (4317) and HTTP (4318) listen on 127.0.0.1 only: use an SSH tunnel or a collector on this instance.
# Health:  https://<host>/healthz   (no login; answers "ok")
# The certificate is self signed (minted on this instance): accept the browser warning once, give senders the
# certificate, or install your own. Serve a DNS name instead:  sudo jaeger-set-hostname <your-dns-name>
# Data (Badger): /var/lib/jaeger/data     Self-test: sudo /usr/local/sbin/jaeger-selftest.sh
# User guide: https://www.cloudimg.co.uk/guides/jaeger-2-22-0-on-ubuntu-24-04-aws/

The URL uses the instance's public IP address from the EC2 instance metadata. The certificate also names the public DNS name and the private IP address, so https://<public-dns-name>/ and https://<private-ip>/ work too. If you stop and start the instance without an Elastic IP, the boot service picks up the new address and issues a new certificate for it. To serve Jaeger under your own DNS name, see Step 9.

A request without the password gets nginx's 401, and the health check answers without one:

curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1/
curl -sk https://127.0.0.1/healthz
401
ok

Step 5: Send a first trace

Applications send traces with OTLP over HTTP to https://<host>/v1/traces, with the user admin and the password from the credentials file. To see it work on the instance, send one example trace of a checkout request that passes through three services (frontend, shop-api and payments, seven spans), in OTLP's JSON encoding.

The small shell function jg passes the password from the credentials file to curl on standard input, so it never appears on a command line:

jg() { sudo sed -n 's/^JAEGER_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/jaeger-credentials.txt | curl -sgk -K - "$@"; }
TRACE=$(openssl rand -hex 16); T0=$(( $(date +%s) - 5 ))
ts() { printf '%s%09d' "${T0}" $(( $1 * 1000000 )); }
span() { # id parent name kind start_ms end_ms attributes-json
  printf '{"traceId":"%s","spanId":"%s","parentSpanId":"%s","name":"%s","kind":%s,"startTimeUnixNano":"%s","endTimeUnixNano":"%s","attributes":[%s]}' \
    "${TRACE}" "$1" "$2" "$3" "$4" "$(ts "$5")" "$(ts "$6")" "$7"; }
attr() { printf '{"key":"%s","value":{"stringValue":"%s"}}' "$1" "$2"; }
svc() { printf '{"resource":{"attributes":[%s]},"scopeSpans":[{"scope":{"name":"demo"},"spans":[%s]}]}' "$(attr service.name "$1")" "$2"; }
FRONT="$(span 1000000000000001 "" "GET /checkout" 2 0 420 "$(attr http.route /checkout),$(attr http.request.method GET)"),$(span 1000000000000002 1000000000000001 "POST /api/orders" 3 20 400 "$(attr server.address shop-api)")"
SHOP="$(span 2000000000000001 1000000000000002 "POST /api/orders" 2 30 390 "$(attr http.route /api/orders)"),$(span 2000000000000002 2000000000000001 "SELECT orders" 3 40 160 "$(attr db.system.name postgresql),$(attr db.operation.name SELECT)"),$(span 2000000000000003 2000000000000001 "POST /charge" 3 170 380 "$(attr server.address payments)")"
PAY="$(span 3000000000000001 2000000000000003 "POST /charge" 2 180 375 "$(attr http.route /charge)"),$(span 3000000000000002 3000000000000001 "card-network authorize" 3 200 360 "$(attr peer.service card-network)")"
printf '{"resourceSpans":[%s,%s,%s]}' "$(svc frontend "${FRONT}")" "$(svc shop-api "${SHOP}")" "$(svc payments "${PAY}")" > /tmp/demo-trace.json
jg -X POST -H 'Content-Type: application/json' --data-binary @/tmp/demo-trace.json https://127.0.0.1/v1/traces; echo
echo "trace id: ${TRACE}"

Jaeger accepts the whole trace (an empty partialSuccess means no span was rejected):

{"partialSuccess":{}}
trace id: 85ebcfe31dbcdf5c6d81179a713e86b1

The same upload without the password is refused by nginx before it reaches Jaeger:

curl -sk -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' --data-binary @/tmp/demo-trace.json https://127.0.0.1/v1/traces
401

Step 6: Explore the traces

Ask Jaeger which services and operations it has seen, through the query API (the same API the web UI uses):

jg() { sudo sed -n 's/^JAEGER_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/jaeger-credentials.txt | curl -sgk -K - "$@"; }
jg https://127.0.0.1/api/v3/services; echo
jg "https://127.0.0.1/api/v3/operations?service=shop-api" | jq -c '[.operations[].name]'
{"services":["frontend","payments","shop-api"]}
["POST /api/orders","POST /charge","SELECT orders"]

Search for the traces that touched payments in the last hour and list their spans:

jg() { sudo sed -n 's/^JAEGER_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/jaeger-credentials.txt | curl -sgk -K - "$@"; }
jg -G https://127.0.0.1/api/v3/traces --data-urlencode query.service_name=payments \
  --data-urlencode "query.start_time_min=$(date -u -d '-1 hour' +%Y-%m-%dT%H:%M:%SZ)" \
  --data-urlencode "query.start_time_max=$(date -u -d '+1 minute' +%Y-%m-%dT%H:%M:%SZ)" --data-urlencode query.search_depth=1 \
  | jq -r '.result.resourceSpans[] | (.resource.attributes[] | select(.key=="service.name") | .value.stringValue) as $svc | .scopeSpans[].spans[] | "\($svc): \(.name)"' | sort -u
frontend: GET /checkout
frontend: POST /api/orders
payments: POST /charge
payments: card-network authorize
shop-api: POST /api/orders
shop-api: POST /charge
shop-api: SELECT orders

Jaeger also derives the service dependency graph from the traces:

jg() { sudo sed -n 's/^JAEGER_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/jaeger-credentials.txt | curl -sgk -K - "$@"; }
jg -G https://127.0.0.1/api/dependencies --data-urlencode endTs=$(( $(date +%s) * 1000 )) --data-urlencode lookback=3600000 | jq -c '.data'
[{"parent":"frontend","child":"shop-api","callCount":1},{"parent":"shop-api","child":"payments","callCount":1}]

In the browser, open the URL from the credentials file. Your browser warns once about the self signed certificate; accept it (or install your own certificate, Step 9), then sign in as admin with the password from the credentials file. On Search, pick a service (here frontend) and choose Find Traces: the chart shows each trace's duration over time and the table lists the traces with their services and span counts (the screenshot at the top of this guide shows six example traces).

Click a trace to open its timeline. Each row is a span, indented under its parent, and the bars show when it ran and for how long; here the database query and the card network call are the two longest steps inside shop-api and payments:

The trace timeline of the GET /checkout request through frontend, shop-api and payments, seven spans over 420 ms

System Architecture draws the dependency graph between your services from the traces it has stored:

The System Architecture view with frontend calling shop-api and shop-api calling payments

The example data is yours to delete: it ages out after 48 hours, or stop Jaeger, empty /var/lib/jaeger/data/badger and start it again for a clean store.

Step 7: Send traces from your applications

Point your OpenTelemetry SDKs or your OpenTelemetry Collector at https://<host>/v1/traces (the private IP or your DNS name, which the certificate names) with basic authentication. Copy the instance's certificate (/etc/nginx/tls/jaeger.crt) to the senders so they can verify it, or install a certificate your systems already trust (Step 9).

Any OpenTelemetry SDK (Java, Python, Go, Node.js, .NET and others) reads the standard environment variables. The header value is Basic followed by the base64 of admin:<password>, with the space written as %20:

OTEL_SERVICE_NAME=my-service
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<host>/v1/traces
OTEL_EXPORTER_OTLP_TRACES_HEADERS=Authorization=Basic%20<base64 of admin:password>
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE=/etc/ssl/jaeger.crt

Compute the base64 value on the instance without the password appearing on a command line:

sudo sed -n 's/^JAEGER_PASSWORD=//p' /root/jaeger-credentials.txt | tr -d '\n' | sed 's/^/admin:/' | base64 -w0; echo

OpenTelemetry Collector (or Grafana Alloy's otelcol components): send with the otlphttp exporter and the basicauth extension:

extensions:
  basicauth/jaeger:
    client_auth:
      username: admin
      password: ${env:JAEGER_PASSWORD}
exporters:
  otlphttp/jaeger:
    endpoint: https://<host>
    auth:
      authenticator: basicauth/jaeger
    tls:
      ca_file: /etc/otelcol/jaeger.crt
service:
  extensions: [basicauth/jaeger]
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlphttp/jaeger]

OTLP gRPC (port 4317) is not exposed. To use it from your workstation, forward it over SSH and send to localhost:4317 without TLS:

ssh -i <your-key.pem> -N -L 4317:127.0.0.1:4317 ubuntu@<public-ip>

Keep the password in a secrets store or a file only the sender can read, never in source code. The Jaeger documentation and the OpenTelemetry documentation cover instrumenting each language.

Step 8: Storage and retention

Jaeger stores traces in an embedded Badger database on the local disk under /var/lib/jaeger/data/badger, with no separate database to run. It keeps traces for 48 hours; traces you archive from the web UI (Archive Trace) go to a second store, /var/lib/jaeger/data/badger-archive, kept for 30 days. The settings are in /etc/jaeger/config.yaml:

sudo grep -A6 'cloudimg_store:' /etc/jaeger/config.yaml | head -7
      cloudimg_store:
        badger:
          directories:
            keys: /var/lib/jaeger/data/badger
            values: /var/lib/jaeger/data/badger
          ephemeral: false
          ttl:

To keep traces for 7 days, change spans: 48h under cloudimg_store to spans: 168h (sudo nano /etc/jaeger/config.yaml), then sudo systemctl restart jaeger. Keep every 127.0.0.1 endpoint as it is. Badger reserves its value log files in 2 GiB pieces (sparse files, so they use only the space their data needs). For higher volumes or long retention, Jaeger can use Elasticsearch, OpenSearch or Cassandra instead (jaeger_storage backends; see the storage documentation). jaeger.service is blocked from the instance metadata service, so it cannot use an instance role; a remote store needs its own credentials.

Step 9: Your own DNS name and certificate

Point a DNS record at the instance (an Elastic IP keeps the address stable), then set the name Jaeger is served as. This mints a new self signed certificate for the name, updates nginx, the list of origins the web UI may post from and the URLs in /root/jaeger-credentials.txt, and keeps the name across reboots:

sudo jaeger-set-hostname <your-domain>

To use a certificate from a public or internal certificate authority instead (for example one issued by Let's Encrypt for <your-domain>), copy it over the generated pair and reload nginx (running jaeger-set-hostname again replaces it with a self signed one):

sudo install -m 0644 <your-domain>.crt /etc/nginx/tls/jaeger.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/tls/jaeger.key
sudo nginx -t && sudo systemctl reload nginx

Step 10: Security checks

The shipped self-test checks the exact set of listening ports and that every Jaeger port is on the loopback address, that Jaeger holds no outbound connection, that an unknown host name is dropped, that requests to the web UI, the query API and trace ingest without a password, with admin/admin or with a wrong password are refused by nginx while this instance's password is accepted, that a cross-site post or WebSocket upgrade is refused, and that the instance metadata service cannot be reached from inside the jaeger and nginx services while it can from your shell. The password is handed to curl in a root only temporary file, never on a command line:

sudo /usr/local/sbin/jaeger-selftest.sh
selftest OK: listening tcp lo:13133 lo:16685 lo:16686 lo:4317 lo:4318 lo:53 pub:22 pub:443 pub:80 | jaeger listeners 127.0.0.1:13133 127.0.0.1:16685 127.0.0.1:16686 127.0.0.1:4317 127.0.0.1:4318 (loopback only), no outbound connection; healthz ok, unknown Host dropped (444); anonymous UI+API+ingest 401, admin/admin and wrong passwords 401 (nginx auth), per-instance password 200 (UI + query API), cross-origin POST and WebSocket 403; instance metadata blocked inside jaeger and nginx (000), reachable from the host shell (200)

Add --ingest to also send one span for a service called cloudimg-selftest and read it back.

The metadata block is a systemd setting on each service:

systemctl show -p IPAddressDeny jaeger nginx jaeger-firstboot
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

A request for a host name the instance does not serve gets no answer at all (nginx closes the connection, so curl exits with code 52):

curl -sk -o /dev/null -w '%{http_code}\n' -H 'Host: attacker.example' https://127.0.0.1/healthz; echo "curl exit $?"
000
curl exit 52

The certificate was generated on this instance for its own addresses:

sudo openssl x509 -in /etc/nginx/tls/jaeger.crt -noout -subject -ext subjectAltName
subject=CN = <public-ip>, O = cloudimg
X509v3 Subject Alternative Name: 
    IP Address:<public-ip>, DNS:<public-dns-name>, IP Address:<private-ip>, DNS:<private-dns-name>, IP Address:127.0.0.1, DNS:localhost

Step 11: Versions, build record and files

/usr/local/bin/jaeger version 2>/dev/null; echo
nginx -v 2>&1
grep -E '^(tag commit|toolchain |govulncheck)' /usr/share/doc/jaeger/BUILD-PROVENANCE.txt | cut -c1-110
{"gitCommit":"462bb49fcff9af4be9556bf0206d07f5171bd8d7","gitVersion":"v2.22.0","buildDate":"2026-10-06T11:44:34Z"}
nginx version: nginx/1.24.0 (Ubuntu)
tag commit        462bb49fcff9af4be9556bf0206d07f5171bd8d7
toolchain         go1.27.3 linux/amd64, official tarball https://go.dev/dl/go1.27.3.linux-amd64.tar.gz
govulncheck       v1.9.0 -mode=binary on this binary: Your code is affected by 0 vulnerabilities. This scan al
Path What it is
/usr/local/bin/jaeger The Jaeger binary, run by jaeger.service as the jaeger user
/etc/jaeger/config.yaml Jaeger's settings: receivers, storage and retention, the loopback endpoints
/var/lib/jaeger/data Trace storage (Badger) and the archive store
/etc/nginx/sites-available/cloudimg-jaeger nginx site: HTTPS, basic authentication, proxy to 127.0.0.1:16686 and 127.0.0.1:4318
/etc/nginx/jaeger.htpasswd The basic authentication user and password hash (SHA-512 crypt)
/etc/nginx/tls/ This instance's certificate and private key
/root/jaeger-credentials.txt URL, user and password (root only)
/usr/share/doc/jaeger/ The Apache License 2.0, Jaeger's NOTICE and the build record with the source offer

Step 12: Changing the password

Run these lines in your SSH session. The new password is read at a prompt and reaches openssl on standard input, so it never appears on a command line or in your shell history:

read -rsp 'New password: ' NEWPASS; echo
printf 'admin:%s\n' "$(printf '%s\n' "$NEWPASS" | openssl passwd -6 -stdin)" | sudo tee /etc/nginx/jaeger.htpasswd >/dev/null
unset NEWPASS

nginx reads the file on every request, so the new password works at once; update it on every SDK and collector that sends traces. Then update or delete JAEGER_PASSWORD in /root/jaeger-credentials.txt (sudo nano /root/jaeger-credentials.txt). To give each team or collector its own login, append another user:hash line in the same format.

Step 13: Security recommendations

  • Keep port 443 open only to the networks that need the web UI and the applications or collectors that send traces, and 22 to your admin network. Never open 16686, 16685, 4317, 4318 or 13133.
  • Use your own DNS name and a certificate from a certificate authority your senders trust (Step 9).
  • Keep the password in a secrets store and change it when people leave (Step 12). Traces can carry URLs, user identifiers and query text, so treat them as sensitive data and scrub attributes in your SDK or collector where needed.
  • The instance applies Ubuntu security updates automatically. To update Jaeger itself, read the release notes, back up /var/lib/jaeger/data, and replace /usr/local/bin/jaeger with a newer build, checking its checksum against the release's sha256sum.txt; then restart the service.

Support and licensing

Jaeger is open source software licensed under the Apache License 2.0. This image ships Jaeger 2.22.0 compiled from the unmodified upstream source (github.com/jaegertracing/jaeger at v2.22.0, with the jaeger-ui v2.22.0 web assets) and one dependency update (golang.org/x/net 0.58.0 to 0.60.0); the licence, the NOTICE file and the full build record are in /usr/share/doc/jaeger, and cloudimg provides the corresponding source on request. nginx is licensed under the 2-clause BSD license. cloudimg provides 24/7 support for this image by email at support@cloudimg.co.uk and through the cloudimg website: we help with launching, instrumenting applications and collectors, reading traces, storage and retention, TLS with your own domain, upgrades and troubleshooting.

Jaeger is a trademark of The Linux Foundation. OpenTelemetry is a trademark of The Linux Foundation. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.