# Boop: a self-hosted notification inbox that pushes to your iPhone

> Boop is a single Go binary with SQLite and an embedded web UI that turns HTTP POSTs from your apps into push notifications on iOS, with no hosted relay in between. It is small enough to read in an afternoon and opinionated enough to refuse some jobs.

**chrisgreg/boop** — A tiny, self-hosted notification inbox for developers. Something happened in one of your apps; Boop tells you on your phone.

- Repository: https://github.com/chrisgreg/boop
- Stars: 856 · Forks: 40
- Language: Go
- License: MIT
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/chrisgreg-boop

## The gap Boop fills between log files and a paging service

Most small applications generate events that matter to one person: a backup finished, a deploy failed, a webhook fired. Sending those to a paging service means paying for a product built around on-call rotations, and sending them to email means they get lost. Boop takes the third path. According to the README, a single Go binary stores events in one SQLite file and pushes them to Apple's APNs directly, with no hosted relay, account system or telemetry. The intended user is a developer who runs their own server and wants a phone notification in a few seconds.

The scope is deliberately narrow. Boop is a notification inbox, not an alerting platform: there is no escalation policy, no on-call schedule, and no mention of SMS or phone calls anywhere in the README. It is also iOS-only today. The repository lists an iOS app under ios/ that the reader builds and signs themselves, and describes a native desktop client as planned. Android is not discussed.

## How an event travels from curl to the lock screen

The architecture section of the README is explicit about the data flow. Your application POSTs an event with a project API key. The Go server redacts sensitive keys, stores the event in SQLite, and pushes to APNs using your .p8 key. The push payload carries only the title, body and event id; the iOS app then fetches the full event from your server using its own device credential. That split matters, because it means the complete event body never leaves your infrastructure through Apple. Apple sees a short summary and an identifier.

Two server-side behaviours are worth knowing before you rely on them. Grouping keys off a fingerprint field: send the same fingerprint for the same problem and both the web inbox and the phone show one row per fingerprint, with a count and first and last seen times, opening into the individual occurrences. The README states that grouping only affects display and that every occurrence is still stored and pushed, so it will not quiet a noisy loop. Silences are the documented mechanism for stopping pushes. Redaction happens before storage, and the README says recognised sections such as exception, stacktrace, tags, context and breadcrumbs get nicer rendering while anything else in the data object is kept as-is.

## Installing Boop with Docker and sending a first event

The README gives Docker as the primary path. Clone the repository, copy the example environment file, and on Linux hosts create the data directory owned by uid 1000, because the container runs as that user. The compose file mounts ./data at /data and publishes port 8080.

```bash
git clone https://github.com/chrisgreg/boop && cd boop
cp .env.example .env
mkdir -p data && chown 1000:1000 data
docker compose up -d --build
```

After the build finishes, the README says to open http://localhost:8080. The first visit runs a setup wizard covering the server check, APNs, pairing, the first project and a test notification. APNs credentials are optional: without them, events are stored and shown in the UI but pushes are skipped, and the settings page says so. That is a useful way to try the inbox before you create an Apple key.

There is also a binary path. Every release ships a static binary for Linux, macOS and Windows on amd64 and arm64 with the web UI embedded, and the README notes that BOOP_DATABASE_PATH defaults to /data/boop.db, so you set it somewhere writable.

```bash
tar xzf boop_*_linux_amd64.tar.gz && cd boop_*_linux_amd64
BOOP_DATABASE_PATH=./boop.db ./boop
```

To send an event, create a project in the web UI and copy its API key, which the README says is shown once. The minimum payload is a title.

```bash
curl http://localhost:8080/api/v1/events \
  -H "Authorization: Bearer boop_proj_..." -H "Content-Type: application/json" \
  -d '{"title": "Deploy complete"}'
```

A rich payload adds a body, a level from info, success, warning, error or critical, a source, a fingerprint for grouping, and a data object. The README also documents up to three action buttons per event. Labels are capped at 40 characters and URLs must be absolute, either https or an app scheme. Tapping the notification body still opens the event in Boop rather than following an action. For a shell script, the README offers a function that wraps curl and a one-liner that reports the success or failure of a pg_dump.

## Where Boop is the wrong tool

The most obvious limit is that push only reaches iOS, and only through an app you compile and sign. The README states plainly that you build and sign the iOS app yourself, and points to ios/README.md for the details. Anyone who wants a notification on an Android device, or who does not have an Apple developer setup, has no path here. The desktop client is listed as planned, which is not something to plan around.

The second limit is operational. Without APNs credentials the server still accepts and stores events, so a misconfigured deployment looks healthy from the API side while nothing ever reaches a phone. The README says the settings page reports this, but it is the kind of failure you only notice when you needed the notification. Egress to Apple's push endpoints is also a hard requirement that the repository does not discuss, so a locked-down network is a real risk.

