Project

Telelogs

Oct 05, 2026 RustGPUIgRPCKubernetesDocker

A self-hosted log aggregator: tail Docker and Kubernetes, read it all in a native desktop app, keep every line in a bucket you own.

Source

I didn’t want another place my logs live. I wanted a nicer way to read the place they already live.

What it is

Telelogs is an open-source log aggregator. A small server tails your Docker containers and Kubernetes pods, a native desktop app (built with GPUI) shows them as one live stream, and every line gets archived to S3, R2, MinIO, GCS or a local folder.

It’s at v0.2.0. Docker and Kubernetes work, VMs are next, and the app is macOS only for now.

docker run -d --name telelog-server \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -p 127.0.0.1:7070:7070 \
  ghcr.io/tolaniverse/telelog-server --allow-unauthenticated

Architecture at a glance

Docker socket ─┐
               ├─► telelog-sources ─► telelog-server ─┬─► gRPC ─► telelog-app (GPUI)
Kubernetes API ┘                       │ memory buffer │
                                       ▼               │
                              s3 / r2 / gcs / file ◄───┘ Query reads past the buffer
CrateRole
telelog-coreThe shared log model: LogRecord, Target, Filter
telelog-protoThe gRPC API and conversions
telelog-sourcesDocker (bollard) and Kubernetes (kube-rs)
telelog-serverCollects, buffers, archives and serves logs
telelog-appThe desktop client

The parts I care about

  • One model for a log line. Docker, Kubernetes and later VMs all become the same LogRecord, so the server and app never care where a line came from.
  • The bucket is just files. Lines are zstd-compressed JSON under <prefix>/v1/<YYYY-MM-DD>/<HH>/, each file named after the time range it covers. Finding a time range means listing a folder, no index or database.
  • No gaps after a restart. A _checkpoint.json records the newest archived line, and a restarted server picks up from there with no gap and no duplicates.
  • Live and archived don’t double up. A query tells the server, per source, which lines the app already holds live, so merging the two never shows a line twice. That’s also how logs from removed containers come back.
  • Safe by default. Open on localhost, and it refuses to listen anywhere else without a token.

Tested against a real AKS cluster (4 nodes, 99 containers): ready in about 3 seconds, around 40 MB of memory while streaming.

Known limits

  • A bucket query returns at most the newest 20,000 matching lines.
  • The macOS app isn’t notarized yet, so the first launch needs Open Anyway in System Settings.

I wrote the full walkthrough, with an animated diagram, in How Telelogs works.

Source on GitHub →