Skip to main content
Skip to main content

Setting up your OpenTelemetry Collector

This guide walks you through deploying an OpenTelemetry collector against an existing Managed ClickStack service, or adapting your existing collector, before verifying that data is flowing through.

Agent-Assisted Setup
Use curl to download, read and follow: https://clickhouse.com/docs/skills/clickstack-otel-collector/SKILL.md
Your agent will set up the ClickStack OpenTelemetry collector for you. Works with Claude Code, Cursor, Codex, and other coding agents.
What the agent will do
  1. Install clickhousectl if missing and authenticate it (asks you for a ClickHouse Cloud API key — paste in chat or run the login command yourself).
  2. Ask you for the target ClickHouse Cloud service ID or name, then fetch its HTTPS endpoint.
  3. Create a hyperdx_ingest SQL user on the service (generates a strong password, or uses one you provide).
  4. Run the ClickStack OpenTelemetry collector locally in Docker, pointed at your service.
  5. Send a short burst of synthetic logs, traces, and metrics through the collector to prove the pipeline works.
  6. Verify the data has landed in the otel database, and hand you the ClickStack UI URL where you can view it.

The collector runs as a gateway: a single OTLP endpoint that your applications, SDKs, and agent collectors send to. The gateway batches events, applies any processing you've configured, and writes them to ClickHouse via the ClickHouse exporter. This pattern keeps collection logic out of your application code and lets you scale ingestion independently of the workloads producing data. For background on gateway versus agent roles, see Collector roles.

Existing collector

If you're using an existing OpenTelemetry collector, we assume it's already configured in a gateway role. We don't recommend using this process for reconfiguring collectors in the agent role.

Pick the tab that matches your situation:

Gather your credentials

You'll need:

  • The HTTPS endpoint of your ClickHouse Cloud service, including protocol and port, for example https://abc123xyz.us-central1.gcp.clickhouse.cloud:8443.
  • A ClickHouse username and password for ingestion.

If you don't have these recorded, open your service in the ClickHouse Cloud console and select Connect. Record the url from the subsequent dialog. We will create a dedicated user for ingestion below.

Create an ingestion user

We recommend creating a dedicated user for the collector rather than reusing default. Connect to your service via the SQL console and run:

CREATE USER hyperdx_ingest IDENTIFIED WITH sha256_password BY 'ClickH0u3eRocks123!';
GRANT SELECT, INSERT, CREATE DATABASE, CREATE TABLE, CREATE VIEW ON otel.* TO hyperdx_ingest;
Tip

Replace the password in the snippet above with a strong value.

The collector creates the schema for logs, traces, and metrics inside the otel database on first use. For more guidance on production user setup, see Going to production.

Deploy the collector

Deploy the ClickStack distribution of the OpenTelemetry collector, which is preconfigured for Managed ClickStack. In the example below, we run the collector locally and generate artificial telemetry from the same machine for simplicity.

Note

In production, you would typically deploy the collector in a Kubernetes cluster, or on a virtual machine that can be reached by your OpenTelemetry SDKs, agents, and other collectors. This allows telemetry from across your environment to be centrally collected and forwarded to ClickStack.

Pick a shared secret to authenticate clients sending data to the collector, then export it alongside your connection details and chosen password for the hyperdx_ingest user:

export CLICKHOUSE_ENDPOINT=<HTTPS_ENDPOINT>
export CLICKHOUSE_USER=hyperdx_ingest
export CLICKHOUSE_PASSWORD=ClickH0u3eRocks123!
export OTLP_AUTH_TOKEN="a-strong-shared-secret"

Run the ClickStack OTel collector:

docker run -d \
  -e OTLP_AUTH_TOKEN=${OTLP_AUTH_TOKEN} \
  -e CLICKHOUSE_ENDPOINT=${CLICKHOUSE_ENDPOINT} \
  -e CLICKHOUSE_USER=${CLICKHOUSE_USER} \
  -e CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD} \
  -e HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE=otel \
  -p 4317:4317 \
  -p 4318:4318 \
  clickhouse/clickstack-otel-collector:latest

The collector now exposes OTLP gRPC on 4317 and OTLP HTTP on 4318. Applications, SDKs, and agent collectors should send to these ports with authorization: $OTLP_AUTH_TOKEN in the request headers.

Production deployments

For production, we recommend enabling TLS on the OTLP endpoint. See Securing the collector.

Verify the endpoint

Generate some synthetic traffic against the collector to confirm the full pipeline works. We use telemetrygen, the OpenTelemetry Collector Contrib generator, which emits OTLP logs, traces, and metrics and exposes flags to shape the data across services, severities, span statuses, and metric types.

Run it from its Docker image (no install required). Define a small wrapper so the commands below stay readable; --add-host lets the container reach a collector listening on the host:

telemetrygen() {
  docker run --rm --add-host=host.docker.internal:host-gateway \
    ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest "$@"
}
export OTEL_ENDPOINT=host.docker.internal:4317

