Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Watchtower Laravel Package

ahmedmerza/watchtower

Ahmed Merza's Watchtower is a Laravel/PHP package for application monitoring and observability. It gives you a lightweight way to keep an eye on key events and health signals, helping you spot issues early and stay informed with simple setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Laravel Watchtower gives you a master-owned IP blocklist that satellites mirror, plus an admin page and a GET /blocks JSON endpoint reading the blacklisted_ips table.

  1. Install and publish:
composer require ahmedmerza/laravel-watchtower
php artisan vendor:publish --tag=watchtower-migrations
php artisan migrate

Upgrading to v1.1.0 adds exactly one migration, update_blacklisted_ips_table_with_list_indexes. Publish and run it on every node that holds the table — master and satellites both — not only the master.

  1. Configure the topology. Master needs no special env. Each satellite:
WATCHTOWER_MASTER_URL=https://master.example.com
WATCHTOWER_SYNC_SECRET=...   # must match what the master expects

As of v1.1.0 the secret is effectively mandatory wherever WATCHTOWER_MASTER_URL is set.

  1. Look here first:
  • php artisan route:list | grep watchtower — the management page and /blocks.
  • The package console commands: watchtower:sync (full signed paged pull) and watchtower:reconcile (drift repair).
  • The blacklisted_ips migration to understand source, scope, and expires_at.
  1. First use case: add a block via the management page, run watchtower:sync on a satellite and confirm it lands, then run watchtower:reconcile on the satellite and read the output — on a healthy node it should do nothing.

Implementation Patterns

Pull topology, not push. Satellites fetch; the master never initiates. watchtower:sync does a paged signed pull of the whole active list, so schedule it at whatever cadence matches your acceptable staleness — five to fifteen minutes is typical.

Reconcile is a repair pass, not a second sync. Since v1.1.0 it reads the master's paged list and queues a PushBlockToMaster job only for a block the master is missing or holds with an earlier expiry. A node with nothing of its own to repair doesn't contact the master at all. That makes tight scheduling safe — job volume tracks drift, not blocklist size — so pairing watchtower:reconcile (every minute) with watchtower:sync (every five) is a reasonable default.

Feeds are the bulk path. Tens of thousands of rows arrive from feed import. Set source on every imported row so the admin filter and the new (source, created_at) index stay useful, and don't route bulk data through per-row admin actions.

Reuse the page's predicates in custom tooling. The management page and /blocks sort by (created_at, id), filter by source, and expose an expired view. Any dashboard or report you add should use the same columns and ordering so it stays on the new indexes.

Schedule through the scheduler, not ad-hoc calls:

$schedule->command('watchtower:sync')->everyFiveMinutes()->withoutOverlapping();
$schedule->command('watchtower:reconcile')->everyMinute()->withoutOverlapping();

withoutOverlapping() matters on large blocklists: reconcile now reads in chunks, but a slow or unreachable master still makes a run long.

Gotchas and Tips

  • Upgrade step is additive but per-node. Publish and run update_blacklisted_ips_table_with_list_indexes on every node. Without it things still work — just at the old 15–96 ms per page on 50k rows instead of sub-millisecond. Three indexes, no backfill, no downtime.
  • Do not add an index on scope. It's deliberately unindexed: nearly every row is global, so an index on it selects almost the whole table and the planner discards it. expires_at is indexed only because the expired view matches rarely; a walk of the sort index alone was slower there.
  • The sync secret is now load-bearing on satellites. A satellite with WATCHTOWER_MASTER_URL but no WATCHTOWER_SYNC_SECRET fails the laravel target's reconcile with an explicit message. This replaces the old behaviour of queueing PushBlockToMaster jobs that each logged a warning and sent nothing — expect your failure notifications to be noisier right after the upgrade, and treat that as intended.
  • Reconcile now requires the master to be reachable. It makes the same paged signed request watchtower:sync does. A master outage that used to leave reconcile "quietly broken" now shows up as a failing run. If that's too loud, gate the schedule behind a health check rather than disabling it.
  • Drift is narrowly defined. Reconcile pushes a block the master lacks or holds with an earlier expiry. It will not shorten a longer master expiry, so don't rely on it to propagate expiry reductions — the master is authoritative for those.
  • Debugging pushes: the queued job is PushBlockToMaster; check failed_jobs and the reconcile command output before assuming the master is rejecting writes. The pre-v1.1.0 false negative — jobs queued, nothing sent, warnings only — no longer exists, so a warning now means an actual problem.
  • Chunked reads change the shape of custom code. Reconcile no longer loads the blocklist whole; if you copied the old query pattern into your own code, page it too.
  • Extension points are the columns. source drives admin filtering and the source index; scope decides whether reconcile and the sync pull consider a row node-local or global. Set both on any custom importer, or your rows will be invisible to the filters and misjudged as drift.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource