FAQ
Why did you build this?
Because I can! It started as a fun side project, so I decided to build a notification relay from scratch and play with push protocols, NATS-style permissions, templating, retry queues, all of it.
After running it for a while it's matured into something I actually rely on, and somewhere along the way it picked up a bit of personality.
Okay but why would I use it?
A few things pingd does well:
-
NATS-style permissions. Grant
ro,wo,rw, ordenyon topic name patterns, per user or globally. Wildcards and inheritance work the way you'd expect. See Permissions. -
Per-topic webhooks with templates. Point pingd at any JSON source
(GitHub, Grafana, your scripts) and rewrite the payload with
{{path.to.field}}templates. No glue script in between. - Per-delivery tracking. Every push to every device is its own row with status, retry count, and timestamps. When something doesn't arrive you can actually find out why.
- Per-device subscriptions and controls. Devices are first-class on the server, so you can list them, mute one without unsubscribing the rest, see exactly which device received (or missed) a message, and revoke one when you lose it.
- TTL-aware expiry. Past-TTL deliveries get marked expired instead of pushed late.
- Share tokens for topics. Hand out read-only, write-only, or read-write access to a single topic without creating a user. Each token rotates and revokes on its own, so cutting off one publisher or subscriber leaves the rest untouched.
- Free and self-hostable. One Docker container, one SQLite file, your hardware, your data.
Is it really free?
Yes. No paid tier hiding the good parts. If you self-host, your only cost is the box you run it on.
Is there a hosted version?
Yes! pingd.dev (COMMING SOON!), if you just want to try it or don't feel like running your own. But pingd is meant to be self-hosted. That's the default story!
Why not just use Slack / Discord / PagerDuty?
Use them if they work for you. pingd is for when you want self-hosting, one boring relay between many publishers and many devices, and access control beyond a shared channel webhook. If "post to a Slack channel" already solves your problem, keep doing that.
Can I use Postgres instead of SQLite?
Not yet, maybe later for heavier deployments. For most self-hosted use cases, a single SQLite file with regular backups is enough.
Can I publish without authenticating?
There are three ways to publish, in order of openness:
-
Anonymous, if the topic has
publicPublish=true. No token needed, anyone can post. -
With a share token, by passing a
woorrwtopic share token in theX-Topic-Tokenheader. The token itself grants access, no user account or permission grant needed. -
With a bearer token, if the user owns the topic or has been granted
wo/rwvia the permissions system. This is the normal authenticated path.
See Permissions for the full ACL rules.
Can I subscribe without an account?
Anonymous clients can't subscribe for push, but they can watch a topic over SSE using a share token. To actually receive push, you need a guest or authenticated account. Guest accounts work without signup. See Subscribing.
How do I rotate the admin password?
Three ways:
- Dashboard: account view, change password.
- CLI:
pingd-cli users update admin --password <new>. - API:
PATCH /users/adminwith the new password.
The ADMIN_PASSWORD env var only seeds the very first user on a fresh
database. Changing it later does nothing.
Why isn't my iOS device getting pushes?
Common causes:
PINGD_APNS_MODEis unset, so pingd is using the mock provider. See Configuration.- Sandbox token registered against a production-mode server, or vice versa. Match
PINGD_APNS_ENVto the build. See APNS. - The device's
isActiveflag isfalse(logout deactivates it). - The device's
deliveryEnabledflag isfalse. - No subscription to the topic. Subscribing to
alertsdoes not coveralerts.disk. See Subscribing.
Check GET /messages/:id/deliveries after publishing. Each delivery row has status, retryCount, and timestamps.
Can I subscribe to wildcards?
No. Subscriptions are concrete (alerts.disk, not alerts.*).
Wildcards exist at the permission layer only. They grant access to a pattern, but you
still subscribe to each topic by name.
What happens when I delete a topic?
It's a hard delete and it cascades. Gone with the topic:
- All messages in the topic, and their delivery records.
- All device subscriptions to it (the devices themselves stay).
- All share tokens for it.
- All webhooks attached to it.
Permission grants are pattern-based, not topic-id references, so a grant like
rw on alerts.* survives the deletion of alerts.disk.
If you re-create a topic with the same name, those grants apply again immediately.
How do I back up everything?
Back up $PINGD_DATA_DIR. It contains the SQLite database and the CLI token
config. The simple option is to stop pingd, copy the data directory, then start it again.
For online backups, use SQLite's .backup command against the database file.
Can I contribute?
Yes please! The repo is on GitHub. Bug reports, feature ideas, and PRs are all welcome.
For anything beyond a small fix, opening an issue first helps so we can talk through the shape before you spend time on it. Build and test commands are in the README.
One small note: the codebase is hand-written and that's part of why working on it is fun. AI-assisted PRs are absolutely welcome, just mention it in the description so reviewers have the context.