Or install the binary with Go and target localhost instead:

go install github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen@latest
export OTEL_ENDPOINT=localhost:4317
What to expect

Each tg run lasts about 20 seconds (set by --duration 20s) and streams verbose logs the whole time, that's expected; each run returns on its own once its 20 seconds elapse. The logs below are enough to confirm the pipeline; the optional richer set adds several more runs and takes a few minutes.

Define a small tg helper so each command only specifies what varies (service, severity, status, attributes):

tg() { local signal=$1; shift; telemetrygen "$signal" \
  --otlp-endpoint ${OTEL_ENDPOINT} --otlp-insecure \
  --otlp-header "authorization=\"${OTLP_AUTH_TOKEN}\"" \
  --rate 5 --duration 20s "$@"; }

Send logs as a realistic mix of severities across services, mostly informational with a warning and an error rather than one uniform error stream:

tg logs --service frontend --severity-text Info  --severity-number 9  --body "GET /api/products 200" \
  --otlp-attributes 'deployment.environment="production"' \
  --telemetry-attributes 'http.method="GET"' --telemetry-attributes 'http.status_code="200"'
tg logs --service checkout --severity-text Warn  --severity-number 13 --body "retrying payment authorization" \
  --otlp-attributes 'deployment.environment="production"' \
  --telemetry-attributes 'http.method="POST"'
tg logs --service payment  --severity-text Error --severity-number 17 --body "payment gateway timeout" \
  --otlp-attributes 'deployment.environment="production"' \
  --telemetry-attributes 'http.status_code="500"'

Logs alone confirm the endpoint is working. For a richer demo dataset, a multi-service Service Map and charts across metric types, expand and run the commands below as well. They reuse the tg helper, so run them in the same shell.

Generate richer telemetry (optional)

Send multi-span traces from several healthy services plus one failing dependency. This gives the Service Map a realistic shape, mostly healthy with one erroring service, and still populates the error views:

# Healthy services: the bulk of the traffic, all spans Ok
for svc in frontend checkout cart; do
  tg traces --service "$svc" --child-spans 3 --span-duration 80ms --status-code Ok \
    --otlp-attributes 'deployment.environment="production"' \
    --telemetry-attributes "http.route=\"/$svc\""
done

# One slow dependency returning errors
tg traces --service payment --child-spans 3 --span-duration 450ms --span-links 1 --status-code Error \
  --otlp-attributes 'deployment.environment="production"' \
  --telemetry-attributes 'http.route="/charge"'

Send metrics across the three common types, so charts have a counter, a gauge, and a distribution:

tg metrics --service frontend --metric-type Sum       --otlp-metric-name http.server.requests --aggregation-temporality cumulative
tg metrics --service frontend --metric-type Gauge     --otlp-metric-name system.memory.usage
tg metrics --service payment  --metric-type Histogram --otlp-metric-name http.server.duration

For even more variety, add a couple more services and an exponential histogram:

tg traces  --service catalog   --child-spans 2 --span-duration 60ms  --status-code Ok \
  --otlp-attributes 'deployment.environment="production"' --telemetry-attributes 'http.route="/catalog"'
tg traces  --service inventory --child-spans 2 --span-duration 220ms --status-code Ok \
  --otlp-attributes 'deployment.environment="staging"'   --telemetry-attributes 'http.route="/inventory"'
tg metrics --service payment   --metric-type ExponentialHistogram --otlp-metric-name db.query.duration

For the full set of flags and more variations, see Synthetic data with telemetrygen.

Confirm in the ClickStack UI

Open your service in the ClickHouse Cloud console and select ClickStack from the left menu and then Start Ingestion.

The next step can be skipped, as you've already configured your collector. Click Launch ClickStack to continue.

ClickStack will open in a new tab and you should be automatically directed to the Getting Started page. If not, select Getting Started from the left-hand menu, then click Start Ingestion followed by Next.

You'll land on the Getting Started page. If you're prompted to begin ingestion, proceed through the onboarding screens that guide you to start a Docker collector. Since you've already completed that step, you can simply click through them. ClickStack will automatically detect your telemetry data and tables, after which you can select Start Exploring to begin using the platform.

Switch the source to Logs and set the time range to Last 15 minutes. The synthetic logs from telemetrygen should appear within a few seconds.

If nothing shows up:

  • Confirm the auth header value passed to telemetrygen matches the one your collector expects.
  • Tail your collector's logs and look for export errors.
  • Verify the ClickHouse endpoint configured on the collector includes both the protocol and port (https://...:8443).

Next steps: send your own data

The synthetic burst above only proves the pipeline works. To start sending real telemetry, instrument your own services with the ClickStack SDKs, which provide instrumentation for Node.js, Python, Go, Java, and other languages that export OTLP to the collector endpoint you just verified. For a complete worked example, follow Instrument an application.

To collect from infrastructure rather than application code:

Further reading

This guide covers a single collector instance in its simplest form. The OpenTelemetry collector reference covers what to do next: