Self-hosted service
trickstercache/trickster avatar
trickstercache/trickster

Trickster: delta caching for time series, plus a reverse proxy

Open Source HTTP Reverse Proxy Cache and Time Series DB Query Accelerator

2,092 stars191 forksGoApache-2.0

At a glance

What is it?
Trickster is a Go binary that sits in front of time series databases and HTTP applications, answering dashboards from cached buckets and asking the backend only for the samples it does not hold. It doubles as a reverse proxy, an ingress controller and a gateway controller, and it pays for that range with a dependency list that reaches into ten database clients.
Who is it for?
Adopt Trickster when dashboards re-request the same time ranges on every refresh and a Go operator can own a stateful proxy standing in front of the database. Skip it when queries are narrow or unique, when the database already absorbs the load, or when one more cached hop is more than your on-call rota can carry.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • trickstercache/trickster2,092 ★ · +1
Star history
Repository2026-10-142026-10-23
trickstercache/trickster2.1k2.1k

1 Stars in the last 9 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One binary for dashboard deltas and plain HTTP caching

Trickster is described at the repository root as an HTTP reverse proxy cache and a time series database query accelerator, and its README splits the documentation into exactly those two halves. Behind the first sits a caching proxy for static file servers and REST APIs. Behind the second sits something that answers dashboards backed by Prometheus, ClickHouse, InfluxDB, VictoriaMetrics, Druid, Graphite, TimescaleDB/PostgreSQL, GreptimeDB, QuestDB or MySQL.

Two details in the build files matter before anything runs. Import paths carry the major version, so the module name is `github.com/trickstercache/trickster/v2` rather than the bare repository name, and the container image is published under a different organisation, `tricksterio/trickster` on Docker Hub, not under the GitHub owner. Repository topics also name kubernetes-ingress-controller and kubernetes-gateway-controller, which lines up with the claim that Trickster can serve as an Ingress and/or Gateway controller rather than being deployed behind one.

The project is hosted by the Cloud Native Computing Foundation as a sandbox level project, and the repository is not archived, with the last push on 2026-10-04.

The delta proxy requests the gap, not the whole range

Dashboards are wasteful by default, and the README says why without hedging: most of them ask a time series database for the entire time range they intend to display, and they do it again on every page load and on every auto-refresh. Trickster's Delta Proxy reads the time range of the incoming query, works out which data points are already cached, and forwards only the samples still needed to service that request. What reaches the database is an incremental slice rather than several hundred duplicate points, and the chart is assembled from the cached part plus the fresh part.

This is the whole acceleration story, and it carries a precondition worth stating plainly. It pays off only when consecutive requests overlap. A dashboard that scrolls through disjoint historical windows, or a job that asks for a range nobody has ever requested, gets nothing from the delta logic, because there is nothing cached to serve the first request from. The HTTP half has no such precondition, since a response cache helps whenever the same path is fetched twice. That difference is why the two halves of the product tolerate very different traffic, and why one deployment can be sensible for the time series side and pointless for the web side.

Whole buckets only, and a rename the version list does not show

Time series databases group samples into fixed steps, so a range beginning or ending between two grid points leaves the bucket at that edge holding part of its data. Trickster caches only complete buckets, which keeps overlapping requests fast and keeps every viewer on identical values. What a client sees at those partial edges comes from each backend's own step alignment mode rather than from Trickster, which means the edge behaviour is a database-specific detail you inherit when you pick a backend.

A close reading of the same paragraph turns up a naming problem. Other tools call this aligning queries with their step, and the same paragraph credits Trickster versions before 2.2 with an older term that appears there only in part, ending at Step Boundary Nor. The newest published version listed for the project is v2.1.0, dated 2026-09-21, with v2.0.5 and v2.0.4 behind it. A rename that the documentation dates to versions before 2.2 therefore describes a line with no published entry in that list, so the term you meet in older tickets and blog posts is not the one to search for now.

The distroless image ships the full example configuration

