# otel-desktop-viewer keeps OpenTelemetry traces on the machine that made them

> otel-desktop-viewer is a Go tool that runs the OpenTelemetry Collector and a DuckDB store behind a Svelte interface, so traces, metrics and logs from local development land in a browser window instead of a hosted backend. It is Apache 2.0 licensed, installable five ways, and it also ships a query CLI for working with the same data from a terminal.

**CtrlSpice/otel-desktop-viewer** — otel-desktop-viewer is a CLI tool for receiving OpenTelemetry traces while working on your local machine.

- Repository: https://github.com/CtrlSpice/otel-desktop-viewer
- Stars: 1,299 · Forks: 56
- Language: Go
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/ctrlspice-otel-desktop-viewer

## Collector, DuckDB and Svelte in one local process

otel-desktop-viewer is described as a CLI tool for receiving OpenTelemetry traces while working on your local machine, and as a local OpenTelemetry viewer. The implementation is three well known pieces used for three different jobs.

The OpenTelemetry Collector is the ingest layer, so the tool speaks the standard OTLP protocols rather than a bespoke format, and the dependency list shows the full collector stack in use, including the otlpreceiver, the batch processor, the otlpjsonfilereceiver from the collector contrib package, the service and consumer packages, and the exporter helper. DuckDB is the store, through the duckdb-go v2 driver, which is what makes the local query surface possible. Svelte is the front end, built with Vite in a node stage and embedded into the Go binary.

The interface itself has three views, the traces view, the metrics view and the logs view, and there is documentation for chart palettes and an implementation section.

The audience is a developer working locally. The value proposition is that the telemetry never has to leave the machine, which is what makes the tool usable for work that cannot be sent to a shared backend, and what makes a local stack of several services visible in one window when the SDKs are pointed at localhost.

The licence is Apache 2.0, the repository is not archived, and the last push was on 2026-10-14, with releases v0.4.0, v0.4.1 and v0.5.0 landing between July and August 2026.

## Three ports, and they are a contract with your SDK

The local contract is three ports, and it is worth memorising because every SDK configuration in every service has to point at it. The user interface is at localhost:8000. The OTLP receivers listen on localhost:4317 for gRPC and localhost:4318 for HTTP.

Those are the standard OTLP ports, which is the point: an instrumented service that already exports to a collector can be pointed at the viewer with a host and port change and nothing else. The README covers configuring your OpenTelemetry SDK for this, and gives an example with otel-cli, so the fastest way to see the tool work is to send it a trace from the command line before wiring up a real service.

The Docker path uses the same numbers, mapping 8000, 4317 and 4318, and the Compose example in the README adds the viewer as a service alongside an app and exports OTLP to otel-desktop-viewer:4318 for HTTP or otel-desktop-viewer:4317 for gRPC:

```yaml
services:
  app:
    image: your-apps-image-tag
    # Add your app configuration here

  otel-desktop-viewer:
    image: ghcr.io/ctrlspice/otel-desktop-viewer:latest
    ports:
      - "8000:8000"
      - "4317:4317"
      - "4318:4318"
```

The container entrypoint makes the binding explicit, starting the binary with --host 0.0.0.0 and --grpc 4317, --http 4318 and --browser-port 8000, which is also a good hint about the command line flags that the tool understands.

## Storage is in memory unless you pass --db

Telemetry is stored in memory by default, and the --db flag persists it to a file. That default is the right one for a local development tool, since it means no state survives between runs and nothing has to be cleaned up, and it is also the reason the tool is not a telemetry backend.

The persistence target is a DuckDB database file, and the Makefile shows how the schema is handled: there is a refresh-macros target that reapplies macro DDL in dependency order to a DuckDB catalog, driven by an order file in the queries directory and applied through the duckdb command line binary. The target takes the path as a variable and refuses to run without it:

```bash
make refresh-macros DB=path/to.db
```

