changedetection.io: Watch Any Web Page Change Without Telling a Third Party What You're Watching
"The list of pages you're watching is a better profile of you than your search history. With a hosted change-detector, that list belongs to somebody else."
There is a particular kind of problem that RSS can't solve: the page you care about has no feed, no API, and no notifications. A competitor's pricing page. A government notice board. A product that's been out of stock for six weeks. A job posting that might appear any day. The commercial answer is a "website change monitoring" service — Visualping, Distill, Wachete — and they work, at roughly $7–$20/month for meaningful watch counts. But there is a cost none of them advertise:
you hand them the complete list of pages you're interested in, updated in real time, which is one of the more intimate behavioral profiles that exists.
changedetection.io (GitHub:
dgtlmoon/changedetection.io) is the self-hosted answer. As of August 2026 it carries roughly
33,000 stars, ships under
Apache-2.0, and runs as a single container you control. For a blog about software you run on your own hardware, it closes an obvious gap: the last mile of web monitoring, privately.
This is the honest breakdown — how it detects changes, why the privacy argument is stronger here than people realize, what it really costs, and the security defaults you must fix before putting it anywhere near the internet.
1. What changedetection.io Is (and Isn't)
changedetection.io is a
web page change monitor. You give it a URL and it fetches that page on a schedule; when the content differs from the last snapshot, it records a diff and fires a notification through whatever channel you configured. That's the core, and it is deliberately narrow.
What it is
not: it is not an uptime monitor (that's a different question — "is it reachable" vs "did the content change"), not a scraping framework, and not a full-blown crawler. It watches specific pages you name and tells you when they move. The distinction matters because people reach for it expecting Prometheus and get a diff viewer, or vice versa.
The detection modes are what make it genuinely capable:
- Plain HTTP fetch — the cheap, fast path. Works for server-rendered pages.
- Browser automation (Playwright/Chromium) — renders JavaScript-heavy pages, runs interaction steps, and handles login-gated content. Expensive but sometimes the only option.
- Targeted filtering — CSS selectors, XPath, and JSONPath let you watch one element instead of the whole page. This is the feature that separates a useful monitor from a firehose of noise.
- JSON/API watching — point it at an endpoint and diff the payload, which is excellent for tracking internal or vendor APIs.
That filtering capability is the difference between "the page changed" and "the price dropped below $400" — and it's why the tool has staying power beyond novelty.
2. The Privacy Argument, Made Precisely
Most self-hosting pitches lean on "your data stays home." Here, that framing undersells it, because the sensitive artifact isn't the page content — it's
the watchlist.
Think about what a list of monitored URLs reveals. Watching a competitor's pricing page tells a vendor you're evaluating them. Watching a job board tells someone you're job hunting. Watching a pharmacy's stock page, a government benefits page, a landlord's listing, a specific product page — each is a small confession, and together they form a real-time map of your intentions, anxieties, and plans. Hosted change-monitoring services necessarily store that list, poll on your behalf, and can correlate it with your account.
Self-hosting inverts it entirely. The watchlist lives in a datastore on your disk. The polling requests originate from your IP. No vendor knows what you're tracking, and there's no account to correlate. It's the difference between searching for something in a private window and having someone follow you around a store taking notes.
One detail worth knowing: the app has a periodic version-check call home. You can (and should, for full privacy) disable it:
``
yaml
environment:
- DISABLE_VERSION_CHECK=true
`
That single line is the difference between "mostly local" and "genuinely private." It's the kind of small, concrete detail that separates a real deployment guide from a feature list.
3. Under the Hood: Filters, API, and Keeping Alerts Useful
The craft of running this well is filtering aggressively. A naive watch on a busy page alerts you every time a timestamp, rotation banner, or personalized element changes — and you will silence the notifications within a week, which defeats the purpose.
The discipline: watch the smallest stable fragment that answers your question. Use a CSS selector to target the price element, not the whole product page. Use JSONPath to watch one field of an API response. Reserve browser automation for pages that genuinely require JavaScript or login, because it costs far more CPU, memory, and wall-clock time than a plain fetch — and mixing a few heavy browser watches with dozens of cheap HTTP ones without separating their polling intervals is how people end up with a bottlenecked instance.
For automation at scale, the tool exposes a REST API under /api/v1/
for provisioning watches, tags, and notifications programmatically. If you're managing more than a handful of monitors, treat the API as your source of truth rather than clicking through the UI — it makes environments reproducible and auditable in a way manual config never is.
There's also a nice touch for this blog's audience: change summaries can be routed through self-hosted LLM endpoints via LiteLLM, meaning you can point it at your own Ollama, vLLM, or llama.cpp instance for AI-generated summaries of what changed — locally, with none of your watched content shipped to an AI vendor.
4. Notifications and Integrations
Alerts are the point, and the channel coverage is broad: Discord, Slack, Telegram, email, webhooks, Gotify, ntfy, and many more. Notifications can carry the diff itself, a link back to your instance (via BASE_URL
), and be templated to taste.
Because it's just webhooks underneath, it composes cleanly with the rest of a self-hosted stack. A price drop can fire an n8n workflow; a government notice can post to a private channel; a stock alert can push to your phone through a self-hosted push service. This is where "own it, don't rent it" compounds: the monitor, the automation, and the notification path can all be infrastructure you control end to end.
5. The Cost, Honestly
Software: $0, Apache-2.0. No tier, no watch-count limit, no "unlimited monitors" upsell — because there's nothing to sell.
Hosting: it's lightweight in the HTTP-only case; a $4–$6/month VPS or spare resources on an existing box is plenty. If you lean heavily on browser automation, budget more RAM — each Chromium instance is not free.
Storage: diffs and snapshots accumulate. For a personal instance with dozens of watches it's trivial; for hundreds with long history, plan disk and prune.
Your time: low to start (a container and a URL), moderate to keep quiet. The ongoing work is tuning filters so alerts stay meaningful — the maintenance cost is attention, not money.
Against commercial services at roughly $7–$20/month for comparable watch counts and frequencies, the software saving is real but modest. The actual reason to self-host is the watchlist privacy argument in section 2. Be clear about your own motivation: if it's purely financial, the hosted free tiers may genuinely be enough.
6. Honest Limitations (Read This Before You Expose It)
This project has more sharp edges than its star count suggests, and they cluster around security and maintenance:
- No authentication by default. The container comes up with an open web UI. If you expose it without adding auth, anyone who finds the URL sees your entire watchlist — which, per section 2, is exactly the data you self-hosted to protect. Put it behind a reverse proxy with authentication and TLS, always.
- A real security history. There have been SSRF vulnerabilities and arbitrary-file-read issues fixed in past releases. This is not a knock on a project that discloses and patches; it is a structural consequence of what the tool does — it fetches arbitrary URLs and can run browser automation. Treat it as untrusted-input-processing software: constrain network egress, run with least-privilege filesystem access, and don't host it on a box with access to secrets you care about.
- Bus factor of 1. Despite ~149 contributors, the project is driven primarily by one maintainer. It's actively developed (releases through July 2026, commits into August) but that concentration is a genuine continuity risk for anything business-critical.
- Browser automation is expensive. Many Playwright watches will starve a small VPS. Separate your polling pools by cost.
- Anti-bot systems will block you. Aggressive bot protection defeats headless browsers, and working around it is both unreliable and often a terms-of-service violation. Watch pages you're entitled to watch, respect
robots.txt
and rate limits, and don't use this to hammer someone's site — a monitor that behaves like an attack will get your IP banned and deserves to.
It's a change detector, not an analytics platform. No trend charts, no long-term metrics, no correlation. It answers "did this change" and little else.
The first two bullets are the ones that matter most. This is a tool whose entire value is confidentiality, and its default configuration has none. Fix that on day one.
7. Getting Started
`
yaml
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
restart: unless-stopped
volumes:
- data:/datastore
environment:
- BASE_URL=https://cd.example.com
- TZ=UTC
- DISABLE_VERSION_CHECK=true
ports:
- "5000:5000"
volumes:
data:
`
Then: (1) put authentication and TLS in front of it before you do anything else, (2) add one watch on a page you care about, (3) apply a CSS/JSON filter so only the meaningful element is watched, (4) wire one notification channel and confirm it fires, (5) only then scale up the watch count. That order — secure, filter, verify, expand — is what separates a monitor you trust from one you mute.
8. changedetection.io vs the Alternatives
| | changedetection.io | Visualping / Distill | Uptime Kuma |
|---|---|---|---|
| Question answered | did content change | did content change | is it reachable |
| License | Apache-2.0 | proprietary | MIT |
| Watchlist privacy | ✅ local | ❌ vendor-held | ✅ local |
| Cost | $0 | $7–20/mo | $0 |
| Browser automation | ✅ | ✅ | ❌ |
| Fine-grained filters | ✅ CSS/XPath/JSON | partial | ❌ |
| Self-hosted LLM summaries | ✅ (Ollama/vLLM) | ❌ | N/A |
The short version: for content-change monitoring with a watchlist you'd rather not share, changedetection.io is the clear pick. Hosted services win on convenience and lose on the privacy axis that makes the tool worth running. And note the division of labour with Uptime Kuma — the two are complements, not competitors: Kuma tells you a service is up, changedetection tells you its content moved. A mature self-hoster runs both.
9. Who Should Run It
Run it if: you track prices, stock, listings, or notice boards; you monitor competitors or vendors; you want API/JSON change detection; or — most importantly — you've realized that a hosted watchlist is a profile you'd rather not hand over.
Skip it if: you want plug-and-forget convenience and don't care about the privacy tradeoff, you need trend analytics rather than change alerts, or you aren't willing to put authentication in front of it. An unsecured instance is worse than a hosted service, because it leaks the exact data you moved to protect.
For this blog's readers — people already running Uptime Kuma, Home Assistant, Headscale, and a Docker host — changedetection.io fills the last monitoring gap. It's the piece that watches the outside world on your behalf, from inside your own network, without a single company knowing what you're interested in.
10. A Real Deployment Walkthrough
The order below is deliberate — secure first, then filter, then verify, then scale:
Step 1 — auth and TLS before anything else. Put it behind Caddy/Nginx with authentication. The container ships open by default, and an open instance exposes precisely the watchlist you self-hosted to protect.
Step 2 — one watch, aggressively filtered. Don't add the whole page. Open the page, find the element that actually matters, and target it. Watching a price? Use a CSS selector on the price element, not the product page.
Step 3 — choose the cheapest fetch mode that works. Start with plain HTTP. Only enable browser automation if the content is JavaScript-rendered or login-gated, because Playwright is dramatically more expensive per check.
Step 4 — one notification channel, verified. Configure a single webhook or chat target and force a change to confirm the alert actually arrives and renders the diff. An unverified notification config is a monitor that silently does nothing.
Step 5 — set a sane interval, then expand. A pricing page needs checking a few times a day, not every minute. Respect the site you're watching: polling aggressively is rude, gets you rate-limited or IP-banned, and may breach their terms.
Only after a single watch is quiet and trustworthy should you add twenty more. Bulk-importing a hundred unfiltered watches is the standard way to build a system you mute within a week.
11. Troubleshooting & False-Positive Tuning
The two failure modes are "it never alerts" and "it never stops alerting" — and both are configuration:
- Alerts fire constantly on a busy page. You're watching too much. Narrow the CSS/XPath/JSON filter to the one element that answers your question, and strip noise like timestamps, CSRF tokens, and rotating banners.
- It never fires even though the page changed. You're filtering too hard, or the site needs JavaScript. Loosen the selector or switch that watch to browser mode.
- Nothing happens at all in browser mode. Playwright/Chromium isn't reachable (
PLAYWRIGHT_DRIVER_URL
), or the container lacks memory. Check the logs.
The site blocks you. Anti-bot protection. Reduce frequency, set a realistic user agent, and accept that some sites simply cannot be watched — working around bot protection is unreliable and usually a terms violation.
Notifications stopped after an upgrade. Re-verify the channel; notification internals have shifted between versions. This is why step 4 exists.
Instance became slow. Too many browser-based watches polling on the same schedule. Separate heavy watches into their own polling pool and spread the intervals.
The throughline: a change monitor is only as good as its signal-to-noise ratio. Tuning filters is not busywork — it is the entire craft, and it's the difference between a tool you rely on and one you silence.
12. Watching APIs and JSON Endpoints
The most underrated use of this tool isn't web pages at all — it's JSON endpoints. Any API that returns structured data can be watched with JSONPath, and because the payload lacks the decorative churn of an HTML page, the signal-to-noise ratio is dramatically better.
Point a watch at an internal health endpoint and diff the status
field. Watch a vendor's status API for the moment a component flips from operational
. Track a price field in a JSON response, a version
string in a manifest, or a count
in an analytics summary. In each case you're watching exactly one value, which means no false positives from layout tweaks, cookie banners, or timestamps.
`
yaml
conceptual: watch one field, not the whole payload
url: https://status.vendor.example/api/v1/summary
filter: $.components[?(@.name=="API")].status
``
This is also where the tool earns its place in a professional stack rather than a hobby one. Internal services, release manifests, and third-party status APIs are all things you'd otherwise poll with a custom cron job and a diff script. changedetection.io gives you the scheduling, the history, the diffing, and the notification routing for free — and keeps the fact that you're watching them inside your own network.
One caveat: treat API watches with the same courtesy as page watches. Poll at a reasonable interval, honour rate limits, and authenticate properly. A monitor that looks like abuse will lose access to the very thing it's watching.
Related
Comments (0)
No comments yet. Be the first to comment!