Deployment guide

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 8480 carries 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/ping returns the four-character response pong and 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 on 8484 that prints the running configuration, and the bundled Prometheus on 9090 (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.service and 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 8480 to 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_B2s with 2 vCPU and 4 GB RAM suits development and light dashboard workloads. For higher query rates or a larger cache working set, choose Standard_D2s_v5 or 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 8480

Connect 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 -1

Expected 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.txt

The 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-selfcheck

Expected 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/ping

Expected output

pong

Point 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 401

Query 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 = 1

Those 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=hit

The 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

5

The 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 -4

Expected 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"} 3

The 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 up

Accelerate 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.yaml

Change 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: default

Leave 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 trickster

Once Trickster points at your own backend, you can stop the bundled Prometheus to reclaim its memory and disk:

sudo systemctl disable --now prometheus

Trickster 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 -20

Confirm 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 000

000 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-pager

Reload Trickster after a configuration change without dropping connections, or restart it outright:

sudo systemctl reload trickster

Read the logs:

sudo journalctl -u trickster -n 30 --no-pager

Scripts 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.log

Troubleshooting

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.txt

trickster.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-pager

If 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.sh

Queries 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.yaml

The 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.txt is mode 0600 and 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 trickster

Support

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

Deploy on Azure