Building the container exposes the first real decision. A build argument named `BIN_SOURCE` picks between a stage that compiles inside the image and a scratch stage that copies an already built artifact from `dist/linux-${TARGETARCH}`. Compilation disables CGO and produces two files, the binary and a generated `third-party-licenses` file:

dockerfile
RUN GOOS=linux GOARCH=${TARGETARCH} CGO_ENABLED=0 BUILD_FLAGS=-v make build third-party-licenses \
    && mkdir /out && mv bin/trickster bin/third-party-licenses /out/

The final stage is `gcr.io/distroless/static-debian12`. It receives a CA bundle from a separate alpine stage, the binary, the generated dependency file, and `examples/conf/example.full.yaml` copied to `/etc/trickster/trickster.yaml`. It runs as `nobody` with a single entrypoint and nothing else layered in, which means the configuration you actually run is whatever you mount over that path. The Makefile names `cmd/trickster` as the main package and stamps build information from `git describe --tags --dirty --always`, so a binary built outside a container still records its origin.

One wrinkle sits in those two files. The Makefile comments that `go tool` compiles for the target GOOS and GOARCH, so a cross-build must point `GO_LICENSES` at a host-built binary, while the container build runs that same target with `GOARCH=${TARGETARCH}` already set.

Cache backends and access rules are per-path YAML

`docs/caches.md` lists four storage choices for cached objects: in memory, on the filesystem, in Redis, or in bbolt. The module file agrees with that list rather than describing something else. `github.com/dgraph-io/ristretto/v2` and `github.com/dgraph-io/badger/v4` cover the in-memory side, `github.com/redis/go-redis/v9` speaks to Redis, and `go.etcd.io/bbolt` is the file-backed store. `github.com/alicebob/miniredis/v2` sits in the same file, and it is an in-process Redis for tests, which tells you the suite runs without standing up a Redis server.

Around that core the README attaches a long list of HTTP behaviour, each item linked to its own page: negative caching, collapsed forwarding, byte range request acceleration, OpenTelemetry tracing over OTLP, per-backend access and error logs whose formats are configurable, and IP and geo ACLs that apply to HTTP, native database and stream listeners alike. `docs/paths.md` is the reason tuning reaches as far as individual HTTP paths, and nothing in the files shown reads tuning from environment variables. The knob surface is YAML.

One line of that list deserves a second look. Pool autodiscovery is described as drawing from Kubernetes, AWS, GPC, Azure, Consul, Nomad and DNS. The listed dependencies back the DNS part with Cloudflare, RFC 2136 and Route 53 resolvers, and none of the ones shown is a GPC resolver, so treat that entry as a spelling the dependencies do not confirm.

Each supported database drags its own parser into the binary

To decide which samples it already holds, Trickster has to read the query, which means the process links a parser for every dialect it advertises. `go.mod` shows how wide that becomes: `github.com/ClickHouse/clickhouse-go/v2` and `github.com/ClickHouse/ch-go` for ClickHouse, `github.com/influxdata/influxql` and `github.com/influxdata/influxdb` for InfluxDB, `github.com/VictoriaMetrics/metricsql` for VictoriaMetrics, `github.com/cockroachdb/cockroachdb-parser` with `github.com/jackc/pgx/v5` for TimescaleDB and PostgreSQL, and `github.com/prometheus/prometheus` for Prometheus.

That same list explains why this is heavier than a plain response cache. Two ClickHouse libraries ship together, `github.com/ClickHouse/ch-go` and a columnar Arrow reader, next to `github.com/quic-go/quic-go` for HTTP/3 and `github.com/caddyserver/certmagic` for automatic certificates. None of that is wasted in isolation, but the unit you operate is one process serving two very different workloads, and a team that only needs HTTP caching still deploys all of it.

The boundary is the dialect itself. A query Trickster cannot parse is not accelerated no matter which cache backend you configure, because the delta logic needs a time range it can extract first. The compatibility table names ten databases, so anything fronting an eleventh runs the reverse proxy without the accelerator.