That detail is worth understanding if you plan to keep data around. The store is not just a table dump, it has a set of macros that define the queries the application runs, and a database file created by an older version may need those macros reapplied before it behaves as expected.

A local run with persistence is one command in the Makefile, run-go-persist, which starts the tool with --db duck.db, while run-go starts it with the browser port set to 8000 and nothing persisted.

## The CLI can query the same data the browser shows

The README has a section on querying a running viewer and another on searching telemetry from the command line, and the repository layout shows how much of the project is behind those two headings. There is a separate Go file for each surface: query_command.go, span_command.go, trace_command.go, logs_command.go, metrics_command.go, metric_command.go, services_command.go, attributes_command.go, export_command.go, log_command.go and skills_command.go, with telemetry_search.go holding the search logic and rpc_client.go the client side of the connection to a running viewer.

The separation between a running viewer and a query client is the design point. The viewer holds the data in its process, and a second command connects to it, which means the same data is reachable from a terminal while a browser session is open, and that search results and exports do not need the user interface to be driving.

The skills_command.go and skills/ directory point at an agent usage skill, which is an unusual thing to find in a telemetry tool and suggests the project is designed to be driven by an automated assistant as well as a person. The manpages/ directory confirms that the command line is meant to be documented properly rather than discovered by trial and error.

For the source tree, the tests are similarly split by surface, with a test file next to most command file, including integration tests for attributes and for HTTP import, and testdata/ holding the fixtures.

## Five install routes, and one of them has a floor

The install options are unusually thorough, and the differences between them matter.

On macOS the Homebrew cask route is two commands:

```bash
brew tap ctrlspice/otel-desktop-viewer
brew install --cask otel-desktop-viewer
```

Prebuilt binaries come from GitHub Releases, with archives for macOS on Apple Silicon and Intel, Linux on x86_64 and arm64, and Windows on x86_64 where you unzip and run otel-desktop-viewer.exe. The Linux warning is specific and easy to miss: the release binaries require glibc 2.39 or newer, Ubuntu 24.04, Debian 13 or Fedora 40 and later, and this applies to the tarballs, the deb and rpm packages and the Docker images. On an older distribution the binary simply will not start.

For Debian and Ubuntu and for Fedora and RHEL, stable releases include deb and rpm packages on GemFury, installed by adding the repository and its GPG key, and for Fedora the repository file sets gpgcheck=0.

The go install route requires Go and CGO, and the README is upfront about the consequences: CGO_ENABLED must print 1, and a C compiler such as gcc must be present. On Windows that means installing MSYS2 and using the UCRT64 terminal, with the exact pacman packages listed. The README also notes that @latest resolves to the newest stable tag on the Go module proxy rather than an alpha or beta, and suggests pinning a version such as @v0.3.0 or using a release binary to avoid compiling locally.

## Docker is the shortest path, and the image is built in three stages

The container route needs no local Go installation:

```bash
docker pull ghcr.io/ctrlspice/otel-desktop-viewer:latest
docker run -p 8000:8000 -p 4317:4317 -p 4318:4318 ghcr.io/ctrlspice/otel-desktop-viewer:latest
```

Pinning a version or a per architecture tag is also supported, with tags such as v0.3.0, latest-amd64 and latest-arm64, and a local build is documented with docker build followed by the same run command.

The Dockerfile shows how the pieces fit. The first stage is node:24-bookworm, where npm ci installs the front end dependencies and npm run build produces the Svelte assets. The second is golang:1.26, which installs gcc, g++, git, ca-certificates and libstdc++6, downloads the Go modules, copies the built front end into the server's static directory and compiles the binary. The final stage is debian:13, and the comment there explains why it is not a slim image: duckdb-go requires libraries that slim images do not have, so the runtime copies libstdc++ from the build stage along with the CA certificates. The image exposes 8000, 4317 and 4318 and sets the container metadata, including the Apache-2.0 licence label.

