How to self-host Superlog without surprise costs
Superlog turns OTLP traces, logs and metrics into agent-triaged incidents. See the Docker Compose stack, four Node services, no release tags, and real VPS footprint.
Wetin self-hosting Superlog really dey install
To self-host Superlog, you go clone the repository, bring up Postgres, ClickHouse and an OpenTelemetry collector with Docker Compose, run one database migration, then start four Node services from source. Your applications go send OTLP (OpenTelemetry protocol) traces, logs and metrics to an intake port. Superlog go fingerprint dem, group the repeated ones into one incident, and an agent go write the first triage draft. The install fit take one afternoon. The footprint and the real limits na the parts wey you need read before you start.
Superlog get Apache 2.0 license and e dey for github.com/superloglabs/superlog. As of August 2026, e get around 1.2k stars, roughly 460 commits for main, and no release tags at all. That last point affect the install: git checkout v1.0.0 get nothing wey you fit check out, so you go pin one commit by yourself, or run anything wey main happen to dey for the morning wey you clone am.
Wetin Superlog dey answer wey Uptime Kuma and Langfuse no dey answer
Self-hosted monitoring tools fit look like say dem dey do the same thing from outside. But dem no be the same, and if you run the wrong one, you go use server resource without gain.
- Uptime Kuma dey probe your endpoints from outside and e answer one question: e dey up?
- Zabbix dey watch hosts and services for Ubuntu 24.04, with CPU, memory, disk and service state against thresholds wey you set.
- Langfuse dey trace LLM calls, e dey record the prompt, the model, the tokens, the latency and the cost of each call.
- Superlog dey collect the telemetry wey your normal services already dey emit and turn repeated failures into incidents.
Superlog dey handle different question: something break, wetin break, and why. E no get opinion about LLM calls, and e no dey probe you from outside. E dey ingest OTLP from your normal application code and put agent for the triage step. Na the first check wey human on call go do anyway.
The difference wey matter for VPS budget na the storage. Uptime Kuma fit run well with 1 GB of RAM because e dey store only few thousand check results. Superlog carry column store because telemetry dey write once, then dem dey query am by time range across millions of rows. Na wetin ClickHouse dey do, and Postgres no fit do this same work. Postgres still dey inside the stack, where e dey hold small relational data: projects, users, incidents and ingest keys.
Wetin docker compose up -d really dey start?
Three containers, and none of dem be Superlog. Dis one dey surprise people wey expect say one command go install everything.
postgres:16, wey publish for host port 5434clickhouse/clickhouse-server:26.1, for 8123 for HTTP and 9000 for the native protocolotel/opentelemetry-collector-contrib:0.150.1, for 4317 for gRPC and 4318 for OTLP over HTTP
The Superlog applications dey run for host, from source, and pnpm dev dey start dem. As of August 2026, repository no get production compose file, so long-lived install mean say you go need your own systemd units around each app start script, or use the per-app Dockerfiles wey dey inside the tree.
Keep the path wey one span dey follow for mind, because every failure below mean say one hop for that path don break. Your app dey post OTLP go the Superlog intake proxy. The proxy authenticate the request with your ingest key, add the project id to am, then forward am go the collector. The collector remove any superlog.* attributes wey client try set, add superlog.project_id from the header wey proxy provide, batch the data, and write am to ClickHouse. The web app and the API then read telemetry back from ClickHouse, while dem dey read everything else from Postgres.
That attribute stripping na real multi-tenancy control; e no be decoration. Without am, anybody wey get one valid ingest key fit set superlog.project_id by themselves and write data inside another project.
Wetin size VPS need?
Plan for 4 vCPU, 8 GB RAM and 40 GB SSD for one node install with low ingest volume. Na planning floor, no be measurement, so use am as starting size and check am against your own traffic.
The memory dey go four places. ClickHouse dey built for machines wey get plenty RAM, and na that the defaults assume. Postgres 16 no too heavy here, because e dey hold metadata instead of telemetry. The collector no too heavy too. The four Node processes no be like that: one Vite development server plus three tsx watch processes each dey hold hundreds of megabytes. Na why pnpm dev for 2 GB box dey cause plenty wahala.
Disk na the quieter problem. pnpm install for this monorepo dey pull AWS SDK, ClickHouse client, OpenTelemetry SDK and React toolchain before you ingest even one span. ClickHouse go then grow as your traffic dey grow. Measure both:
df -h /
free -m
docker stats --no-stream
docker compose exec clickhouse clickhouse-client --database superlog --query "SELECT table, formatReadableSize(sum(bytes_on_disk)) AS size FROM system.parts WHERE active AND database = 'superlog' GROUP BY table ORDER BY sum(bytes_on_disk) DESC"For low volume, where small number of services dey send few hundred spans per minute, the box dey quiet and ClickHouse dey idle most of the time. Na burst dey cause serious load: one bad deploy fit produce thousands of identical errors per minute. Fingerprinting dey combine dem into one incident for the reader, but ClickHouse still dey write every row underneath.
Na you go set retention. The collector's ClickHouse exporter dey create the tables, otel_traces, otel_logs and one table for each metric type. E only apply a time to live if the config for infra/collector/config.yaml set one. Nothing go expire by itself, so one busy month fit fill the whole disk unless you plan for am.
Install from a pinned commit
git clone https://github.com/superloglabs/superlog.git
cd superlog
git tag -l
git log -1 --format='%H %cs %s'git tag -l no dey print anything, and na this result dem expect as of August 2026. Pick the commit wey you test, then remain on am:
git checkout 0d3a6c8bb63eda3493e6ba0003e7c2a70750bc1eNext na the toolchain:
node -v
corepack enable
corepack prepare pnpm@9.12.0 --activate
pnpm -vpackage.json declare engines.node as >=20.0.0 and packageManager as pnpm@9.12.0. If you run the install with older Node, pnpm go stop with ERR_PNPM_UNSUPPORTED_ENGINE and name the version wey e need. The nodejs package for the Ubuntu 24.04 archive old pass version 20, so install Node 20 or newer from NodeSource or nvm. The repository get .nvmrc, so nvm use go choose the intended version if you get nvm.
pnpm install
docker compose up -d
docker compose psWait for the health checks; no trust up -d alone to mean say service ready. Postgres and ClickHouse both declare one for the compose file:
curl -sS http://127.0.0.1:8123/ping
pg_isready -h 127.0.0.1 -p 5434 -U postgresClickHouse dey answer Ok., and pg_isready dey answer accepting connections. If connection refuse on 8123, e mean say the container still dey start or e don die. docker compose logs clickhouse go show which one, and docker inspect $(docker compose ps -q clickhouse) | grep -i oomkilled go report true when kernel kill am because of memory. This one point to the box being too small, no be your config.
Then na the migration and the applications:
pnpm --filter @superlog/db db:migrate
pnpm devTake note of the port: 5434, no be 5432. The compose file publish Postgres for 5434 so e no go clash with Postgres wey already dey installed for the host. The app .env.example files match am, together with DATABASE_URL=postgres://postgres:postgres@localhost:5434/superlog. If you point the migration to 5432 for a box wey already dey run Postgres, you fit get refused connection or, worse, migration fit apply to the wrong database.
pnpm dev start the four processes wey repository list for Procfile: api, web, worker and proxy. Each one tee e output enter tmp/logs/, so tail -f tmp/logs/proxy.log na where you monitor ingest. The README put the web app for http://localhost:5173, the API for http://localhost:4100, and the OTLP intake for http://localhost:4101.
Confirm wetin actually bind before you point anything there:
ss -lntp | grep -E '4100|4101|5173'
curl -sS http://127.0.0.1:4101/healthThis one go matter later. The proxy read e own port from the PORT environment variable, and e fall back to 4000 when PORT no set. The development stack set am for you. But systemd unit wey you write yourself no set am, so exporter wey target 4101 while proxy dey listen on 4000 go fail with connection refused and give you no other clue.
Send one trace, produce one error, see one incident
Create one project for the web app and copy its ingest key. The intake dey authenticate every request with that key, so any telemetry wey dem send without am no go reach ClickHouse.
Point any OpenTelemetry SDK to the intake with the standard environment variables:
export OTEL_SERVICE_NAME=checkout-api
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4101
export OTEL_EXPORTER_OTLP_HEADERS='x-api-key=YOUR_INGEST_KEY'The intake dey read the key from the x-api-key header, and e also accept authorization: bearer YOUR_INGEST_KEY if your exporter easier to configure that way. E dey serve the three standard OTLP paths, /v1/traces, /v1/logs and /v1/metrics, plus /health.
One trap dey worth naming. OTEL_EXPORTER_OTLP_ENDPOINT na base URL, and the SDK go append the signal path to am. The signal-specific variables like OTEL_EXPORTER_OTLP_TRACES_ENDPOINT dey used exactly as dem dey written, with no path appended. If you set the signal-specific variable to http://127.0.0.1:4101, every export go post to /. That one no be route, so nothing go arrive, and the SDK go log export failure while your app still look healthy.
For one Node service, the zero-code path dey enough to prove the pipeline:
npm install @opentelemetry/api @opentelemetry/auto-instrumentations-node
node --require @opentelemetry/auto-instrumentations-node/register server.jsNow break something on purpose. Any route wey throws go work:
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/boomCheck the hops one after another, because the first gap go show you which one fail:
tail -n 50 tmp/logs/proxy.log
docker compose exec clickhouse clickhouse-client --database superlog --query 'SELECT count() FROM otel_traces'If the count for otel_traces dey rise while the web app empty, na project mismatch. Check which project the ingest key belong to. If the count flat but the proxy log show activity, check the collector or the ClickHouse write, then read docker compose logs collector. If the proxy log show no activity at all, the exporter never reach the intake: wrong port, wrong path, or rejected key.
For the web app, those repeated failures go arrive as one incident instead of one row for every request. Superlog dey fingerprint incoming signals and group the ones wey match, so inbox no go hold 4,000 identical errors when one page fit hold one. The agent then write its investigation on top of that group.
The investigation step dey call one model, so the worker need a model provider wey dem configure. Take those variable names from the .env.example file inside each app directory of the commit wey you pin, instead of any external write-up, because dem dey change together with main. The same thing apply to the GitHub and Sentry integrations. Dem get their own setup documents for docs/github-app-setup.md and docs/sentry-app-setup.md, and webhook payloads dey documented for docs/webhooks.md.
Make the intake private, and make the agent read only
Docker dey publish container ports for 0.0.0.0 by default. These published ports dey bypass ufw, because Docker dey write im own rules for the DOCKER-USER chain, and dem rules dey process before ufw see the packet. For VPS wey get public IP, the compose file as e come dey put ClickHouse HTTP for 8123 and Postgres for 5434, where internet fit reach dem. The credentials for that file na development defaults: ClickHouse user default with empty password, and Postgres with postgres as both user and password.
Bind dem to loopback. Every published port for the compose file dey take the host side from environment variable, so one .env for the repository root don do:
POSTGRES_HOST_PORT=127.0.0.1:5434
CLICKHOUSE_HTTP_HOST_PORT=127.0.0.1:8123
CLICKHOUSE_TCP_HOST_PORT=127.0.0.1:9000
COLLECTOR_GRPC_HOST_PORT=127.0.0.1:4317
COLLECTOR_HTTP_HOST_PORT=127.0.0.1:4318Verify the result before you trust am, then recreate the containers:
docker compose config
docker compose up -d
ss -lntp | grep -E '5434|8123|9000|4317|4318'docker compose config dey print the resolved file, so you fit read 127.0.0.1:5434:5432 instead of guessing. ss suppose then show 127.0.0.1:5434 and never 0.0.0.0:5434. No try fix this with compose override file wey re-declare ports, because Compose dey join port lists across files instead of replacing dem. That one go leave both bindings, and the public one still open.
The intake need the same care. Your ingest key dey travel inside header, so e need TLS (transport layer security) in front of am. Terminate TLS for nginx or Caddy before the proxy, or keep ingest inside private network or WireGuard tunnel. The web app for 5173 na Vite development server, and e no suppose face internet at all.
Then the agent itself. Superlog's pitch na say the agent investigates and proposes fix, and the important word na proposes. Keep am read only against production until you don watch am work for some real incidents. Give the GitHub App read scopes, and allow am open pull requests wey you go review. Agent wey dey read telemetry and write patch dey useful. Agent wey fit restart your services na different level of risk. That one suppose be decision wey you make deliberately, instead of default wey you inherit. Cost need the same attention, because every investigation na model call: budget for agent spend on a VPS before you point am at noisy production system, and keep record of wetin the agent actually do so surprising pull request go get audit trail behind am.
Wahala wey you go meet, and strings wey name dem
ERR_PNPM_UNSUPPORTED_ENGINEduringpnpm installmean say Node old pass 20.node -vgo confirm am for one line.ECONNREFUSED 127.0.0.1:5434during the migration mean say compose stack no dey up, orDATABASE_URLname wrong port.- If ClickHouse dey restart for loop, na usually memory issue. Read
docker compose logs clickhouse, then check whether container getOOMKilledset totrue. - If exporter report success while web app still empty, e usually mean say data go direct to collector for 4318. This bypass the project stamping wey proxy dey do.
- If connection refuse on 4101 for production install, e mean say proxy fall back to
PORT=4000. SetPORTclearly inside the unit file. - If
docker compose psshow0.0.0.0:8123, e mean say your loopback bindings no dey take effect. Rundocker compose configand read the ports wey system resolve.
Flawless, HyperProbe, and where Superlog dey fit
This category still young, and the tools no agree on wetin agent fit touch. Flawless na open source AI SRE (site reliability engineering) tool wey target Kubernetes. E dey read from existing Prometheus, Loki and Grafana stack instead of owning the pipeline. HyperProbe dey go the other way. Na hosted product be that, and as of August 2026, e be closed source. E puts read only probes inside running process to capture variable state, then exposes that state to assistant through MCP (model context protocol).
Superlog dey between the two. E owns the pipeline from beginning to end, from OTLP intake reach ClickHouse storage. E puts agent for triage step, not for fix step. Na this design make self-hosting am an infrastructure decision, not just a container wey you forget about. Once you run Superlog, you dey run a column store. E needs the same care like any other database wey you own.
FAQ
How much RAM self-hosted Superlog need?
Plan for 8 GB RAM, 4 vCPU and 40 GB disk for one node wey get low ingest volume. The stack na Postgres plus ClickHouse plus OpenTelemetry collector plus four Node processes, and ClickHouse need extra headroom. 1 GB or 2 GB VPS no go dey enough: pnpm install alone heavy, and kernel out of memory killer fit kill ClickHouse when load high. Measure your own numbers with docker stats --no-stream and free -m instead make you trust any published figure, including this one.
Which port I go point my OTLP exporter to?
Na Superlog intake proxy, wey README put for http://localhost:4101. E dey serve /v1/traces, /v1/logs and /v1/metrics, and e dey authenticate with your project's ingest key wey e take from x-api-key header or authorization: bearer header. Port 4318 na the OpenTelemetry collector underneath. If you export there directly, you go bypass the proxy. Na the proxy dey stamp your project id onto the data. The proxy fall back to port 4000 when PORT unset, so run ss -lntp and confirm wetin e bind to before you assume say na 4101.
Superlog fit replace Uptime Kuma or Zabbix?
No. Uptime Kuma answer whether endpoint dey respond from outside your network, while Zabbix dey monitor host and service metrics against thresholds wey you set. Superlog dey consume traces, logs and metrics wey your applications emit, then e group repeated failures into incidents. Keep external uptime probe alongside am, because probe wey dey run for another place still go report when na the box wey hold your telemetry pipeline be the thing wey crash.
Superlog agent fit change my production systems?
Only through permissions wey you grant am. The output na investigation and proposed change wey human go review. At first, keep GitHub App for read scopes with pull requests, and make any credentials wey worker hold dey limited to reading. Treat write access to production as separate decision wey you make deliberately, because agent wey fit restart services na much bigger commitment pass agent wey dey read telemetry and write patch for review.
I suppose pin a commit or track main?
Pin a commit. Repository no get release tags as of August 2026, so main na the only moving target wey dey available, and e dey take several commits every week. Record the SHA wey you test, deploy that one, and read the diff before you move forward. git log --oneline <old-sha>..main na the review, while the per-app .env.example files na the first place to check for newly required variables after any bump.