Cache invalidation is the part the overview leaves out

Two behaviours are easy to accept and easy to miss. The first is that caching stops at complete buckets, so a window beginning mid-step re-reads its edge bucket on each refresh instead of serving it from cache. The second is negative caching, which the README frames as a way to prevent domino effect outages: failed responses get remembered so a sick backend does not absorb every request. Both are sensible for dashboards, and both are stale by construction.

What the README does not document is any way to purge or invalidate an object on demand, for either half of the product. Every capability in the feature list links out to its own page, so freshness, revalidation and eviction are defined in `./docs/caches.md` and neighbouring documents rather than in the overview. If your backends return different values for an unchanged time range, those pages need reading before deployment.

The operational cost follows from the shape. Trickster holds a second copy of recent data, so the deployment needs a cache backend sized for it plus health and Prometheus metrics endpoints to watch, and every dashboard now depends on the proxy being up. That is two new things to alert on, for a saving that only exists when requests repeat.

Querying the database directly versus routing dashboards through a hop

The real alternative is putting nothing in front of the database and letting each dashboard compute its own range, which is where most teams begin. The difference in approach is exactly the redundant work described earlier: with no delta proxy, every auto-refresh repeats the same full-range scan and the database pays for it each time. Trickster moves that cost to the first request of a window and serves the rest from cache, in exchange for one more network hop and one more stateful component in the path.

For the HTTP half, the comparison is against whatever already fronts your static files and REST endpoints. Where that is a Kubernetes ingress controller, the README's claim about acting as an Ingress and/or Gateway controller becomes the deciding point, and the differentiators to check are the capabilities with dedicated pages: negative caching, collapsed forwarding, byte range request caching. Whether they matter depends on your traffic, and the overview puts no numbers against any of them.

Neither path is free, and the exchange is visible at the query level. Measure the database first. Where a dashboard's cost is already low, the extra hop buys little and turns an accelerator into another thing that can fail during an incident.

Editorial conclusion

Adopt Trickster when dashboards re-request the same time ranges on every refresh and a Go operator can own a stateful proxy standing in front of the database. Skip it when queries are narrow or unique, when the database already absorbs the load, or when one more cached hop is more than your on-call rota can carry. Before deploying, decide which of the four cache backends the deployment uses, check the step alignment mode each backend expects, and make sure the mounted configuration is not the example file the image ships by default.

Frequently asked questions

Which time series databases can Trickster accelerate?

Ten are listed: Prometheus, ClickHouse, InfluxDB, VictoriaMetrics, Apache Druid, Graphite, TimescaleDB/PostgreSQL, GreptimeDB, QuestDB and MySQL. The README states that Trickster works with virtually any dashboard application that queries those databases.

What storage can Trickster use for its cache?

Four options are documented: in-memory, filesystem, Redis and bbolt. The module file carries ristretto and badger for the in-memory side, go-redis for Redis, and go.etcd.io/bbolt for the file-backed store.

Does Trickster cache the data points a dashboard is still waiting on?

Not the incomplete ones. Trickster caches only complete time buckets, so a window that starts or ends between grid points re-reads its edge bucket, and each backend's step alignment mode decides what a client sees at those partial edges.

Can Trickster act as a Kubernetes ingress controller?

It can. The README says Trickster can serve as a Kubernetes Ingress and/or Gateway controller, with pool autodiscovery from Kubernetes, AWS, Azure, Consul, Nomad and DNS, and it carries pages for negative caching, collapsed forwarding and byte range request caching.

How do I invalidate a cached value in Trickster?

The README does not document cache invalidation or purge for either the HTTP side or the time series side. Cache behaviour is left to the settings described in ./docs/caches.md, which the overview links rather than restates.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. trickstercache/trickster on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/trickstercache-trickster.svg)](https://hysenlabs.com/projects/trickstercache-trickster)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/trickstercache-trickster.svg)](https://hysenlabs.com/projects/trickstercache-trickster)

Was this useful?