Skip to main content

Check Types

SolidPing supports 40 check types across multiple categories for monitoring your services. Each check type has specific configuration options and validation capabilities.

Network Checks​

HTTP/HTTPS​

Monitor web services, APIs, and websites.

URL Format:

http://example.com/path
https://api.example.com/health
OptionDescriptionExample
URLThe endpoint to checkhttps://api.example.com/health
MethodHTTP methodGET, POST, PUT, DELETE, QUERY
TimeoutRequest timeout30s
Expected StatusStatus code to expect200, 2XX (wildcard)
HeadersCustom request headersAuthorization: Bearer token
BodyRequest body (for POST/PUT/PATCH/QUERY){"key": "value"}
Response assertionsAssert on the response body — see Response assertions belowbodyAssertions, json_path_assertions, body_expect
SSH tunnelDial through an SSH check's bastionAn ssh check with expected_fingerprint set
Basic AuthUsername and password — stored encrypted at restuser:password
Custom User-AgentOverride the default user-agentSolidPing/1.0

Status Code Matching:

  • Exact match: 200, 201, 404
  • Wildcard: 2XX (any 2xx status), 5XX (any 5xx status)

QUERY method: QUERY is sent with the request body attached, exactly like POST, and is treated like POST for redirects (a 307/308 re-sends the method and body; 301/302/303 behave the same as they do for any other non-GET/HEAD method).

Basic Auth storage: you still enter a username and a password in the form, but the pair is stored as a single encrypted credential (a reserved basicAuth config key) — both halves are protected, not just the password. The dashboard never gets the credential back: an existing one renders as •••• (encrypted — enter new values to replace), and editing anything else on the check leaves it (and any secret headers) untouched. To change it, retype both fields; to remove it, clear both and save. Checks written before this change keep working and fold into the new shape the next time they are saved.

Examples:

# Basic health check
url: https://api.example.com/health
method: GET
expected_status: 200
timeout: 10s

# API with authentication
url: https://api.example.com/v1/users
method: GET
headers:
Authorization: Bearer your-token
Accept: application/json
expected_status: 200
body_expect: '"users":'

# POST request
url: https://api.example.com/webhook
method: POST
headers:
Content-Type: application/json
body: '{"test": true}'
expected_status: 200

# ASP.NET Core health endpoint — a bare word as text/plain
url: https://api.acme.com/health
method: GET
expected_status_codes: ["2XX"]
bodyAssertions:
type: assertion
operator: eq
value: Healthy
ignoreCase: true

Response assertions​

A check that only looks at the status code reports a degraded service as up: plenty of health endpoints answer 200 while telling you, in the body, that they are unwell. Three assertion families cover that, and they can be combined on one check — all of them must pass for the check to be up.

Body assertions (bodyAssertions)​

Treats the response body as plain text. This is the one to reach for on a text/plain endpoint, and the only one that does exact equality.

url: https://api.acme.com/DictionaryApi/health
expected_status_codes: ["2XX"]
bodyAssertions:
type: assertion
operator: eq
value: Healthy
ignoreCase: true
FieldDescription
typeassertion for a single test, or and / or for a group with children
operatoreq, neq, contains, not_contains, regex
valueWhat to compare the body against
ignoreCasetrue compares without regard to case (and folds the regex)

Whitespace: eq and neq compare against the body with leading and trailing whitespace removed, so an endpoint returning "Healthy " still matches eq: Healthy. contains, not_contains and regex see the body exactly as it arrived.

exists, not_exists and the numeric comparisons are rejected here: a raw body always exists, so accepting them would build an assertion that can never fail.

Groups nest, so "healthy or degraded, but never containing a stack trace" is:

bodyAssertions:
type: and
children:
- type: or
children:
- { type: assertion, operator: eq, value: Healthy, ignoreCase: true }
- { type: assertion, operator: eq, value: Degraded, ignoreCase: true }
- { type: assertion, operator: not_contains, value: "at System." }
JSONPath assertions (json_path_assertions)​

Parses the body as JSON and tests one value plucked out of it with a JSONPath expression. Same node shape, plus a path, and with the full operator list: eq, neq, gt, gte, lt, lte, contains, not_contains, regex, exists, not_exists. ignoreCase applies to the textual ones.

url: https://status.acme.com/api/v2/status.json
expected_status_codes: ["2XX"]
json_path_assertions:
type: and
children:
- { type: assertion, path: "$.status.indicator", operator: eq, value: none }
- { type: assertion, path: "$.uptime", operator: gt, value: "0" }

A body that is not valid JSON fails the check rather than being skipped.

The flat matchers​

Older, simpler and still supported. They are what the importers map foreign conditions onto, so they are not going anywhere.

KeyMeaning
body_expectSubstring that must appear in the body
body_rejectSubstring that must not appear
body_patternRE2 regex the body must match
body_pattern_rejectRE2 regex the body must not match
headers_patternMap of response header name to an RE2 regex; every one must match

body_expect is a substring match, which is why bodyAssertions exists: body_expect: HEALTHY also matches a body of UNHEALTHY, and reports a dead service as up.

TCP​

Monitor TCP services like databases, message queues, and custom services.

URL Format:

tcp://hostname:port
tcps://hostname:port # With TLS
OptionDescriptionExample
HostTarget hostnamedb.example.com
PortTarget port5432
TLSEnable TLS/SSLtrue / false
TimeoutTime budget for the whole exchange: connect, TLS, send and wait for the reply10s
send_dataPayload to send once connected (after the TLS handshake for tcps)PING\r\n
send_encodingHow send_data becomes bytes: text (default), escaped, hexescaped
expect_dataSubstring the reply must contain+PONG
expect_encodingHow expect_data becomes bytes: text (default), escaped, hexhex
expect_patternRE2 regex the reply must match^220 .* ESMTP
SSH tunnelDial through an SSH check's bastion — the hostname is resolved by the bastionAn ssh check with expected_fingerprint set

UDP​

Check a UDP service by sending it something and asserting the answer.

URL Format:

udp://hostname:port
OptionDescriptionExample
HostTarget hostnamedns.example.com
PortTarget port53
TimeoutTime budget for the whole exchange: connect, send and wait for the reply10s
send_dataDatagram to send5350 0100 0001 ...
send_encodingHow send_data becomes bytes: text (default), escaped, hexhex
expect_dataSubstring the reply must contain53508180
expect_encodingHow expect_data becomes bytes: text (default), escaped, hexhex
expect_patternRE2 regex the reply must match^[\x1c\x24]
A UDP check with no expectation proves almost nothing

UDP has no handshake. A datagram sent into the void succeeds whether or not anything is listening, so a UDP check without expect_data or expect_pattern can only ever fail on an ICMP port-unreachable. Always set an expectation.

Send a payload and wait for a reply​

Completing a TCP handshake proves a firewall forwards the port, not that the service behind it works. tcp and udp checks can send a payload and require an answer — which is what actually proves the service is alive.

Redis over TCP — send PING, require +PONG:

host: redis.example.com
port: 6379
send_data: 'PING\r\n'
send_encoding: escaped
expect_pattern: '^\+PONG'

DNS over UDP — send a real query for example.com A and require an answer carrying the same transaction ID, with RCODE 0:

host: 8.8.8.8
port: 53
send_encoding: hex
send_data: "5350 0100 0001 0000 0000 0000 07 6578616d706c65 03 636f6d 00 0001 0001"
expect_encoding: hex
expect_data: "53508180"

How it behaves:

  • Encodings. text sends the string byte-for-byte (the default, so nothing stored before encodings existed changes meaning). escaped decodes the C-style escapes \r, \n, \t, \0, \\ and \xNN — the only way to express a CRLF in a form field. hex decodes hex digits, whitespace ignored.
  • Both expectations apply. expect_data and expect_pattern may both be set; the reply must satisfy both.
  • The reply is read until it matches, not once. A banner split across two segments, or an answer that arrives after a greeting, matches — up to a 4 KB cap on the reply. Matching runs on the whole buffer; the received_data output field is capped at 1 KB and rendered with \xNN escapes when the reply is not valid UTF-8.
  • Silence is a Timeout, not a Down: a port that accepts a connection and then says nothing is a distinct failure, reported as no matching reply within 10s (0 bytes received).
  • A payload with no expectation keeps its old meaning: the reply is read once for diagnostics and silence is not a failure.
  • A raw byte above 0x7F is not valid UTF-8 and cannot be written as \xNN in expect_pattern — assert it with a hex expect_data instead.

ICMP (Ping)​

Check host availability using ICMP echo requests.

URL Format:

ping://hostname
icmp://hostname
OptionDescriptionDefault
HostTarget hostname or IP-
CountNumber of packets3
IntervalTime between packets1s
TimeoutTotal timeout10s
Permissions

ICMP checks may require elevated permissions on some systems. Docker containers typically need NET_RAW capability.

DNS​

Verify DNS resolution and record values.

URL Format:

dns://resolver/domain?type=A
dns://8.8.8.8/example.com?type=MX
OptionDescriptionExample
ResolverDNS server to query8.8.8.8, 1.1.1.1
DomainDomain to resolveexample.com
TypeRecord typeA, AAAA, MX, TXT, CNAME, NS, SOA
ExpectedExpected values93.184.216.34

WebSocket​

Monitor WebSocket endpoint availability.

URL Format:

ws://hostname/path
wss://hostname/path # With TLS
OptionDescriptionExample
URLWebSocket endpointwss://api.example.com/ws
TimeoutConnection timeout10s

RDP (Remote Desktop)​

Monitor Remote Desktop Protocol servers. Unlike a plain TCP/3389 port probe, this checker performs the pre-auth RDP negotiation handshake (X.224 Connection Request/Confirm, MS-RDPBCGR): a valid answer proves the RDP listener (TermService, xrdp, …) actually parsed the request — not just that a firewall forwards the port. The handshake needs no credentials and stops before any authentication.

With username and password set, the check goes further: it completes a real interactive Windows logon — CredSSP/NLA, the full connection sequence, a settle wait until the screen stops painting, an optional screenshot, and an explicit session end (log off by default, or disconnect). See the caveats below; this mode is a user-visible logon, not a probe.

OptionDescriptionDefault
HostRDP server hostname or IP- (required)
PortTCP port3389
TimeoutCheck timeout (max 30s pre-auth, 90s with credentials)5s pre-auth, 45s with credentials
Require NLAMark down unless the server selects Network Level Authentication (CredSSP) — catches NLA silently disabled by policyoff
Cert warning (days)Mark warning when the server certificate expires in at most this many days. 0 = offoff
Cert critical (days)Mark down when the server certificate expires in at most this many days. Must be ≤ Cert warning. 0 = offoff
Username / PasswordPerform an authenticated interactive logon. Both must be set togetheroff
DomainWindows domain for the logon (optional; local accounts leave it empty)off
ScreenshotCapture a PNG of the desktop once the logon settles (authenticated runs only)off
End sessionlogoff (default) or disconnectlogoff
  • Default verdict = TCP connect + a valid X.224 Connection Confirm. A negotiation failure (RDP_NEG_FAILURE, e.g. HYBRID_REQUIRED_BY_SERVER) or a non-RDP answer yields down.
  • The negotiated security protocol (rdp, tls, nla, nla_ex, rdstls) and the server's negotiation flags are reported in the check output.
  • When a TLS-based protocol is selected, the checker completes one TLS handshake to read the server certificate (subject, issuer, expiry, self-signed flag). RDP certificates are routinely self-signed or from an internal CA, so only the leaf's expiry is inspected — the chain is deliberately not validated. An already-expired certificate is always down.
  • Authenticated failure states carry distinct codes in the check output: auth_rejected (bad password, locked account, or a domain that disabled NTLM), logon_timed_out (the desktop never settled within the timeout), and session_disconnected (the server ended the session — licensing, policy, or "another user is logged on").
  • A failed log off or screenshot after a successful logon is reported in the output details but never turns an up run down: the logon itself succeeded.
  • The check runs at most 4 RDP logons at a time per worker; a saturated worker reports a timeout on the late check rather than queueing invisibly.
Authenticated runs are real Windows logons

Every authenticated run is a real interactive Windows logon:

  • it loads a user profile and runs logon scripts / GPOs;
  • it may consume an RDS client access license;
  • on a single-session server (or with "restrict to one session per user") it can disconnect a real logged-in user;
  • it shows up in the Security event log (4624/4634) every run.

So: use a dedicated monitoring account, never a person's account, and keep the interval long — the minimum period is 15 minutes for every authenticated run, enforced by the API. Authentication is NTLM through CredSSP only; Kerberos is not supported, so domains that disable NTLM will fail at NLA with a clear error.

Network access

RDP hosts are typically reachable only from inside a network — run the check from a worker with network access to the host. The handshake is pre-auth and closes cleanly, so it generates connection events but no authentication-failure noise in Windows event logs.

Security & Certificates​

SSL/TLS Certificate​

Monitor SSL/TLS certificate expiration and validity.

OptionDescriptionExample
HostTarget hostnameexample.com
PortHTTPS port443
Warning DaysDays before expiry to warn30
Critical DaysDays before expiry to alert7

What gets checked:

  • Certificate validity
  • Expiration date
  • Chain validation
  • Hostname verification

Domain Expiration​

Monitor domain name registration expiration. Lookups go through RDAP (RFC 7480–7484), the structured, HTTPS-based successor to WHOIS, with an automatic fallback to WHOIS when RDAP can't answer (no RDAP service for the TLD, a request error, or a response with no expiration date).

OptionDescriptionExample
DomainDomain name to checkexample.com
Warning DaysDays before expiry to warn30
Critical DaysDays before expiry to alert7
Lookup method (Advanced)auto (default, RDAP first with WHOIS fallback), rdap (RDAP only, no fallback), or whois (WHOIS only)auto

The check result reports which path answered via a method field ("rdap" or "whois") alongside the existing expiry_date, days_remaining, and registrar fields.

Expiry is graduated across two tiers, exactly like the SSL/TLS check: at or below Critical Days the check goes down (pages); between Critical and Warning Days it reports warning (amber, counts as up, no incident). Existing checks that only set the legacy single threshold keep working unchanged — it's treated as Critical Days, with Warning Days defaulting to the same value.

DNSBL (DNS Blocklist)​

Check whether an IP address or hostname is listed on DNS-based blocklists (DNSBLs) such as Spamhaus, SpamCop, Barracuda, or UCEPROTECT. This is essential for monitoring the reputation of mail servers and public IPs.

OptionDescriptionDefault
TargetIPv4 address or hostname to check- (required)
BlocklistsList of DNSBL zones to queryzen.spamhaus.org, bl.spamcop.net, b.barracudacentral.org, dnsbl-1.uceprotect.net
NameserverCustom DNS resolver (host:port)system resolver
TimeoutQuery timeout (max 60s)10s

Hostnames are resolved to IPv4 before lookup. The check fails when the target is listed on at least one blocklist, and succeeds when it is clean. If every queried zone errors out, the result is inconclusive (timeout).

Error/status codes. DNSBLs reserve the 127.255.255.0/24 range for error and status replies rather than real listings — Spamhaus, for example, returns 127.255.255.254 when a query arrives via a public/open resolver (refused) and 127.255.255.255 when a rate limit is exceeded. SolidPing treats any 127.255.255.x answer as an error code, not a listing: the affected zone is reported as inconclusive and the raw code is surfaced under error_codes in the check output.

Spamhaus from cloud IPs. Because workers run on public cloud (and use the provider's shared resolvers), the public zen.spamhaus.org zone will typically be refused with 127.255.255.254. For reliable Spamhaus results, use the Spamhaus Data Query Service (DQS): configure the DQS account-key blocklist zones and a dedicated resolver via the Blocklists and Nameserver options rather than querying the public zen.spamhaus.org zone.

Database Checks​

PostgreSQL​

Monitor PostgreSQL database connectivity and query execution.

URL Format:

postgres://user:password@hostname:5432/database
OptionDescriptionExample
URLConnection stringpostgres://user:pass@db:5432/mydb
QueryOptional test querySELECT 1
TimeoutConnection timeout10s

MySQL / MariaDB​

Monitor MySQL or MariaDB database connectivity and query execution.

URL Format:

mysql://user:password@hostname:3306/database
OptionDescriptionExample
URLConnection stringmysql://user:pass@db:3306/mydb
QueryOptional test querySELECT 1
TimeoutConnection timeout10s

MongoDB​

Monitor MongoDB connectivity using the ping command.

URL Format:

mongodb://user:password@hostname:27017/database
OptionDescriptionExample
URLConnection stringmongodb://user:pass@db:27017/mydb
TimeoutConnection timeout10s

Redis​

Monitor Redis server availability using the PING command.

URL Format:

redis://hostname:6379
redis://:password@hostname:6379
OptionDescriptionExample
URLConnection stringredis://redis:6379
TimeoutConnection timeout10s

Microsoft SQL Server​

Monitor MSSQL database connectivity and query execution.

URL Format:

sqlserver://user:password@hostname:1433?database=mydb
OptionDescriptionExample
URLConnection stringsqlserver://sa:pass@db:1433?database=mydb
QueryOptional test querySELECT 1
TimeoutConnection timeout10s

Oracle Database​

Monitor Oracle database connectivity and query execution.

URL Format:

oracle://user:password@hostname:1521/service
OptionDescriptionExample
URLConnection stringoracle://user:pass@db:1521/orcl
QueryOptional test querySELECT 1 FROM DUAL
TimeoutConnection timeout10s

ClickHouse​

Monitor ClickHouse connectivity and query execution over the native (binary) protocol — not the HTTP interface — so the check exercises the same transport your analytics clients use.

OptionDescriptionExample
HostServer hostnameclickhouse.example.com
PortNative port. Defaults to 9000, or 9440 when TLS is on9000
UsernameOptional, defaults to ClickHouse's default usermonitor
PasswordOptional password
DatabaseOptional, defaults to defaultmetrics
Use TLSNative protocol over TLS (required by ClickHouse Cloud)false
Verify TLS certificateValidate the server certificate. Requires TLSfalse
QueryOptional test query, must start with SELECTSELECT 1
TimeoutConnection timeout (max 30s)10s

The check pings the server, then runs the query and reports its first cell. The server version is reported in the result output, and connection_time_ms / query_time_ms / total_time_ms are recorded as metrics.

Email Services​

SMTP​

Monitor SMTP server availability with optional authentication.

URL Format:

smtp://hostname:25
smtp://hostname:587
smtps://hostname:465 # With SSL
OptionDescriptionExample
HostSMTP serversmtp.example.com
PortSMTP port25, 587, 465
STARTTLSEnable STARTTLStrue / false
AuthTest authenticationuser:password
TimeoutConnection timeout10s

Send mode: a real probe email​

By default the SMTP check stops at the handshake (EHLO/STARTTLS/AUTH) — it proves the server is reachable and, optionally, that credentials work, but not that mail submitted to it actually gets delivered. Send mode closes that gap: on every check execution, after the normal handshake, SolidPing submits a real, system-generated email through the monitored server and addresses it to a paired Email Reception check's tokenized address.

OptionDescription
Send a probe emailEnables send mode
Mail FromEnvelope sender for the probe email — the monitored server's outbound policy (SPF/DKIM alignment, relay ACLs) usually dictates it
Delivery checkPick a paired Email Reception check; the dashboard fills in the recipient address from it

The message itself is entirely system-generated — there is no subject or body field to fill in, by design: this keeps the feature from ever becoming a way to send arbitrary mail through a monitored server. It carries two headers the Email Reception check reads to attribute and time the delivery:

X-SolidPing-Check: <the sending SMTP check's UID>
X-SolidPing-Sent-At: <RFC3339 send time>

If an intermediate mail server strips these headers, the Email Reception check still ticks up as normal — only the attribution and latency are lost.

The two checks split responsibility cleanly:

  • The SMTP check's own result reflects submission only — a 250 after DATA is up (with submission_ms recorded), any rejection is down with the server's reply.
  • The paired Email Reception check reflects end-to-end delivery — it goes down (via its normal passive-overdue logic) if probes stop arriving within its period.

Submission failures and delivery failures are different problems with different fixes, so keeping them as two checks gives each its own incident lifecycle.

Sizing the paired check's period. The Email Reception check's period is the delivery deadline. Size it generously:

email check period ≥ SMTP check interval + worst acceptable delivery time

Greylisting in particular can legitimately delay a first-time sender by several minutes — a paired check sized too tightly will flap on nothing but normal greylisting behavior.

A 60-second floor applies to send-mode SMTP checks — every execution sends a real email, so an unbounded fast interval could flood the paired inbox.

Several SMTP checks may target the same Email Reception check — the headers keep each arrival attributable to its sender — but pairing one SMTP check to one dedicated Email Reception check is the recommended setup; a shared target's up/down state otherwise conflates multiple senders. Create the Email Reception check first, then select it from the SMTP check's delivery picker — SolidPing does not offer to create one on your behalf, to keep the two checks' lifecycles independent (delete/rename either one without surprising the other).

Requires a configured inbox

Send mode requires the instance to have an email inbox configured (the same one Email Reception checks use to receive mail) — see Email Reception. The recipient address must also be at that inbox's domain; SolidPing rejects anything else.

Under the hood, the recipient address is stored on the check, not resolved at send time. Picking a delivery check in the dashboard just fills in the address for you — the same probe works identically from any worker, including a private location / deported agent, with no extra setup. One consequence worth knowing: SolidPing does not verify that the delivery check you name belongs to your organization once it's just an address — anyone who already knows another check's tokenized address could in principle aim a probe at it too. The receiving inbox domain restriction still holds absolutely (a recipient outside the instance's own inbox is never possible), and the tokenized address itself is exactly as unguessable as it always was.

IMAP​

Monitor IMAP server availability and authentication.

URL Format:

imap://hostname:143
imaps://hostname:993 # With SSL
OptionDescriptionExample
HostIMAP serverimap.example.com
PortIMAP port143, 993
TLSUse implicit TLStrue / false
STARTTLSEnable STARTTLStrue / false
TimeoutConnection timeout10s
Port 993 always means implicit TLS

When creating the check as a JSON config rather than a URL, set "tls": true explicitly alongside "port": 993 — SolidPing also derives implicit TLS automatically from port 993 if tls/starttls are both left unset, but spelling it out keeps a copy-pasted config unambiguous.

POP3​

Monitor POP3 server availability and authentication.

URL Format:

pop3://hostname:110
pop3s://hostname:995 # With SSL
OptionDescriptionExample
HostPOP3 serverpop3.example.com
PortPOP3 port110, 995
TLSUse implicit TLStrue / false
STARTTLSEnable STARTTLStrue / false
TimeoutConnection timeout10s
Port 995 always means implicit TLS

When creating the check as a JSON config rather than a URL, set "tls": true explicitly alongside "port": 995 — SolidPing also derives implicit TLS automatically from port 995 if tls/starttls are both left unset, but spelling it out keeps a copy-pasted config unambiguous.

Email Reception (Passive Inbox)​

Verify end-to-end email delivery rather than just server connectivity. SolidPing generates a unique address for the check; when a message arrives at that address, the check is marked up. This is ideal for monitoring a full sending pipeline (queue → relay → inbox).

OptionDescriptionDefault
TokenSecret part of the unique receiving addressauto-generated

The receiving domain is configured by your administrator. Point a periodic test email (or your application's "send a heartbeat" job) at the generated address, and SolidPing reports an incident if expected mail stops arriving.

Passive check

Email-reception checks are receive-only — SolidPing waits for mail instead of actively probing a server. Combine it with an SMTP check to also monitor outbound connectivity, or use SMTP send mode to have SolidPing generate and submit that probe email for you automatically.

Two kinds of result rows​

Exactly like the Heartbeat check, an email-reception check writes two kinds of row: the signal row recorded when a message actually arrives, and the scheduler evaluation row a checks worker writes every period. Evaluation rows carry evaluation: true, the region of the worker that wrote them, and lastSignalAt / lastSignalResultUid pointing at the last email received; signal rows carry none of that. The messages read Email on time, Email overdue, Last email reported failure and No email received, following the same table. Branch on evaluation, not on the message text.

Remote Access​

SSH​

Monitor SSH server availability.

URL Format:

ssh://hostname:22
OptionDescriptionExample
HostSSH serverserver.example.com
PortSSH port22
TimeoutConnection timeout10s

FTP​

Monitor FTP server availability.

URL Format:

ftp://hostname:21
OptionDescriptionExample
HostFTP serverftp.example.com
PortFTP port21
TimeoutConnection timeout10s

SFTP​

Monitor SFTP server availability.

URL Format:

sftp://hostname:22
OptionDescriptionExample
HostSFTP serversftp.example.com
PortSFTP port22
TimeoutConnection timeout10s
Host key fingerprintOptional pin on the server's host keySHA256:uNiVztks…

Pinning the host key. Leave Host key fingerprint empty and the check accepts whatever key the server presents — it is a reachability probe against a host you own. Either way, the fingerprint the server actually presented is written to the check's output as host_key_fingerprint, so you can read it off a passing check and paste it back into the field. Once set, a server presenting a different key fails the check with both fingerprints in the message, and the rejection happens during the handshake rather than after it. The value uses the same SHA256:… form as the SSH check, so ssh-keyscan host | ssh-keygen -lf - produces it.

Messaging & Streaming​

gRPC​

Monitor gRPC services using the standard health check protocol (grpc.health.v1).

URL Format:

grpc://hostname:50051
OptionDescriptionExample
HostgRPC serverapi.example.com
PortgRPC port50051
ServiceService name to check — leave empty for overall server healthmy.service.v1
TLSEncrypt the connectiontrue / false
Skip TLS verificationAccept an invalid, expired or self-signed certificatetrue / false
MetadataRequest metadata sent on every health RPC, stored in plain textx-tenant: acme
Secret metadataSame, but stored encrypted — for bearers and API keysauthorization: Bearer …
TimeoutOverall check timeout (1–30s)10s

Named service vs overall health. With no service name the check asks for the server's overall health, which every health server answers. With a service name it asks about that one service — and if the server never registered it, the check reports service "…" is not registered with the health server rather than a raw NotFound RPC error. A service that answers NOT_SERVING is reported down but still timed, so you can watch a service slow down before it drains.

TLS modes. tls: false speaks plaintext HTTP/2 (h2c). tls: true verifies the server certificate normally. tls: true with tlsSkipVerify: true encrypts the connection but accepts any certificate — useful for an internal service with a self-signed certificate, but it means the check reports up even when the certificate is invalid or expired.

Request metadata. Both maps are sent as gRPC request metadata on the health RPC, which is what lets you check a health endpoint sitting behind an authenticating proxy. metadata stays in the public, searchable configuration; secretMetadata is encrypted at rest and never returned by the API. Keys must be lowercase (gRPC lowercases them on the wire) and use only letters, digits, -, . or _; the grpc- prefix is reserved by the gRPC runtime and -bin keys (binary metadata) are not supported.

Metrics. The connection is established and timed phase by phase rather than being absorbed by the first RPC:

MetricMeaning
dns_time_msName resolution. Absent for a literal IP and for a tunneled check (which resolves on the far side of the bastion)
connect_time_msTCP connection
tls_time_msTLS handshake. Absent for a plaintext (h2c) check
rpc_time_msThe health RPC itself
total_time_msEnd to end

A failing check names the phase it died in (dns, connect, tls-handshake or rpc), so a name that does not resolve, a refused port, a broken TLS handshake and an unhealthy service are no longer the same error message.

Deprecated

The keyword / invertKeyword options match a substring of the serving-status enum (SERVING / NOT_SERVING), which the serving-status check already covers. They still work for checks that set them, but they are not offered in the dashboard and should not be used for new checks.

Kafka​

Monitor Apache Kafka broker connectivity.

URL Format:

kafka://hostname:9092
OptionDescriptionExample
BrokerKafka broker addresskafka:9092
TimeoutConnection timeout10s

RabbitMQ​

Monitor RabbitMQ, in one of two modes.

AMQP mode (the default) connects over the AMQP protocol itself and, optionally, inspects a queue's depth and consumer count:

OptionDescriptionExample
HostRabbitMQ hostnamerabbitmq.example.com
PortAMQP port5672
Username / PasswordAMQP credentialsguest / guest
Virtual HostAMQP vhost/
QueueQueue to inspect (optional)my-queue
TLSConnect over amqps://
TimeoutConnection timeout10s

Management mode talks to RabbitMQ's HTTP management API instead. It keeps the same backward-compatible up/down probe (GET /api/health/checks/alarms — down the moment RabbitMQ's own resource alarm is already active and blocking publishers), and adds an early-warning layer on top: it also reads each node's memory and disk figures from GET /api/nodes and can grade them against two-tier warning/critical thresholds — so a check can page before RabbitMQ's own watermark trips, not only once it already has.

OptionDescriptionExample
HostRabbitMQ hostnamerabbitmq.example.com
Management PortManagement API port15672
Username / PasswordManagement API credentialsguest / guest
Memory used warning/criticalCeiling on memory used: a percentage of RabbitMQ's high watermark, or a byte size80%, 1.5GiB
Disk free warning/criticalFloor on free disk: a byte size only10GiB
TimeoutConnection timeout10s

Memory accepts a percentage because RabbitMQ reports both mem_used and the high watermark (mem_limit) per node, so "80% of the watermark" is a computable ratio. Disk accepts a byte size only: the management API reports disk_free and the low watermark but never the volume's total size, so there is no denominator to compute a percentage of.

In a cluster, the worst node decides the check's status: a critical breach on any node fails the check, a warning breach (with no critical breach anywhere) puts it in the amber warning state, which counts as up and never opens an incident. Metrics are recorded on every management-mode execution, with or without thresholds configured, so a check gains memory/disk history for free.

Thresholds are only accepted in management mode — AMQP has no visibility into a node's resource usage.

MQTT​

Monitor MQTT broker connectivity via subscription.

URL Format:

mqtt://hostname:1883
mqtts://hostname:8883 # With TLS
OptionDescriptionExample
HostMQTT brokermqtt.example.com
PortMQTT port1883
TimeoutConnection timeout10s

Telephony​

SIP (VoIP)​

Monitor SIP servers used for voice/VoIP, either by checking reachability with an OPTIONS ping or by verifying that a user can register (REGISTER with digest authentication).

Address Format:

host:port
udp://host:5060
tcp://host:5060
tls://host:5061
OptionDescriptionDefault
HostSIP server hostname or IP- (required)
PortSIP port5060 (UDP/TCP), 5061 (TLS)
Transportudp, tcp, or tlsudp
Modeoptions (ping) or register (auth)options
DomainSIP domain for From/To headerssame as Host
UsernameSIP username (required for register)-
PasswordSIP password for digest auth (register)-
Expect StatusAccepted SIP status codes for OPTIONS (e.g. 200,405)-
TLS VerifyVerify the server certificate (TLS transport)false
TimeoutRequest timeout (max 60s)5s
  • OPTIONS mode succeeds when the server returns a valid SIP status code (matching expect_status if set).
  • REGISTER mode performs the standard two-step challenge/response and succeeds only on a final 200 OK.

NTP (Time Server)​

Monitor an NTP time server. Unlike a plain UDP/123 reachability probe, this checker sends a real NTP request and judges the server as a clock: it confirms the server returns a valid response and reports itself healthy (stratum, leap indicator, and root distance, via the server's own self-report — no trust in the worker's clock). Two optional, opt-in thresholds let you also alert on the measured clock offset and on the server's stratum depth.

OptionDescriptionDefault
HostNTP server hostname or IP- (required)
PortNTP UDP port123
VersionNTP protocol version (3 or 4)4
TimeoutQuery timeout (max 60s)5s
Offset warn (ms)Mark warning when the absolute clock offset exceeds this. 0 = offoff
Offset critical (ms)Mark down when the absolute clock offset exceeds this. Must be ≥ Offset warn. 0 = offoff
Max stratumMark down when the server's stratum exceeds this (1–15). 0 = offoff
  • Default verdict = reachable and the server reports a usable clock. A Kiss-o'-Death (stratum 0), an unsynchronized server (stratum 16), a LeapNotInSync leap indicator, or an out-of-range root distance all yield down.
  • Clock offset is measured relative to the worker's own clock. A worker whose clock is itself skewed will report a misleading offset, so the offset thresholds are opt-in rather than the default verdict — keep this in mind when running across a distributed worker fleet.
  • Metrics exposed: clock offset, RTT, stratum, root delay, root dispersion, root distance, poll interval, and precision.
Egress

NTP uses outbound UDP port 123, which is frequently blocked by egress firewalls. A blocked path surfaces deterministically as down/timeout.

Infrastructure​

SNMP​

Monitor devices via SNMP protocol.

OptionDescriptionExample
HostTarget deviceswitch.example.com
CommunitySNMP community stringpublic
OIDObject identifier1.3.6.1.2.1.1.1.0
VersionSNMP version2c
TimeoutConnection timeout10s

Docker​

Monitor remote Docker daemon connectivity.

URL Format:

docker://hostname:2375
OptionDescriptionExample
HostDocker daemondocker.example.com
PortDocker API port2375, 2376 (TLS)
TLSEnable TLStrue / false
TimeoutConnection timeout10s

A2S Game Server (Source / Steam)​

Query Source-engine and Steam game servers using the Valve A2S protocol (A2S_INFO).

OptionDescriptionExample
HostGame server addressgame.example.com
PortQuery port27015
TimeoutConnection timeout10s

Minecraft​

Monitor Minecraft servers (both Java and Bedrock editions), with optional player-count thresholds.

OptionDescriptionDefault
HostServer hostname or IP- (required)
PortServer port25565 (Java), 19132 (Bedrock)
Editionjava or bedrockjava
Min PlayersAlert if fewer players are online (0 = off)0
Max PlayersAlert if more players are online (0 = off)0
TimeoutQuery timeout (max 30s)10s

The check reports online players, max players, MOTD, and version. It fails if the query fails or the player count falls outside the configured bounds.

Freebox Line (xDSL / FTTH)​

Monitor the quality of a Freebox broadband line (xDSL or FTTH) through the Freebox OS API. The check connects via a stored Freebox integration connection rather than a direct address.

OptionDescriptionDefault
ConnectionReference to a freebox integration connection- (required)
Link Typexdsl or ftth- (required)
Min Sync Rate (down)Minimum downstream sync rate, kbps (xDSL)0 (off)
Min SNR Margin (down)Minimum downstream SNR margin, dB (xDSL)0 (off)
Max AttenuationMaximum downstream attenuation, dB (xDSL)0 (off)
Max CRC ErrorsMaximum CRC errors per run (xDSL)0 (off)
Min / Max RX PowerOptical receive power bounds, mW (FTTH)0 (off)

The check reports sync rates, SNR, attenuation, CRC counts (xDSL) or optical power and SFP details (FTTH), and fails when the WAN is down, the link is not trained, or any configured threshold is violated.

Kubernetes (Workload Replica Health)​

Monitor a Kubernetes workload's replica health — the structural analog of how the Docker check mirrors a container's HEALTHCHECK. The check connects via a stored Kubernetes cluster connection (an integration of type kubernetes) referenced by UID, never an inline credential.

OptionDescriptionDefault
ClusterReference to a kubernetes integration connection- (required)
NamespaceWorkload namespace- (required)
KindDeployment or ReplicaSet- (required)
NameWorkload name- (required)
TimeoutPer-execution API timeout (max 60s)10s

Status semantics (ready vs. desired replicas):

  • Up — readyReplicas == desiredReplicas and desiredReplicas > 0.
  • Warning — 0 < readyReplicas < desiredReplicas (mid-rollout or partially degraded), or desiredReplicas == 0 (intentionally scaled to zero — surfaced, not paged).
  • Down — readyReplicas == 0 with desiredReplicas > 0, a stuck rollout (Deployment Progressing=False / ProgressDeadlineExceeded), or the workload no longer exists.

Outputs include the namespace, kind, name, container images, and workload conditions; metrics include desiredReplicas, readyReplicas, availableReplicas, updatedReplicas, and unavailableReplicas.

Cluster connection​

Register a cluster once under Integrations → Kubernetes (it is a data source, not a notification channel). Three authentication modes:

  • API server + token — an API server URL plus a bearer token (typically a service-account token), optionally a CA certificate (or skip TLS verification).
  • Kubeconfig — paste a full kubeconfig that resolves to an API server and credentials.
  • In-cluster — when SolidPing itself runs as a pod in the target cluster, it uses the mounted service-account token; no credentials are stored.

The token / kubeconfig is stored encrypted (AES-256-GCM) in the connection's private settings and is never returned to the dashboard. Use Test connection to confirm the credentials work — it calls the cluster's /version endpoint.

Required RBAC​

The connection only needs read access to the monitored workloads. Bind a read-only ClusterRole to the service account whose token you register:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: solidping-readonly
rules:
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: solidping-readonly
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: solidping-readonly
subjects:
- kind: ServiceAccount
name: solidping
namespace: solidping

Kubernetes discovery (enumerating workloads automatically) builds on this same connection and additionally needs get/list on services, endpoints, and ingresses (and nodes); grant those too if you plan to use it.

Metrics​

Prometheus Metric​

Alert on a number a target reports about itself rather than on whether it answers. Every other check type answers "is it up?"; this one answers "is the value still acceptable?" — a queue depth, a free-disk gauge, an open-file-descriptor count, an error rate. It works against any endpoint that speaks the Prometheus text exposition format (most Go, Java, Python and Rust services expose one at /metrics), and against a Prometheus server itself via PromQL. No Prometheus + Alertmanager stack is required.

OptionDescriptionDefault
URLMetrics endpoint (scrape) or Prometheus server base URL (promql)- (required)
Modescrape or promqlscrape
MetricSeries name to select (scrape mode)- (required in scrape)
LabelsLabel/value pairs the series must carry (subset match, scrape mode)none
QueryPromQL instant query (promql mode)- (required in promql)
Operator>, >=, <, <=, ==, !=>
Warning ValueThreshold that yields Warning (amber, counts as up, never pages)unset
Critical ValueThreshold that yields Down (pages)unset
MatchWhat to do when several series match: single, min, max, sum, avgsingle
On MissingStatus when nothing matches: down, warning, updown
HeadersExtra request headers (bearer / basic auth)none
TimeoutPer-execution request timeout (max 60s)15s

At least one of Warning Value / Critical Value must be set. 0 is a perfectly legal threshold — "alert when free slots reach 0" is written as warningValue: 0, and it is honoured as a real threshold, not read as "unset".

Modes​

scrape fetches the URL, parses the exposition format, and picks the series named by Metric whose labels contain every configured label/value pair (extra labels on the series are fine). Histograms and summaries need no special handling: address them through the flattened series names Prometheus itself exposes — <name>_sum, <name>_count, <name>_bucket (with an le label), or <name> with a quantile label.

{
"url": "https://app.example.com/metrics",
"mode": "scrape",
"metric": "process_open_fds",
"labels": { "instance": "app-1" },
"operator": ">",
"warningValue": 800,
"criticalValue": 1000
}

promql treats the URL as a Prometheus server base URL and runs an instant query against {url}/api/v1/query. Scalar and instant-vector results are accepted; a matrix (range) result is rejected — a range returns a series of points over time, not one current value, so use an instant query.

{
"url": "https://prometheus.example.com",
"mode": "promql",
"query": "sum(rate(http_requests_total{code=~\"5..\"}[5m]))",
"operator": ">",
"warningValue": 1,
"criticalValue": 10
}

Threshold semantics​

The check fires when value <operator> threshold is true:

  • Critical breached → Down (pages, opens an incident).
  • Otherwise warning breached → Warning (amber; counts as up for availability, aggregates to Degraded, does not open an incident).
  • Otherwise Up.

A check with only a Warning Value is valid and can never page — the same contract as warningDays on the Domain and SSL checks. Use it for "I want to see this, not be woken by it".

The operator points the thresholds. For > / >= (something growing too big), Critical must be greater than or equal to Warning. For < / <= (something shrinking too far) the ordering reverses — this is the free-disk shape:

{
"url": "https://app.example.com/metrics",
"mode": "scrape",
"metric": "node_filesystem_avail_bytes",
"labels": { "mountpoint": "/" },
"operator": "<",
"warningValue": 10737418240,
"criticalValue": 2147483648
}

That check warns below 10 GiB free and pages below 2 GiB. == and != accept a Critical Value only — there is no ordering between two equality targets, so a warning tier would be meaningless and is rejected at validation time.

Multiple matching series​

By default (match: single) a selector that matches more than one series is an error result, not a silent pick — an ambiguous selector is a configuration bug, and guessing would grade an arbitrary series. Either narrow the selector with labels, or choose an explicit aggregation: min, max, sum or avg.

Missing metrics​

onMissing decides what an absent metric (or an empty PromQL vector) means. The default is down: a metric that vanished usually means the target is broken, not healthy. Use warning when the metric is expected to be absent occasionally, or up when its absence is genuinely fine.

Counters and rates​

This check reads whatever value is exposed, right now. It performs no client-side rate computation — a rate needs state between executions, which is explicitly out of scope. A raw counter (http_requests_total) only ever grows, so thresholding it directly is rarely what you want. For rates, use promql mode and let Prometheus do the work: rate(http_requests_total[5m]).

Graphing​

Every execution records the resolved value as a value metric, so the check page graphs the monitored number over time and rolls it up like any latency metric — that history is half the point of the check type.

Limits​

Scrape responses are capped at 5 MB. A larger body is refused with an explicit error naming the cap — never truncated: a half-read exposition body still parses, and would then be graded as a wrong-but-plausible value. If your /metrics output is that big, narrow the endpoint or switch to promql mode, where the query bounds the response.

Special Check Types​

Heartbeat​

Passive monitoring that expects incoming pings at regular intervals. Instead of SolidPing checking a service, the service pings SolidPing to report it's alive.

Microcontrollers and cellular modems

A device with no room for a TLS stack can push the same heartbeat over raw TCP or UDP, one line per beat, optionally HMAC-signed and carrying battery volts or RSSI as metrics. See Embedded devices (TCP/UDP).

OptionDescriptionDefault
PeriodExpected ping interval60s
GraceGrace period before incident30s

Use cases:

  • Cron job monitoring
  • Backup completion verification
  • Batch process health checks
  • IoT device connectivity

Each ping's caller metadata (User-Agent header, source IP, and HTTP method) is recorded and shown on that ping's result detail page — useful for confirming which script or host is actually pinging the check.

Two kinds of result rows​

A heartbeat check's result history interleaves two kinds of row, and they mean different things:

Beat (signal row)Scheduler evaluation
Written whenyour caller pings the checkevery period, by a checks worker
Written bythe heartbeat endpointthe scheduler
Regionnonethe worker's region
Output keysmessage, plus userAgent / remoteAddr / httpMethod / data when the caller supplied themevaluation: true, lastSignalAt, lastSignalResultUid, plus overdueBy or runStarted where they apply

evaluation: true is the reliable way to tell them apart. An ingested beat never carries it, so its absence means the row is a real signal. In the dashboard, evaluation rows carry a muted "Evaluation" badge in the Recent Results table and a "Scheduler evaluation" card — with a link to the beat they looked at — on the result detail page.

So a ping usually produces two rows within seconds of each other: your beat, and the scheduler's evaluation confirming it arrived on time. That second row having no caller metadata is expected — nothing called in at that moment.

Evaluation messages:

SituationMessage
The last beat was up and arrived within the periodHeartbeat on time
The last beat was up but is now older than the periodHeartbeat overdue
The last beat reported failure (?status=down)Last heartbeat reported failure
The last beat reported an error (?status=error)Last heartbeat reported error
A running beat is still inside the 2× period grace windowRun in progress
A running beat exceeded that windowRun started but never completed
No beat on record at allNo heartbeat received
Raw retention

The evaluation reads the newest stored beat. Raw results are kept for a limited window (24 h by default) before being rolled up, so a check that has been silent for longer than that has no beat left to point at and its evaluations read No heartbeat received — the same message a check that was never pinged gets.

The keys above are part of the documented API surface: anything reading output from GET /api/v1/orgs/{org}/results or through the MCP server (list_results, diagnose_check) sees them, and should branch on evaluation rather than on the message text.

Sending the token. The dashboard generates a ?token= URL that works everywhere, but the token can also travel as an Authorization: Bearer header instead — useful when you'd rather not put a secret in a URL (proxy and CDN access logs, shell history, Referer headers). Both forms are accepted forever; if a request supplies both, the header wins.

# Query string (works everywhere, including a bare browser tab)
curl "https://your-solidping.example.com/api/v1/heartbeat/default/my-cron-job?token=<TOKEN>"

# Authorization header (keeps the token out of logs and URLs)
curl -H "Authorization: Bearer <TOKEN>" \
"https://your-solidping.example.com/api/v1/heartbeat/default/my-cron-job"

Structured body. A JSON body's message key still becomes the ping's message exactly as before. A durationMs key — a number of milliseconds, between 0 and 604 800 000 (7 days) — becomes the result's response time, feeding the check's response-time chart just like an active probe's measured duration; an invalid value (wrong type, negative, or over the cap) is ignored for that purpose but still shown in the "Data" card below, so you can see exactly what was sent. Any other keys in the body are stored alongside it and shown in that "Data" card on the result detail page — handy for a CI run URL, commit SHA, record count, or batch ID. The body is capped at 8 KiB; malformed JSON is tolerated (the ping is still recorded with an empty message), but an over-cap body is rejected with 400.

curl -X POST -H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"message":"backup completed","durationMs":42000,"recordCount":18234,"runUrl":"https://ci.example.com/runs/512"}' \
"https://your-solidping.example.com/api/v1/heartbeat/default/my-cron-job"

Monitoring GitHub Actions​

Your nightly workflow can stop running and nobody notices. GitHub already emails you when a workflow fails, but it has no way to tell you a scheduled workflow never ran at all — and GitHub auto-disables scheduled workflows after 60 days of repository inactivity, silently. A broken cron expression, a renamed default branch, or a deleted secret can just as easily stop a schedule from firing, with zero notifications either way. This is exactly the gap a heartbeat check closes: period + grace is an assertion about absence — "if no ping arrives in time, open an incident" — which no notify-on-failure system can make.

fclairamb/solidping-action wraps the ping in one step, mapping the job's outcome to the right heartbeat status and building an actionable message (run URL, workflow name, run number, commit SHA, actor) from the github context:

- uses: fclairamb/solidping-action@v1
if: always()
with:
org: acme
check: nightly-backup
token: ${{ secrets.SOLIDPING_HEARTBEAT_TOKEN }}
status: ${{ job.status }}

status should always be ${{ job.status }}; the action maps it to SolidPing's vocabulary:

job.statusHeartbeat status
successup
failuredown
cancelledno ping sent
anything elseerror

This is for on: schedule workflows only — not push-triggered CI. A heartbeat's period is meaningful for a cron job that's expected to run every N minutes; a push-triggered job has no period, so a quiet repo with no pushes for a few days would trip the grace window and page someone for nothing. Reporting push-triggered CI failures is a different, useful feature, but it isn't this one.

JavaScript​

Custom monitoring scripts with arbitrary logic, run against a real sandboxed JS engine once per execution: log in through a form and hold a session, chain a bearer-token login into an authenticated call, or aggregate several checks into one result.

Minimum period: 30s (default 1m) — see Check Intervals.

Use cases:

  • Complex multi-step API workflows
  • Custom business logic validation
  • Conditional checks based on time or state
  • Aggregating multiple checks into one
  • Login flow verification — a js check can drive a real headless-Chrome page (fill the form, click, assert on what rendered) through its browser API; a script that opens one runs at the browser check's 1m floor

Where a credential goes, how http and http.session() work, the full solidping.*/base64/browser/console API, and full tested examples (bearer-token chaining, a cookie-jar login, a real-browser form login, Basic auth, cleanup workflows, sub-check aggregation) are on their own page: JavaScript checks →

Browser​

Headless browser-based monitoring using a real browser engine.

Minimum period: 60s (default 5m) — a headless-browser run costs several seconds, so faster periods would occupy a monitoring slot continuously. See Check Intervals.

Use cases:

  • Single-page application monitoring
  • Visual regression detection
  • JavaScript-rendered content checks

A browser check loads a page: navigate, optionally wait for a selector, optionally match a keyword. It has no click and no typing. To drive the page — fill a form, click, assert on the result — write a js check and use its browser API.

Where Chrome comes from​

The SolidPing image is distroless and deliberately ships no browser — a browser check drives a Chrome that lives outside the SolidPing process. There are two ways to give it one, and nothing is ever downloaded at runtime.

1. Remote Chrome over CDP (recommended, and the only option in containers). Point the worker at a long-lived headless Chrome speaking the Chrome DevTools Protocol:

SettingEnvironment variableMeaning
checkers.browser.cdp_urlSP_CHECKERS_BROWSER_CDP_URLWebsocket/HTTP address of the CDP endpoint, e.g. ws://browser:9222
checkers.browser.chrome_pathSP_CHECKERS_BROWSER_CHROME_PATHLocal Chrome binary for the fallback below

Each execution opens a fresh isolated (incognito) browser context and tab, torn down afterwards, so consecutive checks never share cookies, storage or a service worker. Because the browser process is already running, the measured duration no longer includes Chrome's ~1s cold start.

The expected deployment shape is a sidecar next to each checks worker — chromedp/headless-shell, pinned to a tag so every region runs the same Chrome version, reached over localhost. In Docker Compose that is an extra service; see Docker Compose installation. In Kubernetes it is a second container in the worker Pod with SP_CHECKERS_BROWSER_CDP_URL=ws://127.0.0.1:9222.

If the endpoint is unreachable, the check reports an error — never "down". Your monitored site is not implicated by your browser sidecar being down, and SolidPing will not raise a false incident for it. The browser capability (below) also drops on the next worker heartbeat.

The same sidecar now serves js checks too: a script's browser API opens its page through this endpoint, under the same slot cap and the same isolated context. Give every region that runs js checks one, or pin browser-using scripts to regions whose capability list shows browser — a js check is scheduled as a js check and is not routed to a browser-capable region for you.

2. Local Chrome binary (fallback). With no cdp_url configured, SolidPing executes a locally installed Chrome/Chromium: chrome_path if set, otherwise the usual binary names (google-chrome, chromium, chromium-browser, …). This is the zero-config path on a developer laptop and an opt-in for agents installed on a host that already has Chrome. SolidPing never downloads a browser.

Concurrency​

At most 4 browser executions run at a time per worker — a browser execution costs orders of magnitude more than a network probe, and an unbounded pool of tabs would starve the sidecar. A fifth execution waits for a slot inside its own timeout budget and reports a timeout if none frees up. A js script holding a page counts as one of the four, for as long as it holds it. Space browser checks out, or add workers, rather than lowering their period.

Region capability​

Workers self-report a browser capability (a reachable CDP endpoint, or a local binary), aggregated per region alongside ipv4/ipv6. Creating or editing a browser check in a region whose live workers report no browser produces a warning naming a region that does have one — the check is still saved and still runs. A region with no live worker, or served by an agent predating the probe, reports unknown and never warns.

Common Options​

All check types support these common options:

OptionDescriptionDefault
nameDisplay name-
descriptionDescription-
enabledEnable/disable checktrue
periodCheck interval60s
timeoutRequest timeout30s
regionsWorker regions to run fromall
incident_thresholdFailures before incident1
escalation_thresholdFailures before escalation3
recovery_thresholdSuccesses before recovery1
ipVersionAddress family to probe over (auto, ipv4, ipv6)auto

IP version​

An IPv4 check does not verify IPv6 reachability. By default a check resolves its target and probes exactly one address — whichever family it lands on first. So a dual-stack host whose IPv6 path is broken (a missing AAAA record, a firewall rule that was never added for v6, a dead v6 route, a load balancer listening on v4 only) keeps reporting up while every IPv6 user is down.

Set ipVersion to pin the check to a family, or pick it under Advanced → IP version in the dashboard:

ValueMeaning
auto (default)No constraint — probe one address, exactly as before this option existed. Unchanged behaviour for every check that does not set it.
ipv4Probe over IPv4 only. Fails if the target publishes no A record.
ipv6Probe over IPv6 only. Fails if the target publishes no AAAA record.

The family a probe actually used is reported back as the ip_version field of the result output, and shown on the check detail page — so an ipVersion: ipv6 check that reports ip_version: ipv6 is real, verified IPv6 coverage.

One check covers one family. auto means "pick one", not "probe both" — this is a deliberate difference from Better Stack, where an unset value monitors both. To cover both families, create two checks on the same target, one pinned to each; the Better Stack importer warns when a monitor relied on that default.

Supported on http, tcp, udp, icmp, ssl, ssh, smtp, imap, pop3 and dnsbl (where only auto/ipv4 are accepted — DNS blocklists are indexed by IPv4 address). The dashboard only shows the option on types that support it, driven by supportsIpVersion on /api/v1/orgs/{org}/check-types.

dns checks do not take ipVersion and reject it. For a DNS check the option could mean either "which record types to assert on" or "which transport to reach the nameserver over" — two different features. Use the dns check's own record_type (A / AAAA) to assert on records.

Tunneled checks do not take ipVersion either and reject the pair. A tunneled check is resolved and dialed on the far side of the bastion, so the address family is the tunnel's to choose — pinning it here could only ever be a claim the worker cannot honor.

When a worker has no IPv6. A check pinned to a family the worker itself cannot originate fails with an explicit error saying so — it names the worker's missing egress rather than blaming the target. If you see it, change the check's region rather than investigating your service.

SSH tunnel​

Every TCP-based check type can dial its target through an SSH check's bastion — set tunnelCheckUid to a reference to an ssh check that has expected_fingerprint set, or pick it under Advanced → Run through SSH tunnel in the dashboard. This covers the classic bastion use cases (a database or broker on a private network): http, tcp, ssl, websocket, grpc, postgresql, mysql, mssql, oracle, clickhouse, redis, mongodb, rabbitmq, kafka, mqtt, smtp, imap, pop3, and ftp. A js check can tunnel too: its http.* and socket handles are dialed through the bastion, but a sub-check of an unlisted type or browser.open() is refused rather than run from the worker's own network.

UDP- and ICMP-based types (icmp, udp, ntp, snmp, dns, dnsbl, sip, a2s) cannot tunnel — an SSH direct-tcpip forward is TCP only. The dashboard only shows the option on types that support it, driven by supportsTunnel on /api/v1/orgs/{org}/check-types.

Check Intervals​

Supported interval formats:

  • Seconds: 10s, 30s, 60s
  • Minutes: 1m, 5m, 15m
  • Hours: 1h, 6h, 24h, 168h (1 week), 336h (2 weeks), 720h (30 days)

The dashboard's interval picker offers 1 week / 2 weeks / 30 days for any check type whose MaxPeriod allows it (uncapped by default — see the table below), useful for slow-moving checks like domain expiration. Every region you select still runs the check at the full interval — see Multiple regions.

Minimum intervals​

The API enforces a minimum period per check type — both in the dashboard and on direct API calls (400 VALIDATION_ERROR naming the floor):

Check typeMinimum periodDefault period
browser60s5m
js30s1m
ssl1h6h
domain6h24h
dnsbl15m1h
All other types10s1m

Heavy check types (headless browser, custom scripts) carry higher floors because each run can occupy a monitoring slot for several seconds — a fast period would keep a runner busy full-time. Existing checks are grandfathered: a period created before a floor was raised keeps working, and the limit applies on the next edit. The check detail page shows a warning when a check occupies 50% or more of a runner slot (its duty cycle).

Recommended minimum: 30s for production.

Multiple regions​

The period applies per region: each region you select runs the check at the full interval, and SolidPing staggers the regions across the period so they don't all fire at once. A 60s check on 3 regions runs every 60 seconds in each region (roughly 20 seconds apart), for a combined detection interval of about 20 seconds — selecting more regions multiplies coverage, it does not divide the frequency.

By default the inter-region offset ("spread") is period ÷ region count. Set regionSpread (a duration, API-first) to override it — e.g. 1s to sample all regions almost simultaneously for comparative cross-region latency, or 0s to fire them together. It must satisfy 0 ≤ regionSpread < period.

Because every region executes independently, a multi-region check consumes regions × 60s ÷ period checks per minute against your plan's checks-per-minute limit (a 60s check on 3 regions counts as 3/min). The Usage page reflects this multiplier.

Best Practices​

  1. Use appropriate timeouts - Set timeouts based on expected response times
  2. Avoid excessive frequency - Sub-minute checks increase load on both SolidPing and targets
  3. Use meaningful names - Make check names descriptive for quick identification
  4. Set appropriate thresholds - Balance between noise and missing real issues
  5. Monitor from multiple regions - Use distributed workers for global services
  6. Use database checks for actual connectivity - Prefer native database checks (PostgreSQL, MySQL, Redis) over generic TCP checks for database monitoring
  7. Leverage heartbeat checks - Use heartbeat mode for monitoring cron jobs and batch processes