How to Self-Host SigNoz with OpenTelemetry: Docker or Helm (2026)

Nov 10, 2026 · 12 min · Ajay Kumar

This guide is for engineers who want a self-hosted, OpenTelemetry-native APM on one VM or a Kubernetes cluster, with logs, traces and metrics in one place. At the end you will have SigNoz running, a Python service sending all three signals through an OpenTelemetry Collector, logs linked to their traces, retention set, and an alert wired to a channel. Versions tested or checked on 10 November 2026: SigNoz v0.149.0 (4 November), signoz-otel-collector v0.144.15, ClickHouse 25.12.5, Foundry v0.5.0, Helm chart signoz 0.149.0, OpenTelemetry Python 1.45.2 with contrib 0.66b2, and the upstream Collector contrib 0.165.0. You need Docker Engine 20.10+ with Compose v2 and at least 4 GB of memory for Docker, or a Kubernetes cluster with Helm 3. If you want the background on why SigNoz sits on ClickHouse and how it compares with ClickStack and Uptrace, read the companion post, ClickHouse for Observability in 2026.

Where the telemetry goes: app to SigNoz in four hops Python app OTel SDK 1.45.2 OTel Collector agent, contrib 0.165.0 4317 SigNoz ingester otel-collector v0.144.15 ClickHouse 25.12.5 SigNoz server v0.149.0, UI on :8080 queries ClickHouse, metadata in Postgres 16 Alert channels Slack, webhook, email Docker install: Foundry generates the right-hand side from casting.yaml. Helm: same parts, ZooKeeper instead of Keeper.
Components and versions from the Foundry Compose example and the signoz 0.149.0 Helm chart; the agent Collector is optional but recommended.

Step 1: Choose Docker (Foundry) or Helm

The install changed in 2026, and most tutorials still show the old way. SigNoz v0.130.0, released 23 June 2026, deprecated install.sh and the Compose files under deploy/. At v0.149.0 the deploy/docker directory no longer exists; the deploy README points to Foundry. If a blog tells you to git clone and cd deploy/docker, it is out of date.

Foundry is a small CLI, foundryctl, that reads one casting.yaml and generates manifests into pours/. It has three verbs: gauge checks prerequisites, forge generates files, and cast does both and deploys. It targets Compose, Swarm, systemd, Kustomize, Helm, ECS and a few PaaS platforms.

For Kubernetes, the classic Helm chart at https://charts.signoz.io is still released alongside each SigNoz version. Use Foundry for a single VM, Helm for a cluster.

Step 2: Install SigNoz on Docker with Foundry

Install the CLI. This is the command from the SigNoz Docker install docs; read the script before piping it to a shell, as with any installer.

curl -fsSL https://signoz.io/foundry.sh -o foundry.sh
less foundry.sh          # check where it installs foundryctl
bash foundry.sh
foundryctl --help

Write a casting.yaml. The minimal one in the docs works, but it deploys latest tags. Pin the two SigNoz images so an innocent re-run does not upgrade you. ClickHouse and Keeper are already pinned to 25.12.5 by Foundry.

# casting.yaml for SigNoz v0.149.0 on Docker Compose
apiVersion: v1alpha1
kind: Installation
metadata:
  name: signoz
spec:
  deployment:
    flavor: compose
    mode: docker
  signoz:
    spec:
      image: signoz/signoz:v0.149.0
  ingester:
    spec:
      image: signoz/signoz-otel-collector:v0.144.15

Check, generate and deploy:

foundryctl gauge -f casting.yaml     # verifies docker and compose are present
foundryctl cast -f casting.yaml      # forge + docker compose up
docker ps --format 'table {{.Names}}\t{{.Status}}'

