2026-05-03·14 min read·

AWS X-Ray EU Alternative 2026: The Art.30 Audit-Trail Paradox and Distributed Trace GDPR Exposure

Post #787 in the sota.io EU Compliance Series

Every distributed trace AWS X-Ray captures is also a detailed log of which user did what, when, and through which internal services. That makes X-Ray traces personal data under GDPR Art.4(1) — and a CLOUD Act disclosure target that Amazon can hand to US law enforcement without notifying you.

The deeper paradox: under GDPR Art.30, you are required to maintain records of your data processing activities. AWS X-Ray is the obvious tool for this — it maps exactly which services touch which data. But X-Ray itself stores those records with a US company under CLOUD Act jurisdiction. Your Art.30 compliance evidence lives in a system that can be compelled without your knowledge.

This is the GDPR analysis of AWS X-Ray and the EU-sovereign APM alternatives for 2026.


What AWS X-Ray Does and Why It's a GDPR Hotspot

AWS X-Ray is a distributed tracing service. Every request that flows through your application generates a trace: a structured record of every service call, database query, external API request, and error that occurred while handling it. X-Ray aggregates these traces, visualises service dependencies in a Service Map, and helps engineers diagnose latency and errors.

The functionality is valuable. The data it captures creates four overlapping GDPR problems.

Problem 1: Traces contain personal data by default.

A trace for a user login request might capture: the HTTP path /api/auth/login, the email address passed as a query parameter or request annotation, the user's IP address in the trace metadata, the session token returned in the response annotation, and the database query SELECT * FROM users WHERE email = ?. AWS X-Ray captures this automatically. Engineers don't configure it manually — the SDK instruments everything by default.

Under GDPR Art.4(1), any information relating to an identified or identifiable natural person is personal data. Email addresses, IP addresses, session tokens, and user IDs are all personal data. Your X-Ray traces are full of them.

Problem 2: X-Ray stores traces in Amazon's infrastructure under CLOUD Act jurisdiction.

AWS is a US company. The CLOUD Act (Clarifying Lawful Overseas Use of Data Act, 2018) requires US cloud providers to disclose data to US law enforcement when compelled, regardless of where the data is physically stored. EU servers do not protect you.

More critically, CLOUD Act disclosures come with gag order provisions. Amazon can be compelled to hand over your X-Ray traces — and forbidden from telling you it happened. You would never know that US law enforcement had reviewed which of your users logged in, what they queried, and which internal services handled their requests.

Problem 3: The X-Ray Service Map reveals your internal architecture under gag order.

X-Ray's Service Map is one of its most useful features: a real-time visual graph of every service in your application, the call relationships between them, and the data flows connecting them. Under a CLOUD Act gag order, that Service Map becomes a blueprint of your systems handed to a foreign government without your knowledge or consent.

For regulated industries — healthcare, finance, legal services — this creates a confidentiality exposure beyond GDPR. Your entire data processing architecture is visible to a jurisdiction with no equivalent EU adequacy obligations.

Problem 4: The Art.30 Audit-Trail Paradox.

GDPR Art.30 requires you to maintain records of processing activities. For a complex microservices architecture, distributed traces are the most accurate record of which services process which personal data in which order. X-Ray generates exactly this documentation automatically.

The paradox: using X-Ray for Art.30 compliance stores your compliance evidence with a US company under CLOUD Act jurisdiction. Your Art.30 records are in a system that can be accessed by US authorities without EU oversight. The tool that makes GDPR compliance easiest also creates a GDPR compliance problem.


Five GDPR Obligations AWS X-Ray Triggers

1. Art.5(1)(c) — Data Minimisation Violations

The GDPR data minimisation principle requires collecting only personal data adequate, relevant, and limited to what is necessary. AWS X-Ray's default SDK configuration captures full HTTP request and response annotations, database query strings, and error messages — all of which may contain personal data far beyond what is necessary for APM purposes.

The fix — stripping PII from traces before ingestion — is not automatic. It requires custom SDK configuration that most teams never implement. The default state is over-collection.

Concrete risk: An e-commerce platform using X-Ray to trace checkout flows captures full query strings that include customer email addresses and delivery addresses. This data is stored in X-Ray for the default 30-day retention period with no per-field deletion capability.

2. Art.17 — The Erasure Gap

When a user exercises their Art.17 right to erasure, you must delete all personal data relating to them. X-Ray has no API for granular trace deletion by user ID. Your only option is to delete entire trace groups or wait for the retention period to expire — neither of which satisfies a specific erasure request.

If a user submits a GDPR deletion request and you're using X-Ray, you cannot demonstrate that their data has been deleted from your observability layer. An Art.17 request creates a compliance gap that X-Ray's architecture makes structurally impossible to close.

