Trickster
Overview
This guide covers deploying and using Trickster 2.0 on Ubuntu 24.04 on Azure from the SecureAnyCloud Azure Marketplace image.
Trickster is an open-source HTTP reverse proxy cache and time series dashboard accelerator. It sits between your dashboards and your time series database, understands the query language of backends such as Prometheus, InfluxDB and ClickHouse, and caches the results of the range queries that dashboards issue on every refresh.
When a panel reloads, Trickster serves the part of the time range it already holds from cache and asks the database only for the newly elapsed slice. It also collapses concurrent identical requests into a single origin fetch, so a wall of dashboards opening at once does not stampede the backend.
A Prometheus is bundled on this VM and wired up as the default origin. Trickster is a caching proxy, so on its own it has nothing to accelerate. This image ships Prometheus 3.13.2 alongside it, listening on the loopback interface and already scraping both itself and Trickster. The server answers real queries with real data as soon as first boot finishes. You do not have to keep it: repointing Trickster at your own Prometheus, InfluxDB or ClickHouse takes a single line of configuration and a reload, covered later in this guide.
Trickster has no web interface. It is a proxy: HTTP requests in, cached responses out. You work with it using curl and standard HTTP clients, or by pointing Grafana at it as an ordinary Prometheus data source. The bundled Prometheus has its own web UI, and this guide shows how to reach it safely over an SSH tunnel.
Security by design. A caching proxy in front of a metrics store is a data exposure risk, so this image is locked down by default:
- Exactly one port is public, and it is authenticated. TCP
8480carries the accelerated query API. Trickster's own HTTP Basic authenticator protects every backend route on it, using a bcrypt credential generated uniquely on your VM at first boot. An unauthenticated request, or one with the wrong password, is refused with HTTP 401. - The only unauthenticated path is the liveness probe.
GET /trickster/pingreturns the four-character responsepongand nothing else, so an Azure Load Balancer or Application Gateway health probe works without a credential. It discloses no version, hostname, configuration or data. - Everything else is bound to the loopback interface. The Trickster metrics endpoint on
8481, the management endpoint on8484that prints the running configuration, and the bundled Prometheus on9090(including its web UI) are reachable only from the VM itself or through an SSH tunnel. - Nothing is baked into the image. The captured image contains no configuration file and no credential. Trickster cannot start until first boot has generated your credential, so the proxy never runs unprotected and no password is shared between customers.
What you get:
- Trickster 2.0.5 from the official upstream release, verified against a pinned SHA 256 checksum at build time, running under systemd as
trickster.service - Prometheus 3.13.2 from the official upstream release, also SHA 256 verified, running as
prometheus.serviceand bound to the loopback interface as the default origin - A 512 MB in-memory result cache, and Trickster's Delta Proxy Cache engine, which fetches only the newly elapsed slice of a repeated range query
- A per-VM HTTP Basic credential generated on first boot and written to a root-only credentials file
trickster-selfcheck, a bundled command that verifies the whole appliance end to end, including that the proxy is serving from cache- The upstream Trickster example configurations and documentation under
/usr/share/doc/trickster/ - Unattended security upgrades left enabled so the appliance keeps receiving patches
Prerequisites
- An active Azure subscription, an SSH public key, and a VNet and subnet in the target region
- A subscription to this listing on Azure Marketplace
- A network security group that allows TCP 22 for administration and TCP 8480 for the accelerated query API. In production, restrict
8480to the subnet your dashboards run in. No other port needs to be open, because every other service on this appliance is bound to the loopback interface. - Recommended virtual machine size:
Standard_B2swith 2 vCPU and 4 GB RAM suits development and light dashboard workloads. For higher query rates or a larger cache working set, chooseStandard_D2s_v5or above.
Deploy the virtual machine
To deploy from the Azure portal, select the image from Azure Marketplace, choose your VM size, and supply your SSH public key for the azureuser account. Alternatively, deploy from the Azure CLI.
Run these commands on your own workstation, not on the VM:
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:trickster-ubuntu-24-04:default:latest
az vm create \
--resource-group my-resource-group \
--name my-trickster-vm \
--image sac1647283583153:trickster-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group my-resource-group --name my-trickster-vm --port 8480Connect over SSH once the VM is running:
ssh azureuser@<vm-ip>Confirm the services are healthy
Both services start automatically on boot. Check that they are active and confirm the versions:
systemctl is-active prometheus trickster trickster --version prometheus --version 2>&1 | head -1Expected output
active active Trickster version: 2.0.5 (linux/amd64), buildInfo: 2026-07-31T17:10:27+0000 43f88baa43118d9c07991b857d06511ed919004c, goVersion: go1.26.5, copyright: © 2018 The Trickster Authors prometheus, version 3.13.2 (branch: HEAD, revision: bb5dff00cf8fdfbf5c65e0531aa835fa238a43a2)Next, confirm the network posture. Exactly one port is bound to all interfaces. The other three are bound to the loopback address only:
ss -tln | grep -E ':(8480|8481|8484|9090) 'Expected output
LISTEN 0 4096 127.0.0.1:8481 0.0.0.0:* LISTEN 0 4096 127.0.0.1:8484 0.0.0.0:* LISTEN 0 4096 127.0.0.1:9090 0.0.0.0:* LISTEN 0 4096 *:8480 *:*This is the intended result. 8480 is reachable from your dashboards, and the metrics endpoint, the configuration endpoint and Prometheus itself are unreachable from outside the VM.
Retrieve your per VM credentials
First boot generates the HTTP Basic credential for this VM and writes it to a root-only file. Read the file first, because this is the only place the password appears in plain text:
sudo cat /root/trickster-credentials.txtThe file records the accelerated query URL, the username and password, the loopback-only endpoints, and copy-ready example commands.
Verify the whole appliance end to end at any time with the bundled self check. It proves that unauthenticated and wrong-password requests are refused, that your credential is accepted, and that the local origin returns real data. It also proves that a repeated range query is served from cache and that the metrics, management and Prometheus ports refuse connections on the VM's own network address:
sudo trickster-selfcheckExpected output
Trickster appliance self-check [ok] liveness: /trickster/ping returns pong [ok] unauthenticated query -> HTTP 401 (refused) [ok] wrong password -> HTTP 401 (refused) [ok] authenticated instant query -> 2 live series from the local Prometheus [ok] range query -> 30 data points returned [ok] repeat range query served from cache -> X-Trickster-Result: engine=DeltaProxyCache; status=hit; ffstatus=off [ok] port 8481: reachable on 127.0.0.1, refused on 10.0.0.10 (loopback only) [ok] port 8484: reachable on 127.0.0.1, refused on 10.0.0.10 (loopback only) [ok] port 9090: reachable on 127.0.0.1, refused on 10.0.0.10 (loopback only) SELFCHECK OK — appliance authenticated, locked down and accelerating.The address shown on the last three lines is your own VM's private address, so it will differ from the example above.
The liveness probe is the only unauthenticated path
Health probes must work without a credential, so /trickster/ping is deliberately left open. It returns four characters and reveals nothing else:
curl -s http://localhost:8480/trickster/pingExpected output
pongPoint your Azure Load Balancer or Application Gateway health probe at this path on port 8480.
Everything else requires the credential
Request data from the accelerated API without a credential, and then with the wrong password. Both requests are refused:
curl -s -o /dev/null -w 'no credential: HTTP %{http_code}\n' 'http://localhost:8480/api/v1/query?query=up'
curl -s -o /dev/null -w 'wrong password: HTTP %{http_code}\n' -u 'sac:WRONG' 'http://localhost:8480/api/v1/query?query=up'Expected output
no credential: HTTP 401 wrong password: HTTP 401Query through the accelerator
Read the credential straight from the root-only file so you never paste a password by hand. Trickster is API-compatible with Prometheus, so the familiar query endpoints work unchanged:
TPASS=$(sudo grep '^trickster.password=' /root/trickster-credentials.txt | cut -d= -f2-)
curl -s -u "sac:$TPASS" 'http://localhost:8480/api/v1/query?query=up' | jq -r '.data.result[] | "\(.metric.job) \(.metric.instance) = \(.value[1])"'Expected output
prometheus 127.0.0.1:9090 = 1 trickster 127.0.0.1:8481 = 1Those two series come from the bundled Prometheus, which scrapes itself and Trickster. Both report 1, so both targets are up and the whole chain from your client, through Trickster, to the origin is working.
Watch the cache actually accelerate
This is what Trickster is for. Issue a range query, then issue exactly the same query again, and compare the X-Trickster-Result response header:
TPASS=$(sudo grep '^trickster.password=' /root/trickster-credentials.txt | cut -d= -f2-)
END=$(date -u +%s);
START=$(( END - 900 )) RANGE="http://localhost:8480/api/v1/query_range?query=up&start=${START}&end=${END}&step=60" echo "first request:" curl -s -D- -o /dev/null -u "sac:$TPASS" "$RANGE" | grep -i '^x-trickster-result' | tr -d '\r' echo "same request again:" curl -s -D- -o /dev/null -u "sac:$TPASS" "$RANGE" | grep -i '^x-trickster-result' | tr -d '\r'Expected output
first request: X-Trickster-Result: engine=DeltaProxyCache; status=kmiss; ffstatus=hit same request again: X-Trickster-Result: engine=DeltaProxyCache; status=hit; ffstatus=hitThe first request reports kmiss: the result was not cached and was fetched from the origin. The second reports hit: Trickster served it entirely from cache and never asked the origin.
If you have already run a similar query on this VM, the first request reports phit instead of kmiss. That is a partial hit, and it is the case that matters most in production. As time moves forward, Trickster serves everything it already holds from cache and fetches only the newly elapsed seconds. This is where most of the saving on a live dashboard comes from. A phit line also carries a fetched=[...] field naming the exact window it had to fetch from the origin.
The response body is identical either way, so the data your dashboard receives is unchanged:
TPASS=$(sudo grep '^trickster.password=' /root/trickster-credentials.txt | cut -d= -f2-)
END=$(date -u +%s);
START=$(( END - 900 )) curl -s -u "sac:$TPASS" "http://localhost:8480/api/v1/query_range?query=up&start=${START}&end=${END}&step=60" | jq '.data.result[0].values | length'Expected output
5The number of data points depends on how long your VM has been running and on the step you request, so your instance may show a different figure.
Connect Grafana
Trickster speaks the Prometheus HTTP API, so Grafana treats it as an ordinary Prometheus data source. In Grafana, add a Prometheus data source and set:
Field Value
URL http://<vm-ip>:8480
Authentication Basic authentication
User the trickster.username value from your credentials file
Password the trickster.password value from your credentials file
Save and test. Every panel query now flows through the accelerator, and repeat refreshes of the same dashboard panel are served from cache instead of querying the backend again.
Watch the cache statistics
Trickster publishes its own Prometheus-format metrics on the loopback interface, and the bundled Prometheus already scrapes them. Read them directly on the VM:
curl -s http://127.0.0.1:8481/metrics | grep -E '^trickster_cache_operation_objects_total' | head -4Expected output
trickster_cache_operation_objects_total{cache_name="default",operation="get",provider="memory",status="hit"} 7 trickster_cache_operation_objects_total{cache_name="default",operation="setDirect",provider="memory",status="none"} 3The exact counters depend on how many queries your instance has served. Because the bundled Prometheus scrapes these metrics, you can also graph them through the accelerator itself, so the appliance monitors itself.
Reach the bundled Prometheus web UI
Prometheus is bound to the loopback interface on purpose. It has no authentication of its own, so exposing it would publish your metrics to anyone who finds the port. Reach its web UI with an SSH tunnel instead.
Run this on your own workstation, not on the VM:
ssh -L 9090:127.0.0.1:9090 azureuser@<vm-ip>Leave that session open and browse to http://127.0.0.1:9090/ on your workstation. You get the full Prometheus UI, including the expression browser, the targets page and the alerting views, without exposing any port.
Confirm on the VM that both scrape targets are healthy:
curl -s http://127.0.0.1:9090/api/v1/targets | jq -r '.data.activeTargets[] | "\(.labels.job) \(.health)"'Expected output
prometheus up trickster upAccelerate your own upstream instead
The bundled Prometheus makes the appliance useful and verifiable the moment it boots. To put Trickster in front of a database you already run, edit the backend section of the configuration:
sudo nano /etc/trickster/trickster.yamlChange the origin_url under backends: default: to your own endpoint. To accelerate a different kind of backend, change provider as well. Trickster supports prometheus, influxdb, clickhouse, reverseproxycache and reverseproxy, among others. For example:
backends: default: provider: prometheus origin_url: http://my-prometheus.internal:9090 listener_name: default is_default: true authenticator_name: sac_basic cache_name: defaultLeave authenticator_name in place so your per-VM credential keeps protecting the accelerated port. Apply the change without dropping in-flight connections:
sudo systemctl reload tricksterOnce Trickster points at your own backend, you can stop the bundled Prometheus to reclaim its memory and disk:
sudo systemctl disable --now prometheusTrickster keeps running, because the bundled Prometheus is only its default origin, not a dependency of the proxy itself. The upstream example configurations for other backend types are under /usr/share/doc/trickster/conf/, and the full upstream documentation is under /usr/share/doc/trickster/docs/.
Review the running configuration
The management endpoint prints the exact configuration Trickster is running, which is useful after an edit. It is bound to the loopback interface because the printout includes your origin addresses:
curl -s http://127.0.0.1:8484/trickster/config | head -20Confirm from the VM that the same endpoint is unreachable on the VM's own network address:
HOST_IP=$(hostname -I | awk '{print $1}')
curl -s -o /dev/null -m 5 -w 'off box: HTTP %{http_code}\n' "http://${HOST_IP}:8484/trickster/config" || echo 'off box: connection refused'Expected output
off box: HTTP 000000 means curl could not establish a connection at all. This is the correct result.
Server components
Component Version Purpose
Trickster 2.0.5 HTTP reverse proxy cache and time series dashboard accelerator
Prometheus 3.13.2 Bundled local time series database, the default origin
Ubuntu Server 24.04 LTS Base operating system
Filesystem layout
Path Size Purpose
/ 29 GB Root filesystem
/boot 2 GB Operating system kernel files
/boot/efi 100 MB UEFI boot partition (Gen2 Hyper V)
/mnt varies Azure temporary resource disk
Key directories
Path Purpose
/usr/local/bin/trickster The Trickster binary
/etc/trickster/trickster.yaml Trickster configuration, generated on first boot
/etc/trickster/users.htpasswd Your per VM bcrypt credential
/usr/local/bin/prometheus The Prometheus binary
/etc/prometheus/prometheus.yml Prometheus scrape configuration
/var/lib/prometheus/data Prometheus time series database
/usr/share/doc/trickster/ Upstream license, README, example configs and documentation
/root/trickster-credentials.txt Per VM credentials, root only
Network ports
Port Bound to Authentication Purpose
22 all interfaces SSH key Administration
8480 all interfaces HTTP Basic, per VM credential Accelerated query API. /trickster/ping is the only unauthenticated path
8481 loopback only none needed Trickster metrics in Prometheus format
8484 loopback only none needed Management endpoint and running configuration printout
9090 loopback only none needed Bundled Prometheus, including its web UI
Managing the services
systemctl status trickster --no-pager
systemctl status prometheus --no-pagerReload Trickster after a configuration change without dropping connections, or restart it outright:
sudo systemctl reload tricksterRead the logs:
sudo journalctl -u trickster -n 30 --no-pagerScripts and log files
Path Purpose
/usr/local/sbin/trickster-firstboot.sh Generates the per VM credential and configuration on first boot
/usr/local/sbin/trickster-selfcheck End to end verification of the appliance
/var/log/sac/sac-firstboot.log First boot log
/stage/scripts/BUILD_VERSIONS Pinned upstream versions and checksums used at build time
/var/lib/sac/data/trickster-firstboot.done First boot completion sentinel
/var/lib/sac/data/trickster-ready Marker that permits trickster.service to start
On startup
The first time the VM boots, trickster-firstboot.service runs once. It waits for the bundled Prometheus to become ready and generates a unique HTTP Basic password. It then writes the password as a bcrypt hash to /etc/trickster/users.htpasswd, writes /etc/trickster/trickster.yaml, creates the trickster-ready marker, starts trickster.service, records everything in /root/trickster-credentials.txt, and disables itself.
trickster.service has a ConditionPathExists on the trickster-ready marker, so it cannot start before that credential exists. The captured image ships without the marker, a configuration file or a credential, so the accelerated port is never served unprotected. On every later boot the marker is present and both services start normally.
Confirm first boot completed:
ls -l /var/lib/sac/data/
sudo tail -5 /var/log/sac/sac-firstboot.logTroubleshooting
Every request returns HTTP 401, including with the password from the credentials file. Confirm you are sending the username as well as the password, and that you copied the value after the = with no trailing whitespace. Read it programmatically to rule out a copy error:
sudo grep -E '^trickster\.(username|password)=' /root/trickster-credentials.txttrickster.service is inactive and will not start. The service is gated on the first-boot marker. Check whether the marker exists:
ls -l /var/lib/sac/data/trickster-ready systemctl status trickster --no-pagerIf the marker is missing, first boot did not complete. Read /var/log/sac/sac-firstboot.log for the reason, then run it again:
sudo /usr/local/sbin/trickster-firstboot.shQueries return an empty result set. The bundled Prometheus needs a few scrape intervals before it holds data. Confirm both targets are up and give it a minute:
curl -s http://127.0.0.1:9090/api/v1/targets | jq -r '.data.activeTargets[] | "\(.labels.job) \(.health)"'Repeat queries never report a cache hit. Trickster caches by query and time range, so any change to the query string, the step, or the requested window produces a new cache key. Compare two genuinely identical requests. Also confirm the cache is in use:
curl -s http://127.0.0.1:8481/metrics | grep -c '^trickster_cache_operation'A configuration edit stopped the service. Trickster validates its configuration on startup. Check the file before reloading:
sudo /usr/local/bin/trickster -validate-config -config /etc/trickster/trickster.yamlThe bundled Prometheus is not reachable from your workstation. That is intended. It is bound to the loopback interface. Use the SSH tunnel shown earlier rather than opening port 9090.
Security recommendations
- Restrict port 8480 to the subnet your dashboards run in. The port is authenticated, but there is no reason to expose it to the whole internet. Use a network security group rule scoped to your Grafana subnet.
- Terminate TLS in front of the appliance. HTTP Basic authentication sends the credential base64-encoded, not encrypted. Put an Azure Application Gateway or a TLS-terminating load balancer in front of port 8480 for anything that crosses an untrusted network.
- Never open ports 8481, 8484 or 9090. They are bound to the loopback interface deliberately. The management endpoint prints your full running configuration including origin addresses, and the bundled Prometheus has no authentication of its own. Use SSH tunnels.
- Keep the credentials file root-only.
/root/trickster-credentials.txtis mode0600and owned by root. Copy the password into your secret manager and leave the file alone. - Leave unattended security upgrades enabled so the appliance keeps receiving operating system patches.
Rotate the credential when staff change. Write a new bcrypt entry and reload:
sudo htpasswd -B /etc/trickster/users.htpasswd sac
sudo systemctl reload tricksterSupport
SecureAnyCloud provides 24/7 support for this image. Visit secureanycloud.com, or contact support through the Azure Marketplace listing.
Trickster and Prometheus are both open-source projects released under the Apache License 2.0. Their license texts ship on the image at /usr/share/doc/trickster/LICENSE and /usr/share/doc/prometheus/LICENSE.
Deploy on Azure
Launch Trickster 2.0 HTTP Reverse Proxy Cache and Dashboard Accelerator on Ubuntu 24.04 on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com