Per the docs you should see signoz-telemetrystore-clickhouse-0-0, signoz-metastore-postgres-0, signoz-telemetrykeeper-clickhousekeeper-0, signoz-ingester-1 and signoz-signoz-0, with ClickHouse, Postgres and Keeper reporting healthy. Open http://<host>:8080/ and create the admin account. OTLP is exposed on 4317 (gRPC) and 4318 (HTTP).

Two rules from the docs save a lot of grief: every change is "edit casting.yaml, run foundryctl cast again", and you never hand-edit files in pours/, because forge overwrites them.

Step 3: Or install SigNoz on Kubernetes with Helm

Create a values file that pins images and sizes ClickHouse storage. These keys are from the chart's own values.yaml at 0.149.0; I rendered them with helm template to confirm they land where expected.

# signoz-values.yaml for chart signoz 0.149.0
global:
  storageClass: gp3          # a class that allows volume expansion
clickhouse:
  image:
    tag: 25.12.5
  persistence:
    size: 100Gi              # chart default is 20Gi
signoz:
  image:
    tag: v0.149.0
otelCollector:
  image:
    tag: v0.144.15
helm repo add signoz https://charts.signoz.io
helm repo update
helm install signoz signoz/signoz --version 0.149.0 \
  --namespace signoz --create-namespace \
  --wait --timeout 1h \
  -f signoz-values.yaml

kubectl get pods -n signoz
kubectl port-forward -n signoz svc/signoz 8080:8080
curl -s http://localhost:8080/api/v1/health
# {"status":"ok"}

In-cluster apps send OTLP to signoz-otel-collector.signoz.svc.cluster.local on 4317 or 4318, as the Kubernetes install page shows. The Helm chart still uses ZooKeeper (signoz/zookeeper:3.7.1) rather than Keeper.

Step 4: Send a test span with curl

Before instrumenting anything, prove the ingest path works with one hand-written OTLP/HTTP request. This isolates network and port problems from SDK problems.

NOW=$(date +%s)
cat > span.json <<EOF
{"resourceSpans":[{"resource":{"attributes":[
  {"key":"service.name","value":{"stringValue":"curl-test"}}]},
 "scopeSpans":[{"scope":{"name":"manual"},"spans":[{
  "traceId":"5b8efff798038103d269b633813fc60c",
  "spanId":"eee19b7ec3c1b174",
  "name":"hello-signoz","kind":2,
  "startTimeUnixNano":"${NOW}000000000",
  "endTimeUnixNano":"${NOW}500000000"}]}]}]}
EOF
curl -s -i -X POST http://localhost:4318/v1/traces \
  -H 'Content-Type: application/json' --data @span.json | head -1
# HTTP/1.1 200 OK

A 200 means the ingester accepted it. Within a minute curl-test should appear under Services. Note the timestamp: the ingester's filter/drop_stale processor drops data older than 360 hours, so replaying an old fixture with fixed timestamps silently disappears.

Step 5: Instrument a Python app with OpenTelemetry

I tested this exact setup in a clean virtualenv on Python 3.12. Pin the packages; contrib versions are 0.66b2 and must match the 1.45.2 SDK.

python3 -m venv .venv && . .venv/bin/activate
pip install flask==3.1.3 opentelemetry-distro==0.66b2 opentelemetry-exporter-otlp==1.45.2
opentelemetry-bootstrap -a install     # installs flask, logging, requests... instrumentations
# app.py
import logging
from flask import Flask

app = Flask(__name__)
log = logging.getLogger("checkout")
log.setLevel(logging.INFO)

@app.get("/checkout")
def checkout():
    log.info("checkout started")
    return {"ok": True}

Run it under the auto-instrumentation wrapper, pointing at the local agent Collector from Step 6 (or straight at SigNoz on 4317 for a quick test):

export OTEL_SERVICE_NAME=checkout-api
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=dev,service.version=1.4.0
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_TRACES_EXPORTER=otlp OTEL_METRICS_EXPORTER=otlp OTEL_LOGS_EXPORTER=otlp
opentelemetry-instrument flask --app app run --port 5000

