// docs

Quickstart

Five steps from zero to monitored: an account, a check, the server agent, an alert channel and a heartbeat for the jobs that run in the dark. Ten minutes, no credit card.

01

Create your account

Sign up and you land on the free plan: 10 monitors at 5 minute intervals, 1 server agent and a single seat. No card, no trial clock. Any paid plan adds seats, and from then on you invite colleagues from the Team page; every change anyone makes lands in the audit trail.

02

Add your first check

Pick a check type on the Monitors page: HTTP(S), TLS certificate, DNS record, SMTP, TCP port or heartbeat. For an HTTP check you give it a URL and what to assert: the expected status code, text the body must contain, or text it must not contain.

An https URL also watches its own certificate. We read it about once a day and, 14 days before it expires (your call), open a certificate incident and alert your channels. It is a warning rather than an outage: the site is still up, so it never counts against uptime. The separate TLS check is for certificates on hosts that are not websites: mail servers, IMAP, custom ports.

Two settings shape how it pages you. The interval is how often we probe: every 5 minutes on the free plan, down to every 15 seconds on the larger ones. The failure threshold is how many consecutive misses open an incident, 3 by default. One blip never pages anyone; the threshold is yours to tune per monitor.

type     http

url      https://api.example.com/health

assert   status 200 · body contains "ok"

interval 5m · threshold 3

03

Install the server agent

Register the machine on the Servers page and the app hands you a one line installer with a fresh token:

curl -fsSL https://p.relay19.com/v1/install.sh | sh -s -- --token ua_xxxxxxxx

The agent starts reporting CPU, memory, load, every real disk and your Docker containers within a minute. It only talks outbound over HTTPS; nothing listens on the box. Remove it any time with the matching uninstall.sh.

What the installer does

Piping a script into a shell is an act of trust, so here is the whole of it. The script is under a hundred lines of plain POSIX sh with no dependencies beyond curl and systemd, and you can read it first: fetch the URL without the pipe and it prints to your terminal.

  1. 01 Checks it is running on Linux on x86_64 or arm64, and stops with a clear message otherwise.
  2. 02 Uses sudo for the privileged steps when not already root; nothing else needs elevation.
  3. 03 Detects a Docker socket at /var/run/docker.sock and, only then, grants the agent read access to it through the docker group.
  4. 04 Downloads the agent binary for your architecture over HTTPS from the same host that served the script.
  5. 05 Installs it as /usr/local/bin/relay19-agent.
  6. 06 Writes /etc/relay19-agent.env with the endpoint, your token and the interval. The file is owned by root with mode 0600 and is the only place the token exists on the machine.
  7. 07 Writes /etc/systemd/system/relay19-agent.service, which reads that file, with hardening on: a dynamic unprivileged user, a read-only filesystem view, no new privileges.
  8. 08 Enables the unit and starts (or restarts) it. The first heartbeat lands within the interval.

Three files land on disk, all listed above, and the uninstaller removes exactly those three. The token is passed as an argument and written only into the root-only env file; it never goes through the script URL. The unit deliberately does not carry it inline, because systemd would then show it to any local user.

The agent itself is a single static Go binary. Every fifteen seconds it reads /proc and /sys for CPU, memory, swap, load, disk usage and I/O, and network counters, asks the Docker socket for container names and states when it has access, and POSTs one JSON document to the ingest endpoint with the token as a bearer header. It runs as a systemd dynamic user with a read-only view of the system, cannot gain privileges, opens no ports and executes nothing on your behalf. Updating is the same shape: update.sh replaces the binary and keeps the unit and token in place.

04

Wire up alerts

Add a notification channel on the Notifications page. Eight kinds ship today, each formatted for where it lands rather than dumped as raw JSON:

Email
One or more addresses, up to ten per channel.
Slack
An incoming webhook URL from a Slack app.
Discord
A channel webhook URL, posted as an embed.
Microsoft Teams
A workflow URL, posted as an adaptive card.
Telegram
A bot token and the chat id to message.
ntfy
A topic on ntfy.sh or on your own server.
PagerDuty
Opens an alert on your rota, and closes it again on recovery.
Webhook
Signed JSON to any endpoint you control.

