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
limitsintemplate.yamland expect queries over more than a few days to fail.
What it creates
- clickhouse — ClickHouse
25.12.5, built on the instance fromclickhouse/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 fromcollector/Dockerfile. It migrates ClickHouse's schema when it starts, then receives OTLP: gRPC on4317and HTTP on4318inside the instance, and HTTP with a bearer token on the ingest domain. - signoz — SigNoz
v0.141.1, the publishedsignoz/signozimage: 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
| Input | What to give |
|---|---|
| Where the SigNoz UI answers | A domain you control, pointed at your instance. |
| Where telemetry from outside the instance is sent | A 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: Bearerheader. - ClickHouse's password, which the collector and SigNoz connect with.
- The session secret SigNoz signs sign-ins with.
After installing
- Open the UI domain. The first account you create is the admin of the organization; do it before anyone else reaches the page.
- 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
# 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}