To see what the SDK emits without any backend, set OTEL_TRACES_EXPORTER=console OTEL_LOGS_EXPORTER=console. When I did that and hit /checkout, the log record and the span shared a trace ID, trimmed here:

"body": "checkout started",
"trace_id": "0x3aa15e86a36432d9eb9cbd2e2e4b8a7c",
...
"name": "GET /checkout",
"trace_id": "0x3aa15e86a36432d9eb9cbd2e2e4b8a7c",

One change in 0.66b2 worth knowing: the old OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true variable now triggers a warning that the SDK's LoggingHandler is deprecated and asks you to unset it. Without it, logs were still exported with trace context in my test.

Step 6: Put an OpenTelemetry Collector agent in front

Apps can talk to SigNoz directly, but a local agent gives you batching, retries, memory limits, host metadata and one place to change the backend later. If you run this on Kubernetes, my OpenTelemetry Collector setup guide covers the DaemonSet pattern.

# otel-agent.yaml for otel/opentelemetry-collector-contrib:0.165.0
receivers:
  otlp:
    protocols:
      grpc: { endpoint: 0.0.0.0:4317 }
      http: { endpoint: 0.0.0.0:4318 }

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  resourcedetection:
    detectors: [env, system]
  batch:
    send_batch_size: 8192
    timeout: 5s

exporters:
  otlp_grpc:                         # was "otlp" before collector v0.144.0
    endpoint: signoz-host:4317       # Helm: signoz-otel-collector.signoz.svc.cluster.local:4317
    tls:
      insecure: true                 # use TLS once SigNoz sits behind a proxy

service:
  pipelines:
    traces:  { receivers: [otlp], processors: [memory_limiter, resourcedetection, batch], exporters: [otlp_grpc] }
    metrics: { receivers: [otlp], processors: [memory_limiter, resourcedetection, batch], exporters: [otlp_grpc] }
    logs:    { receivers: [otlp], processors: [memory_limiter, resourcedetection, batch], exporters: [otlp_grpc] }
docker run -d --name otel-agent -p 4317:4317 -p 4318:4318 \
  -v "$PWD/otel-agent.yaml:/etc/otelcol-contrib/config.yaml:ro" \
  otel/opentelemetry-collector-contrib:0.165.0
docker logs otel-agent 2>&1 | grep -i -E "error|everything is ready"

The exporter name matters. The Collector changelog for v0.144.0 renamed otlp to otlp_grpc and otlphttp to otlp_http, keeping the old names as deprecated aliases. Old configs still start today; new ones should use the new names. If the agent and SigNoz share a host, publish the agent on different ports or the two will collide on 4317.

Step 7: Correlate logs, traces and metrics

Correlation in SigNoz works on two things: the trace_id and span_id on each log record, and consistent resource attributes. Set service.name and deployment.environment the same way for every signal, which the environment variables in Step 5 do. Then you can move from a span to the log records that carry its trace ID, and from a log record back to its trace. Service metrics (RED: rate, errors, duration) come from the ingester's signozspanmetrics processor computing them from spans, so you get latency charts even before you emit custom metrics.

If your logs are emitted by something other than the SDK (container stdout, a file), add the trace ID to the log line yourself and parse it into the trace_id field with a log pipeline. My structured logging guide shows the JSON shape.

Step 8: Set retention

Per the retention docs, self-hosted SigNoz keeps logs and traces for 15 days and metrics for 30 days by default. Change it in Settings, Workspace tab, Retention Controls, one signal at a time, and click Save for each. Two caveats: the new value applies only to data ingested after the change, and data that reaches its TTL is deleted permanently. Size disk before raising retention; the companion post has a SQL query to measure compression per table.

Step 9: Create an alert and a channel

