Get started

Zero to paged in minutes.

Pingtower is two halves: a server that turns your log stream into alerts, and an iOS app that gets those alerts to a human — through Silent mode when it has to. The hosted service runs the server half for you — sign up and start ingesting. Want the data plane on your own hardware instead? Download a signed release and self-host.

Start hosted — the recommended path

The hosted service is the fastest way to get paged: no server to run, no upgrades to schedule, and the push pipeline to your phone is ours end to end.

Signups are open, and the service is free while Pingtower is in preview — no credit card required (the pricing page spells out the free tier and how pricing would arrive). The iOS app is in public beta — join the TestFlight beta (open to everyone, no invite needed), sign up in the app, and you're pageable. Prefer the API? Everything below works without the app:

curl -s https://api.pingtower.com/v1/signup \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","password":"…"}'
# → {"account":{…},"token":"pts_…"}

curl -s https://api.pingtower.com/v1/tenants \
  -H "Authorization: Bearer pts_…" \
  -H 'Content-Type: application/json' \
  -d '{"name":"Acme"}'
# → {"id":"<tid>","name":"Acme","role":"owner"}

curl -s -X POST https://api.pingtower.com/v1/tenants/<tid>/keys \
  -H "Authorization: Bearer pts_…"
# → {"id":"…","token":"ptk_…"}   # shown once — store it

From there create a project, a source (its pti_ ingest token is what your apps POST with), and a rule — the API reference covers the whole data plane, and the agent guide is written so an AI agent can run the entire setup for you.

Self-host the server

Prefer your alert data on your own box? The server is a single static Go binary — no runtime, no dependencies — and does the whole inbound half locally. Every release carries an SSH signature (ssh-keygen -Y, stock OpenSSH 8.2+) over a signed provenance file naming exactly what was built; the signing key lives on a hardware token and cannot be extracted from it. The table lists the SHA-256 of each binary and of its signature.

ArtifactVersionSizeSHA-256
pingtower-linux-amd64ship/2026-W36.5-223.0 MB19d99da43556fbd98af0f7dd9d59b22b3645ae9f423de2da468a0c8636267d38
pingtower-linux-amd64.release + .sig268d1da7d480666b3e927308b66cecff3a8ded76f54e8c254afd3b53154f3366
pingtower-linux-arm64ship/2026-W36.5-221.7 MB393f21277c50b98d8bea18ead00764380785cf3d40c71b3b733a8f113e93f4e1
pingtower-linux-arm64.release + .sig9bd5f0e3e4021a2550c2bcf593cc0fde6b05a88992dfd23b90e687e5c6bfd95a

Verify the download

Check the SHA-256 against the table above, then verify the signed provenance against the Pingtower release public key (pingtower-release.pub) and make sure its sha256= names the binary you have:

sha256sum pingtower-linux-amd64   # compare with the table above

printf '[email protected] namespaces="pingtower-release" [email protected] AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIBAbOs2Sr9lD/f8puNgGRxbYJPB7QVbxyar4jZfM37rKAAAADHNzaDpzaGlwc2lnbg==\n' > allowed_signers
ssh-keygen -Y verify -f allowed_signers -I [email protected] \
  -n pingtower-release -s pingtower-linux-amd64.release.sig \
  < pingtower-linux-amd64.release

cat pingtower-linux-amd64.release   # sha256= must match the sum above

The provenance line names the artifact, version, git commit, and SHA-256 the signature was made for — every field is inside the signed file, so none can be edited after the fact. If any check fails, do not run the binary.

Set it up on a Linux server

The daemon speaks plain HTTP on localhost and expects a reverse proxy (Caddy, nginx) to terminate TLS in front of it. Data lives in one directory of SQLite files.

Install the binary

sudo install -m 0755 pingtower-linux-amd64 /usr/local/bin/pingtower
sudo useradd --system --home /var/lib/pingtower --shell /usr/sbin/nologin pingtower
sudo mkdir -p /var/lib/pingtower
sudo chown pingtower:pingtower /var/lib/pingtower

Create a systemd unit

/etc/systemd/system/pingtower.service:

[Unit]
Description=pingtower
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=pingtower
Group=pingtower
ExecStart=/usr/local/bin/pingtower \
  -listen 127.0.0.1:8391 \
  -data-dir /var/lib/pingtower
Restart=on-failure
RestartSec=2
TimeoutStopSec=20
LimitNOFILE=65536
UMask=0077
NoNewPrivileges=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/pingtower
ProtectHome=yes
PrivateTmp=yes

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now pingtower
curl -s http://127.0.0.1:8391/healthz

Put TLS in front

With Caddy, one line gets you a certificate and the proxy (/etc/caddy/Caddyfile):

alerts.example.com {
    reverse_proxy 127.0.0.1:8391
}

First account, first tenant

curl -s https://alerts.example.com/v1/signup \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","password":"…"}'

curl -s https://alerts.example.com/v1/tenants \
  -H "Authorization: Bearer pts_…" \
  -H 'Content-Type: application/json' \
  -d '{"name":"Acme"}'

From there, create a source to get an ingest token and start POSTing logs — the API reference covers the whole data plane. Useful flags: -retain-days, -max-logs-per-source, -backup-dir, and -secret-key to seal integration credentials at rest (pingtower -h lists them all).

Ring a phone

A self-hosted server does the whole inbound half — ingest, dedup, rules, escalation, plus webhook, Slack, and Telegram delivery — on your box. Paging a phone through the iOS app additionally needs a pingtower.com account paired to your server (pingtower pair --email [email protected] on the box prints a one-shot code to redeem in the app), because Apple push for the App Store app can only be sent by us. The how it works page and the terms spell out that boundary.