> For the complete documentation index, see [llms.txt](https://docs.qpoint.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.qpoint.io/guides/qtap-guides/observability-and-integration/self-hosted-clickstack-observability.md).

# Self-Hosted ClickStack Observability

**A fully self-hosted observability stack in a single container.**

This guide walks you through setting up Qtap with [ClickStack](https://clickhouse.com/clickstack) — a self-contained observability stack that bundles ClickHouse, an OTel collector, and the HyperDX UI into a single Docker image. Events and artifacts flow through OpenTelemetry, with no S3 setup required.

{% hint style="success" %}
**Quick version:** Run ClickStack, point Qtap at it, see traffic in your browser. Total setup time: \~5 minutes.
{% endhint %}

***

## What You'll Get

| Component      | What It Does                                      | Port             |
| -------------- | ------------------------------------------------- | ---------------- |
| **ClickStack** | ClickHouse + OTel collector + HyperDX UI          | 8080, 4317, 4318 |
| **Qtap**       | eBPF agent capturing traffic and sending via OTel | —                |

Both events (connection metadata, HTTP transactions) and objects (request/response headers and bodies) are sent through the same OTel endpoint. No separate object storage to configure.

***

## Prerequisites

* Docker installed
* Linux host with kernel 5.8+ (for eBPF)
* Root or `CAP_BPF` + `CAP_SYS_ADMIN` capabilities

***

## Step 1: Start ClickStack

```bash
docker run -d --name clickstack \
   -p 8080:8080 \
   -p 4317:4317 \
   -p 4318:4318 \
   -e BETA_CH_OTEL_JSON_SCHEMA_ENABLED=true \
   clickhouse/clickstack-local:latest
```

Wait for it to be ready:

```bash
docker logs -f clickstack
```

Once you see the startup complete, visit <http://localhost:8080> to access the HyperDX UI.

***

## Step 2: Create the Qtap Configuration

Create a file called `qtap-clickstack.yaml`:

```yaml
version: 2
services:
    event_stores:
        - type: otel
          endpoint: "localhost:4317"
          protocol: grpc
          service_name: "qtap"
          environment: "production"
          tls:
              enabled: false
    object_stores:
        - type: otel
          otel_endpoint: "localhost:4317"
          protocol: grpc
          service_name: "qtap"
          environment: "production"
          tls:
              enabled: false
stacks:
    basic_reporting:
        plugins:
            - type: http_capture
              config:
                  level: full
                  format: json
            - type: report_usage
tap:
    direction: egress
    ignore_loopback: true
    audit_include_dns: false
    http:
        stack: basic_reporting
```

This configuration:

* Sends **events** (connection metadata, HTTP transaction summaries) to ClickStack via OTel
* Sends **objects** (full request/response headers and bodies) to ClickStack via OTel
* Captures **all egress HTTP traffic** at full detail level
* Both stores share the same OTel endpoint — no S3 credentials needed

{% hint style="info" %}
This replaces many small S3 files with batched OTLP log records, which ClickHouse ingests efficiently.
{% endhint %}

***

## Step 3: Start Qtap

```bash
docker run -d --name qtap \
  --user 0:0 --privileged \
  --pid=host --network=host \
  -v /sys:/sys \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$(pwd)/qtap-clickstack.yaml:/app/config/qtap.yaml" \
  -e TINI_SUBREAPER=1 \
  --ulimit=memlock=-1 \
  us-docker.pkg.dev/qpoint-edge/public/qtap:v0 \
  --log-level=info \
  --log-encoding=console \
  --config="/app/config/qtap.yaml"
```

Verify Qtap is running and connected:

```bash
docker logs qtap 2>&1 | tail -20
```

You should see the eventstore and objectstore initialize successfully:

```
INFO    eventstore.otel initialized
INFO    objectstore.otel initialized
```

***

## Step 4: Generate Traffic and View

Generate some traffic:

```bash
curl https://httpbin.org/get
curl https://api.github.com
curl -X POST https://httpbin.org/post -d '{"test": "data"}'
```

Open <http://localhost:8080> in your browser. In the HyperDX UI, create a source with table name `otel_logs` to view captured events and artifacts.

You'll see:

* **Connection events** — TCP connections with protocol detection, TLS info, process attribution
* **HTTP transaction summaries** — method, URL, status, duration, content type
* **Full request/response data** — headers and bodies captured by the `http_capture` plugin

***

## How It Works

```
Your Applications
       │
       │  (HTTP/HTTPS traffic)
       ▼
    Qtap (eBPF)
       │
       ├── Events ──→ OTel (gRPC :4317) ──→ ClickStack ──→ ClickHouse
       │
       └── Objects ──→ OTel (gRPC :4317) ──→ ClickStack ──→ ClickHouse
```

Qtap captures traffic using eBPF and exports both event metadata and full artifacts as OpenTelemetry Logs. ClickStack receives them through its built-in OTel collector and stores everything in ClickHouse.

***

## Configuration Options

### Capture Level

Control how much detail is captured per request:

| Level     | What's Captured                    |
| --------- | ---------------------------------- |
| `none`    | Connection metadata only           |
| `summary` | HTTP method, URL, status, duration |
| `details` | Summary + headers                  |
| `full`    | Summary + headers + bodies         |

Change the level in the `http_capture` plugin:

```yaml
plugins:
    - type: http_capture
      config:
          level: summary    # Less data, lower storage
          format: json
```

### Traffic Direction

Control which traffic is captured:

| Direction         | What's Captured            |
| ----------------- | -------------------------- |
| `egress`          | All outbound traffic       |
| `egress-external` | Only external destinations |
| `egress-internal` | Only internal destinations |

***

## Cleanup

```bash
docker rm -f qtap clickstack
```

***

## Next Steps

* [Storage Configuration](/getting-started/qtap/configuration/storage-configuration.md) — Compare OTel vs S3 object storage options
* [Traffic Processing with Plugins](/getting-started/qtap/configuration/traffic-processing-with-plugins.md) — Filter and transform captured traffic
* [OTel Object Store Release Notes](/release-notes/otel-object-store.md) — Feature details
* [Sample Configuration](https://github.com/qpoint-io/qtap/blob/main/examples/sample-otel-objectstore-clickstack.yaml) — Reference config on GitHub


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.qpoint.io/guides/qtap-guides/observability-and-integration/self-hosted-clickstack-observability.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
