UptimeHunt Docs
Probes

Run Your Own Prober

Deploy a private prober inside your own infrastructure to monitor internal services and extend the global probe fleet.

Run Your Own Prober

Quick Reference

  • Tier: Team or Enterprise plan required
  • Connection: Single outbound wss:443 — no inbound ports needed
  • wss URL: auto-derived from API_COLLECTOR_URL (e.g. https://app.uptimehunt.iowss://app.uptimehunt.io/v1/agent)
  • Token env var: PROBE_TOKEN (falls back to API_COLLECTOR_TOKEN)
  • Container: ghcr.io/uptimehunt/prober
  • Register: Settings → Probes → Add Probe

Overview

UptimeHunt operates a global probe fleet that monitors your services from multiple locations worldwide. Private probers let you extend that fleet with your own monitoring nodes — running inside your own infrastructure, data centre, or private network.

Why run a private prober?

  • Monitor internal services (staging, VPNs, private APIs) that are unreachable from the public internet
  • Add a monitoring location specific to your users, such as a region not yet covered by the platform fleet
  • Validate how your service behaves from your own office network or data centre
  • Supplement the global fleet with higher-frequency checks from a known location

Private probers are first-class citizens: their results flow through the same pipeline (realtime updates, history, incidents, alerts) as the platform fleet.

Requirements

RequirementDetail
PlanTeam or Enterprise — see pricing
Outbound networkPort 443/TCP only (HTTPS/WSS). No inbound ports required.
RuntimeDocker or Podman (any host that can run a container)
CPU / RAMMinimal — ~50 MB RAM per prober under normal load

Private probers are not available on Free or Pro plans. The registration UI shows an upsell card if your org is on a lower plan.

How It Works

A private prober maintains a single persistent WebSocket connection (over TLS, port 443) to the UptimeHunt broker. All communication is outbound from the prober — there are no inbound ports, no firewall holes, no public IP required on your side.

Connection lifecycle:

  1. Prober connects and sends a hello frame containing its token.
  2. The broker validates the token once (cached for the session), binds the connection to your org, and pushes the current check config (full desired state, including which services to monitor and their intervals).
  3. The prober persists the config to local disk and self-schedules using its own scheduler.
  4. Check results are streamed back to the broker, which validates authorship and forwards them to the internal pipeline.
  5. If the connection drops, the prober keeps running from its cached config and buffers results locally. On reconnect it receives a fresh config, then replays buffered results with their original timestamps — no data loss or duplicate counting, as long as the outage stays within the buffer's configured capacity (see Buffer size limit below).

Getting Started

1. Register a Private Prober

  1. Go to Settings → Probes in the UptimeHunt dashboard.
  2. Click Add Probe (disabled if your plan does not include private probers — see the upsell card for upgrade options).
  3. Fill in:
    • Name — a label for this prober (e.g. office-london, datacenter-eu-west)
    • Region / Location — optional expected geographic area
  4. Click Create.

Copy the token now

The connection token is shown only once immediately after creation. Copy it and store it in a secret manager. If you lose it, rotate to issue a new one — see Token Rotation.

2. Install and Run

Run the prober container with two environment variables:

  • PROBE_TOKEN — the token copied in the previous step
  • API_COLLECTOR_URL — the base URL of the UptimeHunt API (for the managed cloud this is https://app.uptimehunt.io)
podman run -d \
  --name uptimehunt-prober \
  --restart=always \
  -e PROBE_TOKEN=your-token-here \
  -e API_COLLECTOR_URL=https://app.uptimehunt.io \
  ghcr.io/uptimehunt/prober
docker run -d \
  --name uptimehunt-prober \
  --restart=always \
  -e PROBE_TOKEN=your-token-here \
  -e API_COLLECTOR_URL=https://app.uptimehunt.io \
  ghcr.io/uptimehunt/prober

Create /etc/systemd/system/uptimehunt-prober.service:

[Unit]
Description=UptimeHunt Private Prober
After=network-online.target
Wants=network-online.target

[Service]
Restart=always
RestartSec=5
Environment=PROBE_TOKEN=your-token-here
Environment=API_COLLECTOR_URL=https://app.uptimehunt.io
ExecStart=/usr/bin/podman run --rm --name uptimehunt-prober \
    -e PROBE_TOKEN=${PROBE_TOKEN} \
    -e API_COLLECTOR_URL=${API_COLLECTOR_URL} \
    ghcr.io/uptimehunt/prober

[Install]
WantedBy=multi-user.target

Enable and start:

systemctl daemon-reload
systemctl enable --now uptimehunt-prober

3. Verify the Connection

Go back to Settings → Probes. Your prober should show Online within a few seconds of starting. The Last contact timestamp updates with each heartbeat.

If the status stays Never connected, check the Troubleshooting section.

Connecting — How the wss URL is Derived

You do not set a separate WebSocket URL. The prober derives the broker address automatically from API_COLLECTOR_URL:

API_COLLECTOR_URLDerived wss endpoint
https://app.uptimehunt.iowss://app.uptimehunt.io/v1/agent
https://uptimehunt.example.comwss://uptimehunt.example.com/v1/agent

This means a single environment variable is all that is needed for both API connectivity and the broker WebSocket. No additional configuration is required.

Behaviour

Config Push

Check assignments are pushed to the prober over the WebSocket connection as a full desired-state config frame. The prober persists this config to a local SQLite file. Changes (new service, deleted service, updated interval) propagate within seconds of saving in the dashboard.

Self-Scheduling

The prober schedules checks entirely on its own from the cached config. It does not poll the API for tick instructions — it receives the config once (and again on change) and runs checks at the specified interval on its own clock. This is why the prober can keep running reliably during central downtime.

Downtime Buffering and Replay

If the prober loses its connection to the broker (network outage, broker restart, local reboot), it:

  1. Continues executing scheduled checks from the last-persisted config.
  2. Writes results to a local buffer queue (SQLite, path set by QUEUE_DB_PATH — defaults to a path inside the container).
  3. On reconnect, replays all buffered results to the broker with their original @timestamp values and stable per-result dedup IDs.

The pipeline (archiver, alert-engine, realtime) is designed to accept out-of-order, timestamped results, so a prober that was offline for an hour and then reconnects sends back ~N spaced check results without double-counting or false incident state.

Persist the buffer across container restarts

Mount a host directory for QUEUE_DB_PATH if you need the buffer to survive container restarts:

podman run -d \
  --name uptimehunt-prober \
  --restart=always \
  -e PROBE_TOKEN=your-token-here \
  -e API_COLLECTOR_URL=https://app.uptimehunt.io \
  -e QUEUE_DB_PATH=/data/queue.db \
  -v /var/lib/uptimehunt-prober:/data \
  ghcr.io/uptimehunt/prober

Buffer size limit

The result buffer is bounded to prevent unbounded disk usage, but the two caps are not equally reliable:

  • RESULT_QUEUE_MAX_ROWS (default: 50000) is a size-independent row cap that is always active. When the buffer exceeds this many rows, the oldest are discarded to make space for new ones. This is the bound you can rely on.
  • RESULT_QUEUE_MAX_BYTES (default: 64 MiB / 67108864 bytes) is a best-effort on-disk byte cap. Reclaiming disk space this way only works if the prober's one-time SQLite auto_vacuum=FULL migration succeeded at startup — and that migration itself needs free disk roughly equal to the buffer database's current size to run. On a disk-constrained host (exactly the scenario this setting is meant for), that migration can fail, in which case the byte cap does nothing.

If your prober's logs show a result_queue.vacuum_failed or result_queue.bytecap_inactive warning, the byte cap is currently inactive on that prober — RESULT_QUEUE_MAX_ROWS is the only bound actually limiting buffer size. If your prober is expected to be offline for extended periods, size RESULT_QUEUE_MAX_ROWS for the outage you want to survive, and treat RESULT_QUEUE_MAX_BYTES as an additional safeguard rather than a guarantee:

-e RESULT_QUEUE_MAX_ROWS=100000
-e RESULT_QUEUE_MAX_BYTES=134217728  # 128 MiB — best-effort only, see above

Token Rotation

If a token is compromised or you want to rotate it as a routine security practice:

Via the dashboard:

  1. Go to Settings → Probes.
  2. Click the menu ( ⋯ ) next to the prober.
  3. Select Rotate token.
  4. Copy the new token — it is shown only once.
  5. Update the PROBE_TOKEN env var on your prober host and restart the container.

Via the API:

curl -X POST "https://app.uptimehunt.io/api/v1/probes/{id}/token" \
  -H "Authorization: Bearer <your-jwt-token>" \
  -H "Content-Type: application/json"

The old token is immediately invalidated. The broker force-drops any live connection using it.

The live connection drops instantly

Rotating a token disconnects the running prober immediately. The prober reconnects with the new token automatically only if PROBE_TOKEN has been updated on the host first. Until it reconnects, checks continue from cached config and results buffer locally.

Probe Visibility

By default, private probers are not visible to the public (they show only to authenticated members of your organization). The public field on a probe controls this — it defaults to false for private probers.

If you want the probe to appear in the public probe list (e.g. to contribute a new location to the community view), set public: true via the dashboard or API.

Troubleshooting

Status shows "Never connected" or "Offline"

  1. Check outbound connectivity to port 443. From the prober host, try:

    curl -I https://app.uptimehunt.io/healthz

    You should get a 200 OK response. If this fails, your network is blocking outbound HTTPS.

  2. Verify the token. Confirm PROBE_TOKEN matches the token displayed at registration. Tokens are 32+ characters. Whitespace or newlines in the env var cause auth failures.

  3. Check container logs:

    podman logs uptimehunt-prober

    Look for connected or authentication failed messages.

  4. Corporate proxy. If your network requires an HTTP proxy for outbound traffic, set HTTPS_PROXY or ALL_PROXY:

    -e HTTPS_PROXY=http://proxy.corp.example.com:3128

    The WebSocket upgrade travels over HTTPS and most corporate proxies pass it through. If your proxy blocks WebSocket upgrades, contact your network team — the connection must reach wss://app.uptimehunt.io/v1/agent on port 443.

  5. Only outbound 443 is needed. There is no inbound port requirement. Firewalls only need to allow the prober to reach app.uptimehunt.io:443 outbound.

Status shows "Online" but I see no check results

  • The prober only runs checks that are assigned to your organization. Create a service in the dashboard first.
  • The prober must receive a config push for the check to run. This happens automatically within seconds of creating a service, but verify the prober is online at the moment the service is created.
  • Check that the service is enabled in the dashboard.

Prober runs but results are missing after a restart

Mount the buffer queue to a persistent host path — see Downtime Buffering and Replay. Without a persistent mount, a container restart discards any buffered results from the offline window.

On this page