Skip to content
Nightbell
The Nightbell monitor list at 3:14: a green All 5 operational card, then Checkout API and Auth service with their response times.
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.

Forty seconds, start to alarm.

0:40, captions on

Read the video transcript+
  1. Most uptime alerts are one more notification you swipe away.
  2. Nightbell runs entirely on your Android phone. No server, no account, no third party.
  3. Four steps to add a monitor. Point it at anything that answers over HTTP.
  4. Or at a GitHub repository, and it reports what changed.
  5. When a check fails, Nightbell doesn’t send you something you can miss.
  6. It takes the whole screen, and the alarm loops until you acknowledge it.
  7. 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.

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 codeany 2xxa rangeany response at all

200

up

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.

GETPOSTPUTPATCHDELETEHEAD
containsdoes not containexactly equalsmatches regexJSON field equalsJSON field exists
{
  "data": {
    "items": [{
      "state": "ready"
    }]
  }
}

path data.items[0].state equals ready

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 existselement is gonetext equalstext containstext unchangedor an attribute instead of text
  1. id→
  2. data attribute→
  3. shortest CSS path→
  4. XPath→
  5. text fingerprint

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.

starsissues and pull requestscommentsreleasesdownloads
riveerxd/nightbell
  1. New release on riveerxd/nightbellv3.18.0 Nightbell 3.18.0
  2. New issue on riveerxd/nightbell#20 [Feature] Immediate retries
  3. New issue on riveerxd/nightbell#14 [Feature] Query an Alertmanager (Prometheus/Mimir)

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.

aboveat leastbelowat mostequalsnot equal
Prometheus, Alertmanager
PromQL
sum(rate(errors_total[5m]))
0.42below 1up
Alertmanager
severity="critical"
Nothing firingup
Metrics
up{job="checkout"}
0equals 1down

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.

updegradeddown
noneheadersbasicsingle sign-on

1 down, 1 degraded, 1 up, 1 expected

Paymentsdown

Failed card payments, last 5m

  1. 13:58:00errorGateway declined payment
  2. 14:01:40warnRetry queue above 100
  • Email queuedegraded340 waiting, usually under 50
  • Databaseup12 ms
  • Sandbox gatewayexpectedDown on purpose. Pages again if that changes

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 packetsIPv4IPv6either

ping 192.0.2.10

  1. seq 115.3 ms
  2. seq 216.0 ms
  3. seq 316.7 ms

3 of 3 replies, none lostup

fastest 15.3, average 16.0, slowest 16.7

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 SSH443 HTTPS3306 MySQL5432 Postgresany port
  • 192.0.2.10:22SSHupconnected in 18 ms
  • 192.0.2.10:443HTTPSupconnected in 17 ms
  • 192.0.2.10:5432Postgresdownconnection refused

The host answers ping, but port 5432 is down. The network is fine; look at the service.

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.

DNSNTPSTUNSNMPSIPHTTP/3game serversyour own bytessilent
  • 192.0.2.10:53/udpDNSupNOERROR, 1 answer, 0.41 ms
  • 192.0.2.10:123/udpNTPupStratum 2, offset +3 ms, 18.2 ms
  • 192.0.2.10:51820/udpWireGuardupNot refused, host answers ping

WireGuard never answers a stranger, so silent mode only knows the port was not refused and the host is there.

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.

TLSSTARTTLS
every messagesender hassubject hasskip read

[email protected] on mx1.example.eu

  • Monitoralertweb-3 is DOWN
  • Ann NováknewInvoice for October
  • Bobread alreadyLunch on Friday?

Two notifications, one of them loud. The message read on the laptop first is listed and not announced.

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.
The same widget dragged wider, the six monitors spilling into two columns with a checked just now line underneath.
The same widget dragged flat into a single wide row, showing the three worst monitors and nothing else.

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.

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 checkrequest and responsepage elementGitHub repositoryPrometheushealth endpointpingTCP portUDP portmailbox

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.

    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.

    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.

    +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.
  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.

File
Nightbell-3.23.0-release.apk
Needs
Android 8.0+
Licence
Apache 2.0
Store listing
F-Droid
  • nightbell.appRecommendedThe 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
  • F-DroidUpdates laterServes 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
  • GitHub ReleasesAvailable nowThe APK, from the tag it was built at.Open
  • ObtainiumWorks todayPaste the repository URL for updates without a store.
  • IzzyOnDroidNot yetNot 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.

Worth asking before you install it.

10 more answers
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.