Channels can filter by monitor tag, so the database team only hears about database monitors. Every channel fires when an incident opens and again when it resolves. While a monitor stays down, chat, email and webhook channels also get a still-down reminder on a tapering schedule: 15, 30, 45 and 60 minutes after the outage started, then at 2, 4, 8 and 24 hours, then daily. PagerDuty is left alone because it already escalates on its own. Send a test from the channel dialog before you rely on one; it reports back whatever the receiving end says.

Where each URL comes from

The chat services each hand you a URL or a token from their own settings. This is where they hide it:

Slack

  1. 01 Go to api.slack.com/apps and create a blank app, giving it a name.
  2. 02 Open Incoming Webhooks in the new app's settings.
  3. 03 Enable incoming webhooks, then click Add a new webhook.
  4. 04 Pick the workspace and the channel it should post to, then click Allow.
  5. 05 The webhook URL is shown on the settings page. Copy it and paste it into relay19.

The URL lives in the app settings, never in the Slack client itself, which is why browsing the channel will not turn it up. The old Custom Integrations route is retired, so an app is now the only way to get one.

Discord

  1. 01 Open Server Settings, then Integrations, then Webhooks.
  2. 02 Create a webhook and point it at a channel.
  3. 03 Press Copy Webhook URL.

Microsoft Teams

  1. 01 Add the workflow called Post to a channel when a webhook request is received.
  2. 02 Copy the URL it hands you when the workflow is saved.

The retired Office 365 connector URLs no longer accept posts.

PagerDuty

  1. 01 Go to Services, then Service Directory, and open the service you want alerted.
  2. 02 Click Add another integration, pick the Events API V2 tile from the list and click Continue.
  3. 03 Open the new integration with the cog beside it and copy the Integration Key, 32 characters.
  4. 04 Pick the EU region in relay19 only if your PagerDuty account is on the EU service region.

Events API V2 is a tile in that list, not something you type; there is no search to run. If it is genuinely absent, check that Alert and Incident Settings on the service is set to create alerts and incidents, since V2 cannot attach to a service that creates incidents only. Integration keys live on a service, never in the top level Integrations menu, and the API Access Keys under Developer Tools there are REST keys that will not work for alerts. One last thing: PagerDuty accepts any well formed key and sorts out the routing afterwards, so a passing test proves the request arrived, not that the key is right. Check the alert actually appeared in PagerDuty.

Telegram

  1. 01 Create a bot with @BotFather and keep the token it gives you.
  2. 02 Add the bot to your chat and send it a message.
  3. 03 Read the chat id from api.telegram.org/bot<token>/getUpdates.

Group ids start with a minus sign; include it.

Raw webhooks

Webhook requests carry the event name in an x-uptime-event header. Set a secret on the channel and each request is signed with an HMAC SHA-256 of the body in x-uptime-signature, so your receiver can verify it came from us. The body looks like this:

{
  "event": "incident.opened",
  "timestamp": "2026-08-16T14:04:12.000Z",
  "monitor": {
    "id": "mon_…",
    "name": "api",
    "type": "http",
    "target": "https://api.example.com/health"
  },
  "incident": {
    "id": "inc_…",
    "kind": null,
    "startedAt": "2026-08-16T14:04:11.000Z",
    "resolvedAt": null,
    "cause": "expected status 200, got 503"
  }
}

05

Cover your cron jobs

Backups and pipelines fail silently: nothing is down, the work just did not happen. A heartbeat monitor flips the arrow, so your job pings us. Create one, set the expected interval and a grace window, and append the ping to the job:

./backup.sh && curl -fsS https://p.relay19.com/v1/hb/<token>

GET or POST both count. Miss the interval plus grace and an incident opens like any other check; the next ping resolves it.

// that's it

You are monitored. From here: tune thresholds per monitor, tag monitors to route alerts, and read the incident lifecycle to see exactly when relay19 pages you. Stuck? Mail hello@relay19.com and a human answers.

Start monitoring →