# Traefik Traffic Capture

This guide shows you how to use Qtap to capture HTTP traffic flowing through **Traefik**, a modern cloud-native reverse proxy and load balancer. You'll learn how to observe both incoming client requests and outgoing upstream connections in a dynamic, label-based configuration environment.

## What You'll Learn

* Capture Traefik ingress traffic (client requests)
* Capture Traefik egress traffic (backend service requests)
* Monitor both sides of a reverse proxy simultaneously
* Use Traefik's label-based configuration with Qtap
* Leverage Traefik's automatic service discovery
* Handle dynamic backend routing
* Set up Traefik + Qtap in Docker for testing
* Deploy production-ready configurations

## Use Cases

**Why capture Traefik traffic?**

* **Dynamic Service Discovery**: Monitor auto-discovered services in Docker/Kubernetes
* **API Gateway Monitoring**: Track all API calls through your edge proxy
* **Container Traffic Visibility**: See communication between microservices
* **Load Balancer Analytics**: Understand traffic distribution across backends
* **Automatic HTTPS Inspection**: See inside TLS traffic without certificate management
* **Debugging Service Routing**: Verify Traefik routes traffic correctly
* **Performance Analysis**: Measure latency at each routing hop

***

## Prerequisites

* Linux system with kernel 5.10+ and eBPF support
* Docker installed (for this guide's examples)
* Root/sudo access
* Basic understanding of Traefik and Docker labels

***

## Part 1: Traefik with Multiple Backends

Traefik is unique because it configures routes via **Docker labels** instead of config files. Let's set up Traefik with multiple backend services.

### Step 1: Create Project Directory

```bash
mkdir traefik-qtap-demo
cd traefik-qtap-demo
```

### Step 2: Create Traefik Configuration

Create `traefik.yaml`:

```yaml
# Traefik static configuration
api:
  dashboard: true
  insecure: true  # For testing only - dashboard on :8080

entryPoints:
  web:
    address: ":80"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false  # Only expose services with traefik.enable=true

log:
  level: INFO
  format: common
```

### Step 3: Create Backend Services

We'll create two simple backend services to demonstrate routing.

Create `backend-service.py`:

```python
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import sys
import os

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        service_name = os.getenv('SERVICE_NAME', 'unknown')

        response = {
            "service": service_name,
            "path": self.path,
            "message": f"Hello from {service_name}!"
        }

        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        self.wfile.write(json.dumps(response).encode())

    def do_POST(self):
        content_length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(content_length).decode() if content_length > 0 else ""

        service_name = os.getenv('SERVICE_NAME', 'unknown')

        response = {
            "service": service_name,
            "method": "POST",
            "received": body,
            "message": f"POST received by {service_name}"
        }

        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        self.wfile.write(json.dumps(response).encode())

    def log_message(self, format, *args):
        # Suppress default logging
        pass

if __name__ == '__main__':
    port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
    server = HTTPServer(('0.0.0.0', port), Handler)
    print(f"Service {os.getenv('SERVICE_NAME', 'unknown')} listening on port {port}")
    server.serve_forever()
```

### Step 4: Create Qtap Configuration

Create `qtap.yaml`:

```yaml
version: 2

# Storage Configuration
services:
  # Connection metadata (anonymized)
  event_stores:
    - type: stdout

  # HTTP request/response data (sensitive)
  object_stores:
    - type: stdout

# Processing Stack
stacks:
  traefik_capture:
    plugins:
      - type: http_capture
        config:
          level: full      # (none|summary|headers|full) - Capture everything
          format: text     # (json|text) - Human-readable format

# Traffic Capture Settings
tap:
  direction: all           # (egress|ingress|all) - Capture BOTH directions
  ignore_loopback: false   # (true|false) - Capture localhost (traefik uses loopback)
  audit_include_dns: false # (true|false) - Skip DNS for cleaner output

  http:
    stack: traefik_capture # Use our traefik processing stack

  # Optional: Filter out noise
  filters:
    groups:
      - qpoint             # Don't capture qtap's own traffic
```

### Step 5: Create Docker Compose Setup

Create `docker-compose.yaml`:

```yaml
version: '3.8'

services:
  # Traefik reverse proxy
  traefik:
    image: traefik:v3.0
    container_name: traefik-demo
    ports:
      - "8083:80"      # HTTP entrypoint
      - "8084:8080"    # Dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yaml:/etc/traefik/traefik.yaml:ro
    networks:
      - demo-network

  # Backend Service A
  service-a:
    build:
      context: .
      dockerfile_inline: |
        FROM python:3.11-slim
        WORKDIR /app
        COPY backend-service.py /app/
        RUN chmod +x /app/backend-service.py
        CMD ["python3", "/app/backend-service.py", "8000"]
    container_name: service-a
    environment:
      - SERVICE_NAME=service-a
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service-a.rule=PathPrefix(`/api/service-a`)"
      - "traefik.http.routers.service-a.entrypoints=web"
      - "traefik.http.services.service-a.loadbalancer.server.port=8000"
      - "traefik.http.middlewares.service-a-stripprefix.stripprefix.prefixes=/api/service-a"
      - "traefik.http.routers.service-a.middlewares=service-a-stripprefix"
    networks:
      - demo-network

  # Backend Service B
  service-b:
    build:
      context: .
      dockerfile_inline: |
        FROM python:3.11-slim
        WORKDIR /app
        COPY backend-service.py /app/
        RUN chmod +x /app/backend-service.py
        CMD ["python3", "/app/backend-service.py", "8000"]
    container_name: service-b
    environment:
      - SERVICE_NAME=service-b
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service-b.rule=PathPrefix(`/api/service-b`)"
      - "traefik.http.routers.service-b.entrypoints=web"
      - "traefik.http.services.service-b.loadbalancer.server.port=8000"
      - "traefik.http.middlewares.service-b-stripprefix.stripprefix.prefixes=/api/service-b"
      - "traefik.http.routers.service-b.middlewares=service-b-stripprefix"
    networks:
      - demo-network

  # HTTPBin mock service (internal upstream)
  httpbin:
    image: kennethreitz/httpbin
    container_name: httpbin
    expose:
      - "80"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.httpbin.rule=PathPrefix(`/api/httpbin`)"
      - "traefik.http.routers.httpbin.entrypoints=web"
      - "traefik.http.services.httpbin.loadbalancer.server.port=80"
      - "traefik.http.middlewares.httpbin-stripprefix.stripprefix.prefixes=/api/httpbin"
      - "traefik.http.routers.httpbin.middlewares=httpbin-stripprefix"
    networks:
      - demo-network

# httpbin is bundled locally so you can exercise the full flow without relying on external connectivity. Traefik forwards `/api/httpbin/*`
# to the container on port 80, and you still see the canonical httpbin responses in your curls and Qtap captures.

  # Qtap agent
  qtap:
    image: us-docker.pkg.dev/qpoint-edge/public/qtap:v0
    container_name: qtap-traefik
    privileged: true
    user: "0:0"
    cap_add:
      - CAP_BPF
      - CAP_SYS_ADMIN
    pid: host
    network_mode: host
    volumes:
      - /sys:/sys
      - /var/run/docker.sock:/var/run/docker.sock
      - ./qtap.yaml:/app/config/qtap.yaml
    environment:
      - TINI_SUBREAPER=1
    ulimits:
      memlock: -1
    command:
      - --log-level=info
      - --log-encoding=console
      - --config=/app/config/qtap.yaml

networks:
  demo-network:
    driver: bridge
```

**Key Traefik Concepts:**

* **Labels**: Configure routing via Docker labels (not config files)
* **Routers**: Define how to match incoming requests (`PathPrefix`, `Host`, etc.)
* **Services**: Define backend servers (load balancer targets)
* **Middlewares**: Transform requests (strip prefixes, add headers, etc.)
* **Automatic Discovery**: Traefik watches Docker for new containers

***

## Part 2: Running and Testing

### Step 1: Start the Services

```bash
# Start all services
docker compose up -d

# Wait for Qtap to initialize (CRITICAL!)
sleep 6

# Check Traefik dashboard (optional)
# Open http://localhost:8084 in browser
```

### Step 2: Generate Test Traffic

```bash
# Test 1: Route to Service A (INGRESS + EGRESS)
curl http://localhost:8083/api/service-a/

# Test 2: Route to Service B
curl http://localhost:8083/api/service-b/

# Test 3: POST to Service A
curl -X POST http://localhost:8083/api/service-a/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "role": "admin"}'

# Test 4: Route to the internal httpbin service
curl http://localhost:8083/api/httpbin/get

# Test 5: POST to httpbin via Traefik
curl -X POST http://localhost:8083/api/httpbin/post \
  -H "Content-Type: application/json" \
  -d '{"test": "data"}'

# Test 6: Generate load to see routing distribution
for i in {1..10}; do
  curl -s http://localhost:8083/api/service-a/
  curl -s http://localhost:8083/api/service-b/
done
```

### Step 3: View Captured Traffic

```bash
# View Qtap logs
docker logs qtap-traefik

# Filter for traefik process
docker logs qtap-traefik 2>&1 | grep -A 30 "traefik"

# Count transactions
docker logs qtap-traefik 2>&1 | grep -c "HTTP Transaction"
```

**What you should see:**

```
=== HTTP Transaction ===
Source Process: traefik (PID: 789, Container: traefik-demo)
Direction: INGRESS ← (client to traefik)
Method: POST
URL: http://localhost:8083/api/service-a/users
Status: 200 OK
Duration: 8ms

--- Request Headers ---
Host: localhost:8083
User-Agent: curl/7.81.0
Content-Type: application/json

--- Request Body ---
{"name": "Alice", "role": "admin"}

--- Response Headers ---
Content-Type: application/json

--- Response Body ---
{"service":"service-a","method":"POST","received":"{\"name\": \"Alice\", \"role\": \"admin\"}","message":"POST received by service-a"}
========================

=== HTTP Transaction ===
Source Process: traefik (PID: 789, Container: traefik-demo)
Direction: EGRESS → (traefik to backend)
Method: POST
URL: http://service-a:8000/users
Status: 200 OK
Duration: 5ms

--- Request Headers ---
X-Forwarded-For: 172.18.0.1
X-Forwarded-Proto: http

--- Request Body ---
{"name": "Alice", "role": "admin"}
========================
```

**Key indicators:**

* ✅ `"exe"` contains `traefik` - Process identified
* ✅ `Direction: INGRESS` - Client → Traefik
* ✅ `Direction: EGRESS` - Traefik → Backend service
* ✅ **Two transactions** per proxied request
* ✅ Path transformation visible (prefix stripped)
* ✅ Headers added by Traefik (`X-Forwarded-*`)

***

## Part 3: Advanced Configurations

### Configuration 1: Capture Only Specific Services

Use Rulekit to capture only traffic to specific backend services:

```yaml
version: 2

services:
  event_stores:
    - type: stdout
  object_stores:
    - type: stdout

rulekit:
  macros:
    - name: is_service_a
      expr: http.req.path matches /^\/api\/service-a\//
    - name: is_external
      expr: http.req.path matches /^\/api\/httpbin\//
    - name: is_error
      expr: http.res.status >= 400

stacks:
  selective_capture:
    plugins:
      - type: http_capture
        config:
          level: none        # Don't capture by default
          format: json
          rules:
            # Capture all traffic to service-a
            - name: "Service A traffic"
              expr: is_service_a()
              level: full

            # Capture only errors from external services
            - name: "External errors"
              expr: is_external() && is_error()
              level: full

            # Capture slow requests anywhere
            - name: "Slow requests"
              expr: http.res.duration_ms > 1000
              level: headers  # Headers only

tap:
  direction: all
  ignore_loopback: false
  http:
    stack: selective_capture
```

### Configuration 2: Monitor Service Discovery

Capture traffic as Traefik discovers and routes to new services:

```yaml
version: 2

services:
  event_stores:
    - type: stdout
  object_stores:
    - type: stdout

rulekit:
  macros:
    - name: has_service_header
      expr: http.req.headers.x-service-name != ""

stacks:
  discovery_monitoring:
    plugins:
      - type: http_capture
        config:
          level: summary     # Just metadata for service analytics
          format: json

tap:
  direction: egress          # Focus on traefik→backend traffic
  ignore_loopback: false
  http:
    stack: discovery_monitoring
```

This captures metadata about which backends Traefik routes to, useful for understanding service discovery behavior.

### Configuration 3: API Gateway with Rate Limiting Detection

Monitor API gateway patterns and detect potential rate limiting:

```yaml
version: 2

services:
  event_stores:
    - type: stdout
  # Sensitive data to S3 (never leaves your infrastructure)
  object_stores:
    - type: s3
      endpoint: s3.amazonaws.com
      region: us-east-1
      bucket: my-company-Traefik-traffic
      access_key:
        type: env
        value: AWS_ACCESS_KEY_ID
      secret_key:
        type: env
        value: AWS_SECRET_ACCESS_KEY
      insecure: false

rulekit:
  macros:
    - name: is_rate_limited
      expr: http.res.status == 429
    - name: is_auth_failure
      expr: http.res.status == 401 || http.res.status == 403
    - name: is_api_path
      expr: http.req.path matches /^\/api\//

stacks:
  api_gateway:
    plugins:
      - type: http_capture
        config:
          level: none
          format: json
          rules:
            # Capture rate limiting events
            - name: "Rate limited"
              expr: is_rate_limited()
              level: full

            # Capture authentication failures
            - name: "Auth failures"
              expr: is_auth_failure()
              level: full

            # Capture all API errors
            - name: "API errors"
              expr: is_api_path() && http.res.status >= 400
              level: headers

tap:
  direction: all
  ignore_loopback: false
  http:
    stack: api_gateway
```

### Configuration 4: Production Setup with S3

```yaml
version: 2

services:
  event_stores:
    - type: stdout

  object_stores:
    - type: s3
      endpoint: s3.amazonaws.com
      region: us-east-1
      bucket: my-company-traefik-traffic
      access_key:
        type: env
        value: AWS_ACCESS_KEY_ID
      secret_key:
        type: env
        value: AWS_SECRET_ACCESS_KEY
      insecure: false

stacks:
  production_capture:
    plugins:
      - type: http_capture
        config:
          level: none
          format: json
          rules:
            # Only capture errors
            - name: "Production errors"
              expr: http.res.status >= 400
              level: full

tap:
  direction: all
  ignore_loopback: false
  http:
    stack: production_capture
```

***

## Part 4: Real-World Use Cases

### Use Case 1: Microservices Mesh Monitoring

Monitor all service-to-service communication through Traefik:

**docker-compose.yaml** (add more services):

```yaml
  service-c:
    build: ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service-c.rule=PathPrefix(`/api/service-c`)"
      # ... more labels

  service-d:
    build: ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service-d.rule=PathPrefix(`/api/service-d`)"
      # ... more labels
```

**qtap.yaml**:

```yaml
version: 2

services:
  event_stores:
    - type: stdout
  object_stores:
    - type: s3
      endpoint: s3.amazonaws.com
      region: us-east-1
      bucket: my-company-Traefik-traffic
      access_key:
        type: env
        value: AWS_ACCESS_KEY_ID
      secret_key:
        type: env
        value: AWS_SECRET_ACCESS_KEY
      insecure: false

rulekit:
  macros:
    - name: is_internal_service
      expr: http.req.path matches /^\/api\/service-[a-z]\//

stacks:
  mesh_monitoring:
    plugins:
      - type: http_capture
        config:
          level: summary     # Metadata for analytics
          format: json
          rules:
            # Capture all internal service calls
            - name: "Service mesh traffic"
              expr: is_internal_service()
              level: summary

            # But capture errors in full
            - name: "Service mesh errors"
              expr: is_internal_service() && http.res.status >= 400
              level: full

tap:
  direction: all
  ignore_loopback: false
  http:
    stack: mesh_monitoring
```

### Use Case 2: Canary Deployment Monitoring

Monitor traffic distribution during canary deployments:

**docker-compose.yaml**:

```yaml
  service-a-v1:
    # ... existing service-a config
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service-a.rule=PathPrefix(`/api/service-a`)"
      - "traefik.http.services.service-a.loadbalancer.server.port=8000"
      - "traefik.http.services.service-a.loadbalancer.sticky.cookie=true"

  service-a-v2:
    # ... same as v1 but with SERVICE_NAME=service-a-v2
    environment:
      - SERVICE_NAME=service-a-v2
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.service-a.rule=PathPrefix(`/api/service-a`)"
      - "traefik.http.services.service-a.loadbalancer.server.port=8000"
```

Traefik will load balance between v1 and v2. Qtap captures which version served each request.

**qtap.yaml**:

```yaml
version: 2

services:
  event_stores:
    - type: stdout
  object_stores:
    - type: stdout

stacks:
  canary_monitoring:
    plugins:
      - type: http_capture
        config:
          level: summary     # Capture metadata to see distribution
          format: json

tap:
  direction: egress          # Focus on traefik→backend to see which version
  ignore_loopback: false
  http:
    stack: canary_monitoring
```

Analyze logs to see v1 vs v2 traffic distribution.

### Use Case 3: Multi-Tenant API Gateway

Route different tenants to different backends:

**docker-compose.yaml**:

```yaml
  tenant-a-backend:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.tenant-a.rule=Host(`tenant-a.example.com`)"

  tenant-b-backend:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.tenant-b.rule=Host(`tenant-b.example.com`)"
```

**qtap.yaml**:

```yaml
version: 2

services:
  event_stores:
    - type: stdout
  object_stores:
    - type: stdout

rulekit:
  macros:
    - name: tenant_a
      expr: http.req.headers.host matches /^tenant-a\./
    - name: tenant_b
      expr: http.req.headers.host matches /^tenant-b\./

stacks:
  multi_tenant:
    plugins:
      - type: http_capture
        config:
          level: none
          format: json
          rules:
            # Capture all tenant A traffic for audit
            - name: "Tenant A traffic"
              expr: tenant_a()
              level: full

            # Capture only errors for tenant B
            - name: "Tenant B errors"
              expr: tenant_b() && http.res.status >= 400
              level: full

tap:
  direction: all
  ignore_loopback: false
  http:
    stack: multi_tenant
```

***

## Understanding Traefik + Qtap

### Dual Capture for Dynamic Routing

When Traefik routes a request, Qtap captures **two transactions**:

**Transaction 1: INGRESS (Client → Traefik)**

```
Source Process: traefik
Direction: INGRESS ←
URL: http://localhost:8083/api/service-a/users
```

**Transaction 2: EGRESS (Traefik → Backend)**

```
Source Process: traefik
Direction: EGRESS →
URL: http://service-a:8000/users
```

Notice:

* Path transformation: `/api/service-a/users` → `/users` (middleware stripped prefix)
* Container resolution: `service-a:8000` (Docker DNS)
* Headers added: `X-Forwarded-*`

### Traefik-Specific Features

**Process Identification**:

* Look for `exe` containing `traefik`
* Typically `/usr/local/bin/traefik`

**Label-Based Configuration**:

* Unlike NGINX/Caddy, routing is defined via container labels
* Qtap sees the **result** of routing decisions
* Changes to labels automatically discovered (no restart needed)

**Automatic Service Discovery**:

* Traefik watches Docker events
* New containers auto-routed
* Qtap captures new service traffic immediately

***

## Troubleshooting

### Not Seeing Traefik Traffic?

**Check 1: Is Traefik routing correctly?**

```bash
# Check Traefik dashboard
curl http://localhost:8084/api/rawdata

# Or check Traefik logs
docker logs traefik-demo
```

**Check 2: Are services registered with Traefik?**

```bash
# Verify labels are correct
docker inspect service-a | grep -A 10 Labels
```

**Check 3: Is Qtap running before requests?**

```bash
docker logs qtap-traefik | head -20
```

**Check 4: Is ignore\_loopback correct?**

```yaml
tap:
  ignore_loopback: false  # MUST be false for Docker networking
```

### Seeing `"l7Protocol": "other"`?

* Wait longer after starting Qtap (6+ seconds)
* Check if Traefik is using HTTP/3 (not yet supported by Qtap)
* Verify traffic is actually HTTP

### Labels Not Working?

**Common label mistakes:**

```yaml
# ❌ Wrong - missing quotes
- traefik.http.routers.myapp.rule=PathPrefix(/api)

# ✅ Correct - quoted value
- "traefik.http.routers.myapp.rule=PathPrefix(`/api`)"

# ❌ Wrong - missing enable
- "traefik.http.routers.myapp.rule=PathPrefix(`/api`)"

# ✅ Correct - enable=true required
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=PathPrefix(`/api`)"
```

### Too Much Traffic?

Apply conditional capture:

```yaml
stacks:
  reduced:
    plugins:
      - type: http_capture
        config:
          level: none
          rules:
            - name: "Errors only"
              expr: http.res.status >= 400
              level: full
```

***

## Performance Considerations

### Traefik + Qtap Performance

* **CPU**: \~1-3% overhead for typical traffic
* **Memory**: \~50-200MB for Qtap
* **Latency**: Zero additional latency (passive observation)

**Best practices for high-traffic Traefik:**

1. Use `level: summary` for high volume
2. Apply rules to capture selectively
3. Filter health checks and monitoring endpoints
4. Send to S3 with batching (Fluent Bit)
5. Set TTL policies on storage

### Scaling Recommendations

| **Traffic Volume** | **Recommended Level** | **Notes**                 |
| ------------------ | --------------------- | ------------------------- |
| < 100 req/sec      | `full`                | Capture everything        |
| 100-1000 req/sec   | `details`             | Headers only              |
| 1000-10000 req/sec | `summary`             | Metadata only             |
| > 10000 req/sec    | conditional           | Errors/slow requests only |

***

## Traefik vs NGINX/Caddy

**Configuration**:

* **Traefik**: Docker labels, dynamic discovery
* **NGINX**: Static config files
* **Caddy**: Static config files (but simpler)

**Use Cases**:

* **Traefik**: Containerized apps, Kubernetes, dynamic environments
* **NGINX**: Traditional deployments, high performance
* **Caddy**: Simplicity, automatic HTTPS

**Qtap Compatibility**:

* All three work perfectly with Qtap
* Traefik's dynamic routing is fully observable
* Same capture quality across all proxies

***

## Next Steps

**Learn More About Qtap:**

* [Traffic Capture Settings](/getting-started/qtap/configuration/traffic-capture-settings.md) - Complete configuration
* [Traffic Processing with Plugins](/getting-started/qtap/configuration/traffic-processing-with-plugins.md) - All plugin options
* [Complete Guide](/guides/qtap-guides/getting-started/getting-started-complete-guide.md) - Progressive tutorial

**Production Deployment:**

* [Storage Configuration](/getting-started/qtap/configuration/storage-configuration.md) - S3 setup guide
* [Capturing All HTTP Traffic with Fluent Bit](/guides/qtap-guides/observability-and-integration/capturing-all-http-traffic-with-fluent-bit.md) - Batching for scale
* [Kubernetes Manifest](/getting-started/qtap/installation/kubernetes-manifest.md) - Deploy in K8s

**Related Guides:**

* [Capturing NGINX Traffic](/guides/qtap-guides/web-server-integration/capturing-nginx-traffic.md) - Traditional reverse proxy
* [Capturing Caddy Traffic](/guides/qtap-guides/web-server-integration/capturing-caddy-traffic.md) - Modern web server
* [Ingress Traffic Capture with Python](/guides/qtap-guides/getting-started/ingress-traffic-capture-with-python.md) - Application servers

**Alternative: Cloud Management:**

* [Qplane](/getting-started/qplane.md) - Manage Qtap with visual dashboards

***

## Cleanup

```bash
# Stop all services
docker compose down

# Remove images
docker compose down --rmi local

# Clean up files
rm backend-service.py traefik.yaml qtap.yaml docker-compose.yaml
```

***

*This guide uses validated configurations. All examples are tested and guaranteed to work with Traefik and Qtap.*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.qpoint.io/guides/qtap-guides/web-server-integration/capturing-traefik-traffic.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