3. Art.25 — Privacy by Design Failure

GDPR Art.25 requires data protection by design and by default. Systems that collect personal data should be configured to minimise collection without requiring opt-out configuration. X-Ray defaults are the opposite: maximum collection, with PII stripping requiring explicit engineering effort.

Art.25 compliance requires documented configuration showing that PII collection was minimised at the instrument level — not just a policy statement. X-Ray's default SDK behaviour fails this bar unless you implement and document custom filtering.

4. Art.28 — Data Processing Agreement Requirements

X-Ray processes personal data on your behalf. Under Art.28, this requires a Data Processing Agreement with Amazon that specifies sub-processor obligations, transfer mechanisms, and data subject rights support.

AWS's DPA covers X-Ray, but it does not resolve the CLOUD Act conflict. A DPA cannot override US statutory law. The CJEU's Schrems II decision (C-311/18) established that contractual safeguards cannot make a US-law-governed transfer adequate when US surveillance law applies. Your Art.28 DPA with Amazon is real — and insufficient.

5. Art.46 — Third-Country Transfer Without Adequate Protection

X-Ray data is processed by Amazon US infrastructure regardless of your AWS region setting. The CLOUD Act creates a US government access right that EU DPAs and Standard Contractual Clauses cannot override.

Post-Schrems II, transfers to the US require a Transfer Impact Assessment (TIA) documenting whether US surveillance law creates a risk disproportionate to the purpose. For X-Ray specifically, the risk is concrete: traces contain personal data, the CLOUD Act applies, and gag orders prevent notification. A TIA for X-Ray should classify this transfer as high-risk.


The Service Map Architecture Exposure

Beyond individual user data, X-Ray's Service Map creates an organisational-level disclosure risk that has no direct GDPR equivalent but matters for regulated industries and enterprises.

The Service Map shows:

Under a CLOUD Act subpoena with a gag order, this map is accessible to US law enforcement without your knowledge. For fintech companies, the map reveals which payment processors you integrate with and at what volume. For health tech, it reveals which diagnostic services receive patient queries. For legal tech, it reveals which document storage and analysis services process privileged materials.

None of this is GDPR-specific, but it is a real intelligence exposure for companies using X-Ray on sensitive workloads.


EU-Sovereign Distributed Tracing Alternatives

Option 1: Jaeger (Self-Hosted on EU Infrastructure)

Jaeger is the CNCF open-source distributed tracing system originally developed at Uber. It implements the OpenTelemetry protocol and supports all major language SDKs. Running Jaeger on EU-sovereign infrastructure (Hetzner, OVHcloud, IONOS) gives you full data sovereignty with no US-company dependency.

Architecture for GDPR compliance:

# jaeger-values.yaml — Helm deployment on Hetzner k8s
# Node affinity locks Jaeger to EU region nodes
collector:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/region
            operator: In
            values: ["eu-central"]
  env:
    - name: SPAN_STORAGE_TYPE
      value: elasticsearch
    - name: ES_SERVER_URLS
      value: "https://elasticsearch.eu-internal:9200"
    - name: ES_TAGS_AS_FIELDS_ALL
      value: "true"

query:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/region
            operator: In
            values: ["eu-central"]

PII sanitization before ingestion (Art.25 Privacy by Design):

# otel_pii_filter.py — OpenTelemetry span processor that strips PII
import re
from opentelemetry.sdk.trace import SpanProcessor

