Know before a certificate expires. Every port, every address.
tlswatch probes the certificates your servers actually serve, not the ones you think you installed. Expiry, chain of trust, hostname, legacy TLS and silent changes, with an alert before anything goes dark.
Expiring next
All certificates| Target | Days | Lifetime used |
|---|---|---|
| SIP trunk | 5 | |
| Mail relay | 22 | |
| Status page | 48 | |
| Resident portal | 61 | |
| Vault | 74 | |
| PBX Fabrikam | 340 |
Open findings
AllWhat is served, not what is installed
Checks happen over the network, the way a client sees it. Every address behind a name is probed, so the one stale node behind a load balancer is caught.
Any port, not just 443
Implicit TLS or STARTTLS on SMTP, IMAP, POP3, FTP and PostgreSQL. Mail relays, SIP trunks and PBXes get the same attention as the website.
Alerts on transitions, not on every run
A finding opens once and alerts once, with reminders, a recovery notice when it clears and acknowledgements when you already know.
More than an expiry date
A certificate can be valid and still wrong. tlswatch looks at the whole picture on every check.
Expiry with thresholds
Warning and critical days, with separate defaults for manual and ACME-managed certificates.
Chain of trust
Verified for server authentication like a browser does, against the system store, your own CA bundle, or a pinned self-signed certificate.
Hostname and signature
Does the certificate match the name you are connecting to, and is the signature algorithm still acceptable?
Legacy TLS versions
A daily handshake pinned to TLS 1.0 and 1.1. A server on TLS 1.3 can still accept them unnoticed; now it is a finding.
Change detection
A new certificate or issuer opens a finding. Let's Encrypt and other ACME renewals are recognised and stay quiet.
Inconsistent addresses
When the addresses behind one name serve different certificates, you hear about it before a customer does.
Certificate inventory
Every certificate seen, where it is served, when it expires. Exportable as CSV.
Live pages
Overview, targets and findings update as checks land. No refresh, no WebSocket, nothing to configure.
Roles and audit log
Viewer, operator and admin. Local accounts and Google sign-in. Every change is in the audit log.
The alert goes where your team already looks
Each channel has its own severity floor, so Telegram sees everything and the on-call pager only the critical ones. Mail carries a plain-text and an HTML part. Rate limits and retries are handled for you, and an optional weekly digest sums up what is due.
- Host
- sip.contoso.example:5061
- Expires
- 2026-10-20 12:00 UTC
- Issuer
- Sectigo RSA Domain Validation
- Renewal
- manual, no ACME seen
- Addresses
- 2 of 2 serve this certificate
Three steps, no YAML
Add a target
A hostname or IP, a port, a protocol. Choose the trust store, set thresholds or keep the defaults, tag it. Test without saving to see the result first.
It keeps probing
Every address behind the name, on a schedule you set, with the legacy TLS test once a day. Results land on live pages and in the inventory.
You get told once
A finding opens when something changes and alerts your channels. Acknowledge it, get a reminder, and a recovery notice when it is fixed.
Running in a minute
One binary, one SQLite file. On first start you create your admin account in the browser, or restore a backup from another installation. Everything else lives under Settings.
Docker compose
$ git clone https://github.com/sjoerd305/tlswatch $ cd tlswatch $ docker compose up --build -d $ open http://127.0.0.1:8081/
Put a reverse proxy with TLS in front before exposing it, then set the public URL under Settings › General.
Binary go 1.27
$ make build # or download a release $ ./tlswatch serve # creates ./data/ and a secret key $ open http://127.0.0.1:8080/
Listen address, database path and secret key file are environment variables; everything else is in the UI.
Try one endpoint first, no server needed
The same checks as the server, printed as JSON, with an exit code for scripts: 0 ok, 1 warning, 2 critical. A server that still accepts TLS 1.0 exits 1.
$ tlswatch check mail.example.nl:587 -protocol smtp { "target": "mail.example.nl:587", "protocol": "smtp", "status": "warning", "days_left": 22, "not_after": "2026-11-06T09:12:00Z", "issuer": "DigiCert Global G2 TLS RSA SHA256 2020 CA1", "addresses": [ { "address": "203.0.113.21", "ok": true, "tls_version": "TLS 1.3", "verified": true, "legacy_accepted": [] }, { "address": "2001:db8:c0::21", … } ], "findings": [ { "kind": "expiring", "severity": "warning", "details": "expires in 22 days" } ] } $ echo $? 1
Boring in the right places
Watch the certificates you actually serve.
MIT licensed. Source, releases and documentation on GitHub.