Skip to content

Deploy your own instance

A watch is only useful if it runs while you are asleep. Watcher separates two planes, and you can host them independently:

Plane What it is Always on?
Execution The monitoring itself (pnpm run-due) No. A short-lived, outbound-only job.
Management The API + admin panel (pnpm serve) No — if it sleeps, watches still run.

That split is why the whole thing fits in free tiers: the expensive part of hosting is a 24/7 inbound server, and the monitoring does not need one.

Section titled “Option A — scheduled CI job (recommended)”

The repository ships a scheduled workflow that runs the executor on a cron. It is the simplest way to keep watches running with no server at all.

  1. Add the secrets your run needs to the repository’s Actions secrets. At minimum:

    Secret Feeds
    GOOGLE_GENERATIVE_AI_API_KEY the evaluator, embedder and drafter
    MONGODB_URI the database
    TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID alert delivery
    TAVILY_API_KEY (optional) search sources and page extraction
  2. Check the schedule. The shipped workflow runs hourly, in UTC, and also accepts a manual run from the Actions tab with a force toggle that ignores per-watch due-filtering.

  3. Remember the resolution rule. A watch runs at the slower of its own cron and the workflow’s cron. A watch scheduled */5 * * * * under an hourly workflow is checked hourly. See Scheduling.

If you want the admin panel, the AI drafter and the REST API, run the service:

Terminal window
pnpm serve

It starts a Fastify API on port 3000 (override with PORT) plus an in-process scheduler that runs due watches on SCHEDULER_CRON (default */5 * * * *).

There is also a container image:

Terminal window
docker build -t watcher .
docker run -d --name watcher --env-file .env -p 3000:3000 watcher

GET /health works as a container health check.

Variable Required
GOOGLE_GENERATIVE_AI_API_KEY yes
MongoDB_URI yes
telegram_bot_token, telegram_chat_id for Telegram alerts
TAVILY_API_KEY for search sources

The complete list, with defaults and what reads each one, is in Environment variables.

The API requires a signed-in user. Create the first account from the CLI:

Terminal window
ADMIN_PASSWORD='a-long-random-password' pnpm users:create you@example.com --role admin --id local

Passing the password through the environment keeps it out of your shell history. --id local adopts data created before you had accounts, which is what a single-user instance wants. After that, sign in from the panel or with POST /auth/login.

  1. GET /health returns {"status":"ok"}.
  2. GET /config/status shows true for the integrations you configured. It returns booleans only and never a secret value.
  3. Trigger a manual run and confirm a run record appears in the watch’s history.