The third is durability. Everything lives in one SQLite file at ./data/boop.db. The README recommends copying that file for backups and gives sqlite3 data/boop.db ".backup backup.db" for a consistent copy while running, plus a separate backup of the .p8 key. That is honest guidance, but it also means Boop is a single-node system with no documented replication and no documented rollback procedure.

## Boop against a general-purpose push service

The natural alternative is a hosted notification service such as ntfy or Pushover, and the difference is where the event body lives. With a hosted service, your application sends the full message to a third party, which stores it and forwards it to the device. Boop inverts that: the server you run holds the event and the credentials, and the push payload contains only the title, body and event id, with the phone fetching the rest from your own server. If the content of your events is the reason you are self-hosting, that split is the whole argument.

The trade is reach and maintenance. A hosted service supports Android and iOS out of the box and has no server for you to patch. Boop gives you one binary, one SQLite file and one container, which the README presents as the pitch, but you own the host, the TLS proxy, the retention setting and the APNs key rotation. Boop also has a narrower feature set: it is an inbox for events, with grouping and silences, rather than a general pub-sub topic system with per-subscriber subscriptions.

## Retention, configuration and the MIT licence

Configuration is a flat set of environment variables shared by both install paths. BOOP_BASE_URL is the public address your phone uses to reach the server behind your HTTPS proxy. BOOP_RETENTION_DAYS controls how many days of event history are kept, with 0 meaning forever, and the example file notes that the environment value overrides whatever is saved in the web UI. BOOP_ADMIN_USER and BOOP_ADMIN_PASSWORD protect the web UI and admin API with a single login; the README warns that leaving both blank runs without a login, which it says to do only behind your own proxy or VPN, and that the password needs 8 or more characters. BOOP_MCP_TOKEN is a bearer token for a read-only MCP endpoint at /mcp, needs 16 or more characters, and can be left blank.

The APNs block has its own rule worth reading twice. You set APNS_TEAM_ID, APNS_KEY_ID, APNS_BUNDLE_ID and APNS_ENVIRONMENT, and exactly one of APNS_PRIVATE_KEY_PATH or APNS_PRIVATE_KEY. The example file states that a non-empty APNS_PRIVATE_KEY_PATH always wins over APNS_PRIVATE_KEY, so leave the path blank if you are pasting the key. APNS_ENVIRONMENT takes production or sandbox, with sandbox for Xcode debug builds.

Upgrading means replacing a binary or rebuilding an image, and the release history shows two versions a day apart in late August 2026, so the project does move. The repository ships a CHANGELOG.md, which is where release notes live. The licence is MIT, which permits commercial and private use and modification; the practical implication is that you carry the support burden for your own deployment, and nothing in the repository promises a security review or a maintenance window.

## Conclusion

Adopt Boop if you already run a small server and want app events on your phone without shipping them through a vendor, and if you accept building and signing the iOS app yourself. Do not adopt it if you need Android, a hosted service, or an audit trail you can hand to a compliance team, because the README describes none of those. Verify first that your host can reach Apple's APNs endpoints and that your HTTPS proxy forwards to port 8080, since the push path is the part that fails silently when credentials or egress are wrong.

## FAQ

### What is Boop and what does it do?

Boop is a self-hosted notification inbox for developers. Your apps POST events to a Go server that stores them in SQLite and pushes them to your phone through Apple's APNs, with no hosted relay or account system involved.

### How do I install and run Boop with Docker?

Clone the repository, copy .env.example to .env, create a data directory owned by uid 1000 on Linux hosts, then run docker compose up -d --build and open http://localhost:8080. The first visit runs a setup wizard for the server check, APNs, pairing, your first project and a test notification.

### Does Boop work without APNs credentials?

Yes, but pushes are skipped. The README states that without APNs credentials events are stored and shown in the UI, and the settings page says that pushes are being skipped.

### Where does Boop store its data and how do I back it up?

Events live in one SQLite file, ./data/boop.db in the Docker setup. The README recommends copying that file, or using sqlite3 data/boop.db ".backup backup.db" for a consistent copy while the server is running, and backing up your .p8 key separately.

### Does Boop support Android or a desktop app?

The repository lists an iOS app that you build and sign yourself, and describes a native desktop client as planned. Android is not mentioned in the README.

## Sources

- [chrisgreg/boop on GitHub](https://github.com/chrisgreg/boop)
- [Issues](https://github.com/chrisgreg/boop/issues)
- [License: MIT](https://github.com/chrisgreg/boop/blob/main/LICENSE)
- [README](https://github.com/chrisgreg/boop/blob/main/README.md)
- [Releases](https://github.com/chrisgreg/boop/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/chrisgreg-boop
