Signoz

SigNoz: logs, metrics and traces on OpenTelemetry and ClickHouse

SigNoz on Cubeship

SigNoz is an open-source observability platform built on OpenTelemetry: traces, metrics and logs in one UI, stored in ClickHouse, with dashboards and alerts.

This template installs a single-node SigNoz on a Cubeship instance: the UI on one domain, an OpenTelemetry collector that apps on the instance send to directly and senders elsewhere reach on a second domain with a token, and the ClickHouse it all lives in.

SigNoz needs a machine with at least 8 GB of memory and 4 CPU cores. Its apps are limited to 6 GiB between them, plus its Postgres database, and ClickHouse uses what it is given. On a smaller VPS, lower limits in template.yaml and expect queries over more than a few days to fail.

What it creates

  • clickhouse — ClickHouse 25.12.5, built on the instance from clickhouse/Dockerfile, with ClickHouse Keeper running inside it. No domain; its data, and Keeper's, are in a volume at /var/lib/clickhouse.
  • collector — SigNoz's OpenTelemetry collector v0.144.9, built from collector/Dockerfile. It migrates ClickHouse's schema when it starts, then receives OTLP: gRPC on 4317 and HTTP on 4318 inside the instance, and HTTP with a bearer token on the ingest domain.
  • signoz — SigNoz v0.141.1, the published signoz/signoz image: the API, the UI and the alert manager, on the domain you choose.
  • signoz-db — a managed Postgres 16 database, where SigNoz keeps users, dashboards, alerts and settings.

These are the versions SigNoz's own Docker install, Foundry, runs together.

It needs Cubeship 0.7.0 or newer, and an admin to install it: two of its apps are built on the instance, and only admins build.

Why two apps are built

SigNoz's Docker install runs seven containers, three of which Cubeship cannot run as they are:

  • Two run once and exit: one fetches histogramQuantile, a function ClickHouse runs for percentile queries, and one migrates ClickHouse's schema. Cubeship has no one-off containers; an app that exits is a failed app.
  • ClickHouse, Keeper and the collector read configuration files, and Cubeship cannot mount a file into a container.

So clickhouse/Dockerfile is ClickHouse with the function and config.xml built in, and collector/Dockerfile is the collector with config.yaml and start.sh, which runs the migrations before the collector.

What you are asked

InputWhat to give
Where the SigNoz UI answersA domain you control, pointed at your instance.
Where telemetry from outside the instance is sentA second domain, for OTLP over HTTP from anywhere else.

Three secrets are generated and shown once — keep the first:

  • The ingest token, which senders outside the instance put in an Authorization: Bearer header.
  • ClickHouse's password, which the collector and SigNoz connect with.
  • The session secret SigNoz signs sign-ins with.

After installing

  1. Open the UI domain. The first account you create is the admin of the organization; do it before anyone else reaches the page.
  2. Send telemetry. Nothing shows until something does.

Sending telemetry

From an app on the same instance, point its OpenTelemetry SDK or collector at the collector's internal address. No token:

OTEL_EXPORTER_OTLP_ENDPOINT=http://cubeship-signoz-production-collector:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=my-app

Or gRPC, on 4317. The internal name follows the project, environment and app names you install with; it is on the app's page in the dashboard.

From anywhere else, send OTLP over HTTP to the ingest domain with the token:

OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.com
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20<ingest token>"

A request without the token is refused with 401. gRPC cannot be received from outside: a Cubeship domain carries HTTP only, so an exporter set to grpc must be switched to http/protobuf.

How the schema is migrated

SigNoz's schema is created and upgraded by the collector's migrate command. On every start, start.sh waits up to ten minutes for ClickHouse and its Keeper, runs migrate bootstrap and migrate sync up, starts migrate async up in the background — as upstream runs it beside a collector that is already receiving — and then starts the collector. Each step does nothing when there is nothing to do, so a new collector version migrates on its first start after the deploy.

If the synchronous part fails, the collector exits and Cubeship restarts it, and the migration runs again. The collector's log says why.

The collector then receives nothing until SigNoz connects to it over OpAMP, on port 4320 of the signoz app: until then it runs with every pipeline switched off, as upstream's does. On a fresh install that is the minute between the collector starting and SigNoz answering. A running collector keeps its pipelines if SigNoz goes down; one that restarts while SigNoz is down waits for it again.

ClickHouse, on one node

SigNoz creates every table ON CLUSTER cluster, replicated, and both need a coordinator — upstream runs ClickHouse Keeper in a container of its own. Here Keeper runs inside the ClickHouse server, which ClickHouse supports, and the cluster named cluster is that one server. It is the same schema a larger SigNoz runs, with one replica.

Keeper's port, 9181, has no authentication, and like ClickHouse's own ports it is reachable by other apps on the instance, not from outside it. ClickHouse itself asks for the generated password.

Retention