For local development of the front end, the Makefile dev-ts target starts the Vite dev server and points at http://localhost:3001, and the install target installs the npm dependencies and downloads a Chromium build for Playwright, which is what the front end's own tests run against.

## Building from source needs the browser and the database driver

The Makefile is the whole contributor workflow, and it is worth reading before cloning. The install target installs the front end dependencies with npm install and then runs npx playwright install chromium, so a contributor needs Node, npm and a Chromium download even to work on the Go side, because the front end tests depend on that browser.

The Go targets are conventional: build-go compiles the binary, test-go runs go test ./..., format-go runs gofmt and format-go-check fails when files are unformatted, and run-go starts the app with the browser port set. The populate targets are for exercising the tool without a real application. They read an OTLP_DATASET directory, defaulting to testdata/otlp/demo, and post JSON files to an OTLP_ENDPOINT, defaulting to http://localhost:4318, with one target each for traces, logs and metrics.

That gives a fast path to seeing the UI populated. The repository carries the demo data, and the populate targets push it at the receiver, so you can start the viewer, send the fixtures, and look at the traces, metrics and logs views without instrumenting anything.

The Go module targets Go 1.26.0 with toolchain go1.26.5, and the dependency set is the collector stack plus duckdb-go, cobra for the command tree, the uuid package, rs/cors for the browser API, pkg/browser for opening the interface, and testify for tests. The collector and the query surface being the heavy part of the dependency list is the honest summary of what this tool is: a collector with a storage engine and a UI attached.

## Conclusion

otel-desktop-viewer suits a developer who instruments services locally and wants spans, metrics and logs in a single local UI without standing up a hosted tracing backend, and who is comfortable pointing an SDK at localhost. It does not suit a team that needs durable storage, multi-user access or a shared backend, because the default store is in memory and the persistence path is a DuckDB file. Verify first the glibc version on your Linux hosts, since the release tarballs, the deb and rpm packages and the images all require glibc 2.39 or newer, and check that the CGO toolchain is present before you choose the go install route.

## FAQ

### What is otel-desktop-viewer and what is it built on?

It is a local OpenTelemetry viewer for traces, metrics and logs, built on the OpenTelemetry Collector, DuckDB and a Svelte front end, with a Go binary that ties them together.

### Which ports does otel-desktop-viewer use?

The user interface is at localhost:8000, the OTLP receivers listen on localhost:4317 for gRPC and localhost:4318 for HTTP. The container exposes the same three ports and starts the binary with --host, --grpc, --http and --browser-port flags.

### Is telemetry persisted by default?

No. Telemetry is stored in memory by default, and the --db flag persists it to a file, which the Makefile run-go-persist target does with --db duck.db. The store is DuckDB.

### What are the system requirements for the Linux release binaries?

The release binaries require glibc 2.39 or newer, meaning Ubuntu 24.04, Debian 13 or Fedora 40 and later. The requirement applies to the tarballs, the deb and rpm packages and the Docker images.

### Can I install otel-desktop-viewer with go install?

Yes, with go install github.com/CtrlSpice/otel-desktop-viewer@latest, but building from source requires Go and CGO, so a C compiler is needed and on Windows that means MSYS2 with the UCRT64 toolchain. The README notes that @latest resolves to the newest stable tag, and suggests pinning a version or using a release binary instead.

## Sources

- [CtrlSpice/otel-desktop-viewer on GitHub](https://github.com/CtrlSpice/otel-desktop-viewer)
- [Issues](https://github.com/CtrlSpice/otel-desktop-viewer/issues)
- [License: Apache-2.0](https://github.com/CtrlSpice/otel-desktop-viewer/blob/main/LICENSE)
- [README](https://github.com/CtrlSpice/otel-desktop-viewer/blob/main/README.md)
- [Releases](https://github.com/CtrlSpice/otel-desktop-viewer/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ctrlspice-otel-desktop-viewer
