<!-- https://nightbell.app -->

![The Nightbell monitor list at 3:14: a green All 5 operational card, then Checkout API and Auth service with their response times.](https://nightbell.app/hero/calm.webp)

All 5 operational

# The uptime monitor that actually wakes you up.

An open-source Android app that runs every check from your phone. No server, no account.

[Download APK 2.9 MB](https://nightbell.app/download) [Get it on F-Droid](https://f-droid.org/en/packages/me.river.nightbell/)

## Forty seconds, start to alarm.

0:40, captions on

### Read the video transcript+

1. 0:01 Most uptime alerts are one more notification you swipe away.
2. 0:05 Nightbell runs entirely on your Android phone. No server, no account, no third party.
3. 0:12 Four steps to add a monitor. Point it at anything that answers over HTTP.
4. 0:17 Or at a GitHub repository, and it reports what changed.
5. 0:23 When a check fails, Nightbell doesn’t send you something you can miss.
6. 0:28 It takes the whole screen, and the alarm loops until you acknowledge it.
7. 0:33 Nightbell. The uptime monitor that needs no server, no account, and actually wakes you up.

## Hosted, self-hosted, or the phone in your pocket.

Both of the usual answers are better at being a monitoring service. Nightbell is trying to be the thing that wakes you at 3am without a subscription, a server or an account.

### Hosted service

like UptimeRobot

- At 3am you get

  An email or a push notification.

- Infrastructure

  Theirs. Nothing to run.

- Account

  Required.

- Where tokens sit

  On their servers.

- Paging you

  Usually the paid tier.

- It cannot

  Fail quietly. You will get a bill.

### Self-hosted

like Uptime Kuma

- At 3am you get

  Whatever you wired up.

- Infrastructure

  A box, a container, a certificate, and monitoring for the monitor.

- Account

  Usually required, at least locally.

- Where tokens sit

  On your server.

- Paging you

  Yours to build, on top of a push service.

- It cannot

  Watch itself.

### Nightbell

on your own phone

- At 3am you get

  A full-screen page and an alarm that loops until you acknowledge it.

- Infrastructure

  The phone you already carry.

- Account

  None. There is nothing to sign up to.

- Where tokens sit

  On the device: monitors in one JSON document, sign-in secrets in an encrypted keystore vault.

- Paging you

  A local alarm, so there is nothing to bill for.

- It cannot

  Check while the phone is off, or beat Android's 15 minute background floor without a foreground service.

### Why I built it instead of paying for one

I kept missing outages because ordinary monitoring notifications behaved like everything else in the shade. The services I tried either wanted a subscription before they would text me, or sent something that looked exactly like a marketing email and got swiped away with one. By the time I noticed, the interesting part of the incident was over.

The hard part is alerting without crying wolf. An outage on an urgent monitor is not a line in the shade. It behaves like an incoming call, and everything else in the app exists to earn the right to do that.

## The check leaves your phone and goes to your service. That is the entire path.

Your phone runs the check

Monitoring\
company

not in the path

api.nightbell.app/health

200

Your service answers it

state stays here: one JSON document in DataStore phone → your service → phone

### Not in the path, and not in the dependency list.

Every one of these is something a monitoring app normally has. All of them can be checked against the repository, and none of them is there.

- No relay
- No queue
- No worker
- No backend
- No account
- No push service
- No analytics SDK
- No crash reporter
- No icon proxy

What it does not buy you is anonymity from the thing you are watching. Your service sees the request and the IP it came from, exactly as it would for any other client on your network. That is what a check is. Routing a monitor through your own proxy is the one thing that changes it, and only for that monitor.

### Implementation details

A status or request monitor is an OkHttp call from your phone to the host you typed in. A page-element monitor loads the page in an offscreen WebView on the same phone and reads the DOM nodes you picked by tapping them. There is no relay, no queue and no worker somewhere else, because there is nowhere else. The one exception is a hop you set up yourself: a monitor can be routed through a SOCKS5 proxy running on your own device, which is how a Tor or I2P hidden service gets checked. It goes where you point it and nowhere else, and it is off until you turn it on.

Everything the app knows lives in one JSON document in DataStore: your monitors, their headers and bodies, their sample history and your settings. Sign-in secrets are the exception: they sit in a vault encrypted with a keystore key and never go into a backup. Backup and transfer writes that document through the Storage Access Framework, which is why Nightbell asks for no storage permission and why you choose where the file goes. Nothing is uploaded, and there is no import path that could reach anything you did not hand it.

### What that does and does not buy you

It buys you this: an API token in a monitor's headers is on your phone and on your server, and nowhere in between. No third party can leak it, subpoena it, or change its terms of service. There is no analytics library and no crash reporter in the dependency list, and site icons are fetched from the monitored origin itself rather than through an icon proxy.

Two hosts get contacted that you did not choose. The connectivity reference, which you can repoint or switch off. And nightbell.app, every six hours, for the current version number: that one is counted, and what the count is allowed to know is a date, a status and which version asked. Not an address, not an identifier, not a hash standing in for one. It is the difference between knowing that four hundred copies of this are running and knowing anything at all about the four hundred people running them, and only the first of those is any of my business.

## Ten kinds of check. Pick the cheapest one that would catch the outage.

cheapest

### Status check

Hit a URL, assert on the status code. A refused connection reports no response and no latency rather than inventing a zero.

`exact code` `any 2xx` `a range` `any response at all`

precise

### Request and response

Method, headers and a body, then an assertion on what comes back. It catches the deploy that answers 200 with an empty payload.

`GET` `POST` `PUT` `PATCH` `DELETE` `HEAD`

`contains` `does not contain` `exactly equals` `matches regex` `JSON field equals` `JSON field exists`

heaviest

### Page element

Loads the real page in a WebView and watches the nodes you tapped. Every node is read from the same page load, and the alert names the one that broke.

`element exists` `element is gone` `text equals` `text contains` `text unchanged` `or an attribute instead of text`

activity

### GitHub repository

Five tracks of what happens on one repository, each switched on separately. A rate limit is never an outage; a 404 or a dead network is.

`stars` `issues and pull requests` `comments` `releases` `downloads`

metrics

### Prometheus

A metrics endpoint, a PromQL query or an Alertmanager. One kind, because the three differ only in the URL and how the answer is read.

`above` `at least` `below` `at most` `equals` `not equal`

your rules

### Health endpoint

Reads a report your service writes about itself. The verdict pages; each part brings the rule that decided it, what it measured and what it saw. A part that is down on purpose can be marked expected, and pages again when it changes.

`up` `degraded` `down`

`none` `headers` `basic` `single sign-on`

the host

### Ping

ICMP echo to an IPv4 or IPv6 address or a name, with no root. Some replies lost is degraded, none back is down, and it reports the fastest, average and slowest reply.

`1 to 10 packets` `IPv4` `IPv6` `either`

one port

### TCP port

Connects to one port and hangs up, nothing sent. Refused, timed out and no route each say so. Next to a ping on the same host, the app says whether the network or the service is down.

`22 SSH` `443 HTTPS` `3306 MySQL` `5432 Postgres` `any port`

one datagram

### UDP port

Sends the service a real request and checks the reply: a DNS answer, the time from NTP, an SNMP value. A closed port is down at once. Services that never reply get silent mode, which waits for a refusal and pings the host.

`DNS` `NTP` `STUN` `SNMP` `SIP` `HTTP/3` `game servers` `your own bytes` `silent`

what arrived

### Mailbox

Signs in to an IMAP folder and tells you what arrived, louder for the senders or subjects you name. Setup finds the server from the address and lists the folders to pick from. Nothing is ever marked read, and a mail server that stops answering is an outage.

`TLS` `STARTTLS`

`every message` `sender has` `subject has` `skip read`

Home-screen widget

## Worst monitor first.

Put Nightbell on your home screen and see every monitor without opening the app, the one in trouble at the top. Drag the widget flat and the list flows into columns instead of disappearing below the fold.

![The Nightbell widget at its tall size, one column listing six monitors worst first: Checkout API down, Search index slow at 3370 ms, then four healthy services with their response times.](https://nightbell.app/screens/widget-tall.webp)

![The same widget dragged wider, the six monitors spilling into two columns with a checked just now line underneath.](https://nightbell.app/screens/widget-wide.webp)

![The same widget dragged flat into a single wide row, showing the three worst monitors and nothing else.](https://nightbell.app/screens/widget-flat.webp)

## Four steps. Everything is changeable afterwards.

![Step one of the new-monitor wizard: the kinds of check listed as rows, Status check selected with a tick, then Request and response, Page element, GitHub repo and Prometheus.](https://nightbell.app/screens/wizard-1.webp)

Status check, request and response, page element, GitHub repository, Prometheus, health endpoint, ping, TCP port, UDP port or mailbox. This is the only choice that changes the rest of the wizard.

`status check` `request and response` `page element` `GitHub repository` `Prometheus` `health endpoint` `ping` `TCP port` `UDP port` `mailbox`

Nothing here is locked in. Every field is editable from the monitor's detail screen afterwards, and Test now runs the whole check once before you commit to it.

## When it breaks, Nightbell wakes you up.

Urgent is a per-monitor switch for the things you cannot sleep through. It is the one part of the app allowed to be loud.

1. ![A locked phone at 3:14 with a red Nightbell notification at the top: Checkout API is down, Connection refused or dropped.](https://nightbell.app/screens/urgent-lock.webp)

   3:14

   ### It gets through

   A dedicated alarm channel that bypasses Do Not Disturb. The notification is ongoing, so there is no swipe that clears it.

2. ![The full-screen Nightbell urgent page: DOWN, Checkout API, Connection refused, down for 20 minutes, four failed checks, with Acknowledge and Re-check now buttons and the line Repeats every 5 min until acknowledged.](https://nightbell.app/screens/urgent-alarm.webp)

   3:14

   ### It wakes the screen

   With full-screen alerts allowed, the page takes the screen and the siren loops until you act. A phone on silent still vibrates.

3. ![The Nightbell monitor screen for Checkout API marked Down with Connection refused, a 90 per cent uptime ring for the past three hours, and Check, Pause and Mute 1h buttons.](https://nightbell.app/screens/urgent-detail.webp)

   +5 min

   ### It keeps asking

   Posted again every five minutes until you acknowledge. Ack ends this outage only, and recovery re-arms it for the next one.

An eleven second clip of a failed check becoming the alarm. No sound, and it loops.

- On the page

  Acknowledge, Re-check now

- In the shade

  Ack, Re-check, Mute for 1 hour

- Ringer switch

  Followed. Silent still vibrates

- Repeat

  Every 5 minutes by default

### Exact acknowledgement behaviour

Acknowledging stops the repeats for that outage and nothing else. The monitor stays red, the ordinary down notification stays where it was, and recovery re-arms the loop so the next outage shouts again. Urgent overrides cooldown and the repeat toggle, because that is the point, but it still honours the master switch, the per-monitor switch, mute and the failure threshold. It means "do not let me miss this", not "ignore everything I configured".

## Four grants, and the limits that stay after them.

Android will let an app believe it is alerting you while it delivers nothing. These four switches are what stop that.

![The Nightbell setup screen, Let Nightbell wake you when something breaks, showing 2 of 4 ready: notifications and full-screen alerts granted, unrestricted battery with an Allow button and Get through Do Not Disturb with a Settings button.](https://nightbell.app/screens/grants-setup.webp)

1. ### Notifications

   Allow

   Without it nothing is posted at all.

2. ### Unrestricted battery

   Allow

   Checks keep their schedule, and an urgent page keeps repeating in your pocket.

3. ### Full-screen alerts

   Settings

   An urgent page wakes the screen instead of waiting on the lockscreen.

4. ### Get through Do Not Disturb

   Settings

   Without it, Do Not Disturb and Bedtime silence urgent pages completely.

Allow is a dialog inside the app. Settings is a trip to Android's own screen, because Android gives apps no way to flip those two, and the list re-checks itself when you come back.

15 minutes, the shortest interval Android gives a background check. Strict mode goes lower.

- Doze can still delay a 15 minute check.

  Delay is reported in Settings and never paged, because it is not an outage.

- Strict mode costs a permanent notification.

  And real battery. That is Android's price for a real cadence.

- Element checks need a page that renders.

  Slow single-page apps get about five seconds after load before an element counts as missing.

- Quiet hours hold urgent pages back.

  Unless you turn on the bypass. That default is wrong, and it is the next thing I want to fix.

CI runs the 1,237 JVM tests and a release build on every push. The 567 on-device tests are run by hand before every release.

## One APK, 2.9 MB. Signed with a key you can check.

Latest release

3.23.0

[Download APK 2.9 MB](https://nightbell.app/download) [Release notes](https://github.com/riveerxd/nightbell/releases/latest)

- File

  Nightbell-3.23.0-release.apk

- Needs

  Android 8.0+

- Licence

  Apache 2.0

- Store listing

  F-Droid

* nightbell.app Recommended The newest release the day it ships. The app checks here for the next one and installs it when you tap, with Android asking first. [Download](https://nightbell.app/download)
* F-Droid Updates later Serves my APK rather than a rebuild of it, on F-Droid's own build schedule, so it can be several releases behind this page. [Open](https://f-droid.org/en/packages/me.river.nightbell/)
* GitHub Releases Available now The APK, from the tag it was built at. [Open](https://github.com/riveerxd/nightbell/releases/latest)
* Obtainium Works today Paste the repository URL for updates without a store.
* IzzyOnDroid Not yet Not submitted. It was meant to be the fast route and F-Droid proper landed first.

From a computer, once the APK above is downloaded:

```
$ adb install -r Nightbell-3.23.0-release.apk
```

F-Droid rebuilds Nightbell from this source and publishes it only when their output matches the APK above byte for byte, so both downloads carry the same signature and either one updates the other.

F-Droid is the only store Nightbell is listed in. There is no Play listing and no other store anywhere. If you find one, it is not mine.

### Check what you downloaded

- APK SHA-256

  e87b70a4b3832fcbd6f36c09e733d052629d6b26c8fdc2f9b1030dea9463d785

- Signing certificate SHA-256

  20d8abdaa8416a9a751e3ea144ef1523d7ddbaaeee9ec6be01d63a65574a70de

- Certificate subject

  CN=Nightbell, OU=river, O=river, L=Prague, C=CZ

Signed with my key, not Google's, so Play Protect asks once. The certificate fingerprint is what says the next version came from the same person.

### Upgrading, and building it yourself

Android refuses an update signed by a different key, which is what makes the certificate fingerprint above worth checking before you trust an APK you did not get from the link on this page.

Anything from 3.0.1 onward updates in place and keeps your monitors. 3.0.0 and Pulse 2.x cannot be updated over, so the route across is the JSON export in Settings; the FAQ has the steps. Building it yourself needs JDK 17 and an Android SDK with API 36; the commands are in the [README](https://github.com/riveerxd/nightbell#readme).

## Worth asking before you install it.

[10 more answers](https://nightbell.app/faq)

### Does it keep checking when my phone is off?

No. The checks run on the phone, so a phone that is off, out of signal or in flight mode is a monitor that is not checking. That is the honest cost of having no server. If you need something watching while the phone is in a drawer, you need a machine that stays awake, and Nightbell is the wrong tool.

### Can it check more often than every 15 minutes?

Only in strict mode. Android’s PeriodicWorkRequest has a hard 15 minute minimum and WorkManager silently clamps anything shorter, so Nightbell clamps it visibly instead and says so in Settings under Checker health. Turning on strict foreground monitoring runs a foreground service that keeps your interval exactly, down to one minute. It costs a permanent notification and real battery, which is Android’s price for a real cadence and not a design choice.

### Is it on F-Droid?

Yes, since 13 August 2026, though it can run several releases behind this site, because F-Droid builds on its own schedule. It is also a reproducible build: F-Droid compiles Nightbell from this source on their own machines, compares the result against the APK attached to the GitHub release, and publishes mine only because the two match byte for byte. So the F-Droid download and the direct download carry the same signature, which means either one updates the other and you can move between them without uninstalling and losing your monitors. IzzyOnDroid is a separate repository and Nightbell is not in it.

### Why is it not on the Play Store?

There is no Play listing, and F-Droid covers the reason most people want one: a store client that installs it and keeps it updated. What keeps it off Play is the foreground service, which declares specialUse rather than dataSync, because from Android 15 dataSync is capped at six hours a day and that cap would quietly break the one guarantee strict mode exists to make. Shipping it through Play review would mean justifying the subtype. Neither F-Droid nor a direct APK is affected, and both are signed with my own key, so a direct install makes Play Protect ask you to confirm the first time.

### Does anything leave my phone?

Three things, and this answer used to say two, which was wrong for as long as the app has had an update check. First, the check itself, to the service you pointed it at. Second, a connectivity probe to a reference endpoint, so losing signal in a car park is not reported as every service going down at once: an empty 204 carrying nothing about you, pointed at GrapheneOS rather than an advertising company by default, repointable at your own always-up host, and switchable off in Settings under Checks. Third, once every six hours, the version check. It reads a small file from nightbell.app and says which version it is running, and I count those requests, because it is the only way to find out how many copies of this app exist. The log line holds a timestamp, a status and that version string. It holds no address, no identifier and no hash of either, so nothing in it can be tied to a phone or joined up across two days, and there is no per install id anywhere in the app to log even if I wanted one. Turn it off with "Tell me about new versions" in Settings, or leave it on and point it at GitHub if you would rather a stranger got the request. Beyond those three, only what you point it at yourself, such as a webhook or your sign-in provider: no analytics library and no crash reporter in the dependency list, a permission list short enough to read in one go, and site icons fetched from the monitored origin rather than through an icon proxy. Your service sees the request and your IP, the same as any other client on your network would. What never happens is anybody seeing your tokens, your URLs or your uptime history.

### Can I watch something on my home network, or over plain HTTP?

Yes. Cleartext traffic is allowed on purpose, so a box on your LAN answering on http\:// is a valid target. The catch is the same one everywhere else: the check comes from the phone, so the phone has to be able to reach the host. A monitor on a LAN address stops passing the moment you leave the house, which is a true fact about your network rather than a bug.

03:14

## Put it on call tonight.

2.9 MB, Android 8.0 and up, no account to make. Add one monitor, mark it urgent, and the next outage is a phone call instead of a footnote.

[Download APK](https://nightbell.app/download) [Get it on F-Droid](https://f-droid.org/en/packages/me.river.nightbell/)