Create the channel first: Alerts, Notification Channels, New channel. The notification docs list Slack, webhook, PagerDuty, Opsgenie, MS Teams, Google Chat, email and others. Then Alerts, Alert Rules, + New Alert, and pick metric, log, trace, exceptions or anomaly based. A good first rule is a trace-based error rate on checkout-api above 5% over 5 minutes. Fire it once on purpose (stop the app or return 500s) before you trust it.

Step 10: Upgrade without surprises

With Foundry, change the image tags in casting.yaml and run foundryctl cast -f casting.yaml; the migrator container applies schema migrations. With Helm, bump --version and the tags together. Read release notes first. The v0.149.0 notes warn that log bodies are moving to native JSON storage and that logs are now dual-ingested, which needs more collector CPU and memory, ClickHouse CPU and log disk. Upgrade staging, watch disk for a day, then production.

# Helm upgrade, pinned
helm upgrade signoz signoz/signoz --version 0.149.0 -n signoz -f signoz-values.yaml --wait

Troubleshooting

Symptom Likely cause Fix
cd deploy/docker: no such directory Following a pre-2026 tutorial Use Foundry (Step 2); see the migration guide for old installs
Containers restart in a loop on Docker Under 4 GB of memory for Docker Raise Docker memory; check docker compose -f pours/deployment/compose.yaml logs -f signoz-signoz-0
Keeper crashes on Windows Docker Desktop on Windows Use WSL 2 with Docker Engine inside it, as the docs advise
UI loads, no services listed App not exporting, or wrong port/protocol Run the curl test (Step 4); gRPC is 4317, HTTP is 4318 with /v1/traces paths
Python exporter errors with gRPC endpoint on 4318 Protocol and port mismatch Set OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf with 4318, or grpc with 4317
Old or replayed data never appears filter/drop_stale drops data older than 360h Use current timestamps; this is by design
Logs show but no link to traces Logs lack trace_id, or service.name differs Emit logs through the SDK or parse trace IDs; align resource attributes
Agent warns about deprecated otlp exporter Collector v0.144.0+ renamed it Rename to otlp_grpc (or otlp_http)
Port 4317 already allocated Agent and SigNoz on one host Map the agent to other ports or drop the agent on that host
Disk filling after upgrade to v0.149.0 JSON log body dual-ingestion Add disk, shorten log retention, or delay the upgrade

FAQ

Is self-hosted SigNoz free?

The Community Edition is MIT-licensed outside the ee/ and cmd/enterprise/ directories, and you can run it without a licence key. Enterprise features and support are paid; SigNoz's pricing page says the Enterprise tier, which includes a self-hosted option, starts at $4,000 a month.

Is the SigNoz install.sh script still supported?

No. It was deprecated in v0.130.0 in June 2026 along with the bundled Compose files. Foundry is the supported installer, and the migration guide reattaches existing volumes so data is preserved.

Does SigNoz need its own collector, or can I use upstream OpenTelemetry?

Use both. SigNoz's ingester is its own collector distribution with ClickHouse exporters and span-metrics; your apps and agents can run the upstream Collector and forward OTLP to it.

How much hardware does SigNoz need?

The Docker docs set a floor of 4 GB of memory for Docker. Beyond that it depends on ingest volume and retention; SigNoz publishes a resource planning guide, and ClickHouse disk is usually the first limit you hit.

When I set this up for a team, the first thing I check is the curl test from the app's own network, because most "no data" reports are a port or protocol mismatch, not a SigNoz problem. Next I pin every image, set retention to match the disk I actually have, and put an upstream Collector agent in front so the backend stays swappable. Official references: SigNoz Docker install, Kubernetes install, Foundry, and the OpenTelemetry Python docs.

Related: How to Set Up the OpenTelemetry Collector on Kubernetes (2026), How to Trace an LLM App with OpenTelemetry and Langfuse (2026) and Distributed Tracing Explained.

Spot an error or something out of date? Email me and I will fix it and log it on the corrections page.

I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.

Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.


Related