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 itFrom 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.
| Artifact | Version | Size | SHA-256 |
|---|---|---|---|
| pingtower-linux-amd64 | ship/2026-W36.5-2 | 23.0 MB | 19d99da43556fbd98af0f7dd9d59b22b3645ae9f423de2da468a0c8636267d38 |
| pingtower-linux-amd64.release + .sig | 268d1da7d480666b3e927308b66cecff3a8ded76f54e8c254afd3b53154f3366 | ||
| pingtower-linux-arm64 | ship/2026-W36.5-2 | 21.7 MB | 393f21277c50b98d8bea18ead00764380785cf3d40c71b3b733a8f113e93f4e1 |
| pingtower-linux-arm64.release + .sig | 9bd5f0e3e4021a2550c2bcf593cc0fde6b05a88992dfd23b90e687e5c6bfd95a | ||
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 aboveThe 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/pingtowerCreate 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.targetsudo systemctl daemon-reload
sudo systemctl enable --now pingtower
curl -s http://127.0.0.1:8391/healthzPut 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.