SigNoz keeps logs and traces for 7 days and metrics for 30. Change it in the UI under Settings → General. Disk use grows with what you send; watch the volume's machine.

The volume

The clickhouse app runs as one copy on the machine its volume is on, and a deploy stops it for a few seconds: the collector's exports fail and retry, and the UI's queries fail, until it is back. Everything SigNoz received is in /var/lib/clickhouse. What SigNoz itself keeps — accounts, dashboards, alerts — is in signoz-db, backed up like any managed database.

Updating

Change the image tags — clickhouse/Dockerfile, collector/Dockerfile, tag in template.yaml — to a set SigNoz's Foundry releases together, release this repository or your fork, and point both built apps' ref at the new release. Deploy the collector before signoz, so the schema is migrated first.

Resources

ClickHouse is limited to 2 CPUs and 4 GiB of memory, the collector and SigNoz to 1 CPU and 1 GiB each. ClickHouse sizes its caches from its limit; raise it first when queries over long ranges fail for memory.

What this creates

clickhouse

https://github.com/cubeshipd/cubeship-signoz-template

collector

https://github.com/cubeshipd/cubeship-signoz-template

signoz

signoz/signoz:v0.141.1

signoz-db

Postgres 16

/var/lib/clickhouse

Volume of clickhouse

template.yaml
# yaml-language-server: $schema=https://cubeship.dev/schema/template/v1.json
version: 1
minCubeship: "0.7.0"
project: signoz

inputs:
  - key: domain
    type: domain
    label: Where the SigNoz UI answers
  - key: ingestDomain
    type: domain
    label: Where telemetry from outside the instance is sent
    help: OTLP over HTTP, with the ingest token as a bearer token. Apps on the instance send to the collector's internal address instead.
  - key: ingestToken
    type: secret
    label: The token senders outside the instance authenticate with
    generate: 40
  - key: clickhousePassword
    type: secret
    label: ClickHouse's password
    generate: 32
  - key: jwtSecret
    type: secret
    label: The secret SigNoz signs sessions with
    generate: 48

databases:
  - key: meta
    name: signoz-db
    engine: postgres
    # The version SigNoz's own Docker install runs.
    version: "16"
    database: signoz

apps:
  # Deployed in this order: the collector waits for ClickHouse and migrates
  # its schema before it starts.
  - key: clickhouse
    name: clickhouse
    repo: https://github.com/cubeshipd/cubeship-signoz-template
    ref: v1.0.0
    build: dockerfile
    dockerfile: clickhouse/Dockerfile
    port: 8123
    health: /ping
    volumes:
      - path: /var/lib/clickhouse
    limits: { cpu: 2, memory: 4Gi }
    env:
      CLICKHOUSE_HOST: ${app.clickhouse.internal}
      CLICKHOUSE_SIGNOZ_PASSWORD: ${input.clickhousePassword}

  - key: collector
    name: collector
    repo: https://github.com/cubeshipd/cubeship-signoz-template
    ref: v1.0.0
    build: dockerfile
    dockerfile: collector/Dockerfile
    port: 4318
    # No health: a check runs against the domain's port, the receiver that
    # asks for a token, where nothing answers 2xx without one.
    domains:
      - host: ${input.ingestDomain}
        port: 4319
    limits: { cpu: 1, memory: 1Gi }
    env:
      SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN: tcp://default:${input.clickhousePassword}@${app.clickhouse.internal}:9000
      SIGNOZ_OTEL_COLLECTOR_TIMEOUT: 10m
      LOW_CARDINAL_EXCEPTION_GROUPING: "false"
      OTLP_BEARER_TOKEN: ${input.ingestToken}
      SIGNOZ_HOST: ${app.signoz.internal}

  - key: signoz
    name: signoz
    image: signoz/signoz
    tag: v0.141.1
    port: 8080
    health: /api/v1/health
    domains:
      - host: ${input.domain}
    attach:
      - database: meta
    limits: { cpu: 1, memory: 1Gi }
    env:
      SIGNOZ_SQLSTORE_PROVIDER: postgres
      SIGNOZ_SQLSTORE_POSTGRES_DSN: postgres://${db.meta.user}:${db.meta.password}@${db.meta.host}:${db.meta.port}/${db.meta.name}?sslmode=disable
      SIGNOZ_TELEMETRYSTORE_PROVIDER: clickhouse
      SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN: tcp://default:${input.clickhousePassword}@${app.clickhouse.internal}:9000
      SIGNOZ_TOKENIZER_JWT_SECRET: ${input.jwtSecret}
      # A double underscore is an underscore in the key: global.external_url.
      SIGNOZ_GLOBAL_EXTERNAL__URL: https://${input.domain}
      SIGNOZ_GLOBAL_INGESTION__URL: https://${input.ingestDomain}
      SIGNOZ_ALERTMANAGER_SIGNOZ_EXTERNAL__URL: https://${input.domain}