Portchecks – TCP-Monitoring als kleine SaaS
Ein schlanker Monitoring-Dienst, der TCP-Ports überwacht und nur dann mailt, wenn der Zustand vom Soll abweicht. Komplett eigenes SaaS, von Datenmodell bis Stripe-Abrechnung.
Idee
Die meisten Monitoring-Tools können sehr viel und sind entsprechend laut. Ich wollte das Gegenteil: einen Dienst, der prüft, ob ein bestimmter Port offen oder geschlossen ist – und nur dann eine Mail schickt, wenn das Ergebnis nicht zum erwarteten Zustand passt. Kein Dashboard-Lärm, keine Push-Notification-Flut, keine Statusseite, die ohnehin niemand öffnet.
Daraus ist Portchecks geworden: eine kleine SaaS, mit der man Server (Label + IP), zugehörige Port-Checks und Empfänger:innen für Alerts pflegt. Der Rest passiert im Hintergrund.
Funktionsweise
- Pro Server lassen sich beliebig viele Port-Checks anlegen, jeder mit eigenem Intervall (von minütlich bis monatlich) und einem Soll-Zustand (offen oder geschlossen).
- Ein Scheduler stößt jede Minute alle fälligen Checks an. Die eigentliche Prüfung läuft als Queue-Job, damit eine langsame Probe nie das Cron-Fenster sprengt.
- Alarmiert wird nur, wenn der beobachtete Zustand vom Soll abweicht. Die Alerts gehen ausschließlich an Empfänger:innen, die den Bezug per Double-Opt-In bestätigt haben.
- Drei Tarife – Free, Solo, Business – staffeln Server-Anzahl, Checks pro Server und die zulässige Frequenz. Plan-Änderungen kommen ausschließlich über Stripe-Webhooks zurück, nie aus der App heraus.
Architektur-Highlights
- Zwei Queue-Spuren statt einer. Sub-stündliche Checks landen in einer eigenen Lane (
portchecks-minutely), alles ab stündlich teilt sich dieportchecks-Lane. So kann ein Schwall stündlicher Probes nie eine minütliche Prüfung blockieren. - Idempotente Jobs. Das
alert_sent-Flag wird erst gesetzt, wenn die Mail tatsächlich raus ist. Retries sind dadurch gefahrlos – kein einziger Doppel-Alert in den ersten Tagen Betrieb. - Cadence-basierte Retention. Eine minütliche Prüfung erzeugt rund 4.300 Datensätze pro Tag – würde man die 30 Tage halten, explodiert die Tabelle. Sub-stündliche Ergebnisse werden 3 Tage aufbewahrt, alles ab stündlich 7 Tage. Die Retention liegt im selben Enum wie die Frequenz, also genau eine Quelle der Wahrheit.
- Plan-Limits als Enum.
Plan::maxServers(),allowedIntervals(),stripePriceId()– alle Geschäftsregeln und alle Stripe-Mappings stehen an einer Stelle. UI, Validierung und Webhook-Listener nutzen genau diese Methoden.
Stack
Laravel 13, Livewire 4 mit Flux-Komponenten, Tailwind 4, SQLite in der Entwicklung, Postgres im Betrieb. Auth über Fortify inkl. 2FA, Billing über Cashier mit Mailgun für die Zustellung, Pest für die Tests.
Status
Online seit Mai 2026. Aktuell im Einsatz für meine eigenen Server und einige Pilot-Accounts. Die nächsten Schritte: HTTPS-/Status-Code-Checks, Slack- und Webhook-Notifications und ein optionaler Public-Status-Endpoint pro Server.