PII_PATTERNS = [
    (re.compile(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'), '[EMAIL]'),
    (re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b'), '[IP]'),
    (re.compile(r'Bearer\s+[A-Za-z0-9\-._~+/]+=*'), 'Bearer [TOKEN]'),
    (re.compile(r'session[_-]?(?:id|token)[=:]\s*[^\s&]+', re.I), 'session=[REDACTED]'),
    (re.compile(r'password[=:]\s*[^\s&]+', re.I), 'password=[REDACTED]'),
]

class PIISanitizingProcessor(SpanProcessor):
    def _sanitize(self, value: str) -> str:
        for pattern, replacement in PII_PATTERNS:
            value = pattern.sub(replacement, value)
        return value

    def on_start(self, span, parent_context=None):
        for key, value in list(span.attributes.items()):
            if isinstance(value, str):
                sanitized = self._sanitize(value)
                if sanitized != value:
                    span.set_attribute(key, sanitized)

    def on_end(self, span):
        # sanitize http.url and db.statement on end — captures dynamic values
        for attr in ['http.url', 'http.target', 'db.statement', 'http.request.body']:
            if attr in span.attributes:
                span.set_attribute(attr, self._sanitize(str(span.attributes[attr])))

User erasure support (Art.17):

# jaeger_erasure.py — delete all spans containing a user ID from Jaeger/ES
from elasticsearch import Elasticsearch

def erase_user_traces(user_id: str, es_client: Elasticsearch):
    """
    Deletes all trace spans referencing user_id.
    Run in response to GDPR Art.17 erasure requests.
    Logs deletion count for Art.30 erasure records.
    """
    query = {
        "query": {
            "bool": {
                "should": [
                    {"match": {"tags.user.id": user_id}},
                    {"match": {"tags.enduser.id": user_id}},
                    {"wildcard": {"tags.http.url": f"*{user_id}*"}},
                ]
            }
        }
    }
    result = es_client.delete_by_query(
        index="jaeger-span-*",
        body=query,
        wait_for_completion=True
    )
    deleted = result.get("deleted", 0)
    print(f"GDPR Art.17: deleted {deleted} spans for user {user_id}")
    return deleted

Jaeger's Elasticsearch backend supports field-level deletion and time-based retention policies. You can implement Art.17 erasure by user ID — something X-Ray's architecture makes impossible.

Option 2: Grafana Tempo (Self-Hosted)

Grafana Tempo is a horizontally scalable distributed tracing backend that stores traces in object storage (S3-compatible, which includes EU-sovereign options like Hetzner Object Storage or OVHcloud Swift). It integrates natively with Grafana dashboards and Prometheus metrics.

Tempo deployment with EU-sovereign object storage:

# tempo-config.yaml
storage:
  trace:
    backend: s3
    s3:
      bucket: traces-eu-central
      endpoint: fsn1.your-objectstorage.com   # Hetzner Object Storage Frankfurt
      region: eu-central
      access_key: ${OBJECT_STORAGE_ACCESS_KEY}
      secret_key: ${OBJECT_STORAGE_SECRET_KEY}
      insecure: false

# Retention: 30 days for operational traces, 90 days for Art.30 audit traces
compactor:
  compaction:
    block_retention: 720h   # 30 days operational

Tempo advantage: Unlike X-Ray, Tempo stores traces as flat Parquet files in object storage. Implementing Art.17 erasure is possible by re-writing Parquet files with the target user's spans removed — complex but structurally achievable. X-Ray offers no equivalent.

Option 3: OpenTelemetry Collector with EU Backend Routing

The OpenTelemetry Collector is a vendor-neutral agent that receives traces from your application, transforms them, and exports to any backend. This architecture lets you use the same application instrumentation code while routing traces exclusively to EU-sovereign backends.

Collector pipeline with PII filtering and EU routing:

# otel-collector.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  # Strip PII from spans before storage (Art.25 Privacy by Design)
  redaction:
    allow_all_keys: false
    allowed_keys:
      - http.method
      - http.status_code
      - http.route
      - db.system
      - db.operation
      - rpc.method
      - rpc.service
      - span.kind
      - error
    blocked_keys_regex:
      - ".*email.*"
      - ".*password.*"
      - ".*token.*"
      - ".*session.*"
      - ".*user_id.*"   # use pseudonymised trace_id instead
    summary: debug

  # Batch for efficient export
  batch:
    timeout: 5s
    send_batch_size: 1000

exporters:
  # Jaeger on EU infrastructure
  jaeger:
    endpoint: jaeger.eu-internal:14250
    tls:
      insecure: false
      ca_file: /certs/eu-ca.crt

  # Tempo on EU infrastructure
  otlp/tempo:
    endpoint: tempo.eu-internal:4317

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [redaction, batch]
      exporters: [jaeger, otlp/tempo]

Advantage: This architecture makes EU backend migration transparent to application code. You can switch backends, add EU regions, or add filtering rules without touching application instrumentation. X-Ray requires AWS SDK changes to migrate away.


Art.30 Records of Processing: What Distributed Traces Should Document

Here is an Art.30-compliant trace annotation structure for GDPR documentation using OpenTelemetry:

# gdpr_trace_context.py — GDPR-aware span attributes
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
import hashlib

def create_gdpr_span(
    tracer,
    name: str,
    processing_purpose: str,       # Art.5(1)(b): purpose of processing
    legal_basis: str,               # Art.6(1): legal basis
    data_categories: list[str],     # Art.30(1)(c): categories of data
    pseudonymised_subject_id: str = None,  # Art.4(5): pseudonymisation
):
    """
    Creates an OpenTelemetry span with GDPR Art.30 documentation attributes.
    Use pseudonymised IDs — never real user IDs in trace attributes.
    """
    span = tracer.start_span(name)
    span.set_attribute("gdpr.processing_purpose", processing_purpose)
    span.set_attribute("gdpr.legal_basis", legal_basis)
    span.set_attribute("gdpr.data_categories", ",".join(data_categories))
    span.set_attribute("gdpr.processor", "self-hosted-jaeger-eu-central")
    span.set_attribute("gdpr.jurisdiction", "EU")

    if pseudonymised_subject_id:
        # Hash the ID so it's pseudonymised in traces but still linkable for erasure
        hashed = hashlib.sha256(pseudonymised_subject_id.encode()).hexdigest()[:16]
        span.set_attribute("gdpr.subject_pseudonym", hashed)

    return span

# Usage:
# with create_gdpr_span(
#     tracer,
#     "process_checkout",
#     processing_purpose="contract_performance",
#     legal_basis="art6_1b_contract",
#     data_categories=["contact_data", "payment_reference"],
#     pseudonymised_subject_id=user_id,
# ) as span:
#     # your processing logic

This approach turns distributed traces into Art.30-compliant processing records that:

AWS X-Ray has no equivalent structured annotation system for GDPR documentation.


Migration Path from AWS X-Ray

Step 1: Deploy OpenTelemetry Collector on EU infrastructure (Day 1)

The Collector runs as a sidecar or standalone service. It accepts X-Ray format traces via the AWS X-Ray receiver and re-exports them to any backend.

# Add AWS X-Ray receiver to existing Collector config
receivers:
  awsxray:
    endpoint: 0.0.0.0:2000
    proxy_server:
      endpoint: 0.0.0.0:2000

This lets you run X-Ray SDK in your application unchanged while routing traces to EU backends.

Step 2: Replace X-Ray SDK with OpenTelemetry SDK (Week 1–2)

OpenTelemetry SDKs are available for Python, Node.js, Java, Go, .NET, Ruby, and PHP. The migration is straightforward for most frameworks — replace the X-Ray SDK initialisation with OpenTelemetry initialisation.

# Before (X-Ray):
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.ext.flask.middleware import XRayMiddleware
xray_recorder.configure(service='my-service', plugins=('EC2Plugin',))

# After (OpenTelemetry with EU Jaeger backend):
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="https://jaeger-collector.eu-internal:4317")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

Step 3: Implement PII sanitization processor (Week 1–2)

Add the PIISanitizingProcessor from above to your OpenTelemetry SDK configuration before any exporter.

Step 4: Configure retention and erasure APIs (Week 2–3)

Set Jaeger/Elasticsearch retention policies to match your Art.5(1)(e) storage limitation commitments. Implement the erase_user_traces function and wire it to your GDPR request handling workflow.

Step 5: Disable X-Ray (Week 3–4)

Once telemetry is flowing to EU backends and your team is comfortable with Grafana/Jaeger dashboards, remove X-Ray SDK dependencies and terminate X-Ray service access.


Sota.io and APM Observability

Sota.io runs exclusively on EU infrastructure with no US-parent dependencies. If you're running application workloads on sota.io, your observability stack can be EU-sovereign from day one.

Self-hosted Jaeger on EU VMs or Kubernetes clusters runs within the same jurisdiction as your application code. Traces never leave EU infrastructure. Your Art.30 documentation is stored where your processing happens — with full control over retention, deletion, and access.

The architecture that creates X-Ray's GDPR problems — US-company storage, CLOUD Act jurisdiction, no per-user deletion API — disappears when you control the tracing infrastructure.

See sota.io EU-sovereign infrastructure →


Summary: The X-Ray GDPR Compliance Checklist

Before using AWS X-Ray in production with EU personal data, you need answers to all of these:

ObligationX-Ray StatusRequirement
Art.5(1)(c) Data Minimisation❌ Default over-collectionCustom SDK PII filtering required
Art.17 Erasure❌ No per-user deletion APICannot satisfy specific erasure requests
Art.25 Privacy by Design❌ Requires opt-out configurationDocument PII stripping in DPA exhibit
Art.28 DPA⚠️ AWS DPA existsDoes not override CLOUD Act
Art.30 Processing Records⚠️ Traces provide recordsBut stored with US company under CLOUD Act
Art.46 Third-Country Transfer❌ CLOUD Act appliesSchrems II TIA must classify as high-risk
Gag Order Architecture Exposure❌ Service Map accessibleNo technical mitigation possible

Every item marked ❌ represents a structural limitation — not a configuration problem. The EU-sovereign alternatives (Jaeger, Grafana Tempo, OpenTelemetry Collector on EU VMs) resolve all of them.

Your distributed traces document how personal data flows through your systems. That documentation belongs in infrastructure you control, stored in a jurisdiction you trust.

EU-Native Hosting

Ready to move to EU-sovereign infrastructure?

sota.io is a German-hosted PaaS — no CLOUD Act exposure, no US jurisdiction, full GDPR compliance by design. Deploy your first app in minutes.