Redis Test Connection that shows every step
Why a Redis connection fails, step by step: DNS, SSH tunnel, TCP, TLS, AUTH, RESP3, SELECT. Plus command timeouts, SSH keepalives and OS trust roots.
The short version
"Could not connect" is not an error message. "TLS failed: …" is.
Test Connection on a Redis connection now walks the connection one step at a time: name lookup, tunnel or proxy, TCP, TLS, AUTH, HELLO and PING, SELECT and CLUSTER INFO. It logs each step as it finishes, and when one fails, that step heads the error. Along the way every SQLly connection got a name, RESP3, a command timeout and SSH keepalives, and TLS gained an option to trust your computer's certificate store. We also fixed two TLS bugs that turned up in the process.
Why connection errors are hard
Reaching a production Redis server often means a chain: a DNS name, an SSH bastion or a SOCKS
proxy, a TCP port, a TLS handshake against a private CA, an ACL user, a protocol negotiation and
a logical database. Any link can break, and most tools report all of them the same way, as a
timeout or a reset. read ECONNRESET can mean a TLS server got a plaintext hello, a
proxy dropped you, or a firewall cut an idle connection. You end up debugging with
openssl s_client and redis-cli in another window.
So the test does what you would do by hand, in order, and tells you how far it got.
The staged log
Press Test in the connection editor and open the diagnostic log with ⌘⇧L (Ctrl+Shift+L). With Preferences › Advanced › Log level set to Verbose you see every step. A failing step is logged at Errors only too. Each line is the step, its verdict, how long it took, and what it found.
| Step | What it tells you |
|---|---|
| Resolve | The addresses the host name resolves to (skipped behind a tunnel, whose far end resolves it). |
| Tunnel / proxy | The SSH hops, proxy or Kubernetes port-forward, and the local port it opened. |
| TCP | Whether anything accepts the connection on the port. |
| TLS | Version, cipher, server name sent, how many certificates were presented, and whether the chain verifies against what the connection trusts. This is reported even in Require mode. |
| AUTH | The user you signed in as, or that the server wants a password you didn't give. |
| HELLO / PING | RESP3 or RESP2, the product, version, mode and role, and the PING round trip. |
| SELECT | Whether the logical database is accepted (a cluster only has database 0). |
| CLUSTER INFO | Standalone server or cluster node, with state and size. It never fails the test. |
| Session | The real query session opens, startup commands included. |
Here is what a passing test through an SSH bastion to a TLS server with a private CA looks like. The line format is the one SQLly writes; the host names, timings and values are illustrative.
Resolve skipped (0 ms): cache.internal.example is resolved on the far side of the tunnel
Tunnel / proxy ok (212 ms): SSH deploy@bastion.example.com:22 → cache.internal.example:6380 open at 127.0.0.1:53122
TCP ok (1 ms): connected to the tunnel's local end 127.0.0.1:53122
TLS ok (38 ms): TLS 1.3, TLS13_AES_256_GCM_SHA384, SNI cache.internal.example, 2 certificate(s) presented; chain and host name verified against the CA file /Users/dana/certs/corp-ca.pem and the bundled public roots
AUTH ok (4 ms): signed in as app-reader
HELLO / PING ok (5 ms): RESP3 (redis 7.4.11, standalone master); PING 2.1 ms
SELECT ok (2 ms): database 3
CLUSTER INFO ok (2 ms): standalone server (cluster support disabled)
Session ok (61 ms): RESP3 session ready as sqlly:orders-cache:48213
When a step fails, the trace stops there and the connection editor's error starts with that step's name. A TLS server whose certificate was issued by a CA the connection doesn't trust (again illustrative):
Resolve ok (3 ms): cache.example.com → 10.0.4.17
Tunnel / proxy skipped (0 ms): direct connection
TCP ok (2 ms): connected to 10.0.4.17:6380
TLS FAILED (21 ms): TLS 1.3, TLS13_AES_128_GCM_SHA256, SNI cache.example.com, 1 certificate(s) presented; the certificate does not verify against the bundled public roots: invalid peer certificate: UnknownIssuer
Connection error: TLS failed: TLS 1.3, …; the certificate does not verify against the bundled public roots: …
That one line tells you the network is fine, the server speaks TLS, and the fix is a CA file or
the OS-roots option below, not a firewall ticket. A wrong password says AUTH failed:
WRONGPASS …. A non-zero database on a cluster says the cluster has only database 0 and to
clear the Database field. Behind a tunnel, a failure after the tunnel opened adds that the tunnel
accepted the connection, so the thing to check is whether the server is reachable from the far
end.
redis-test-connection-log.webp — the diagnostic log window at Verbose after Test on the TLS fixture connection, showing one line per step from Resolve to Session with the TLS line naming version, cipher, server name and the chain verdictredis-test-connection-tls-failed.webp — the connection editor after Test on the TLS fixture in Verify full with no CA file, with the error headed "TLS failed:" and naming the bundled public rootsThe same trace runs in two more places. On a Sentinel connection, Resolve asks the sentinels which master to trace. In the Cluster Topology… view, Trace runs it against every node and checks each node's certificate against the address that node announces.
How every connection now behaves
RESP3, with identical results
SQLly asks for RESP3 with HELLO 3 and stays on RESP2 when the server doesn't offer
it. RESP3 replies have different shapes (maps instead of flat arrays, doubles instead of
strings), so we made sure the grid doesn't change: hashes, configuration listings, scored ranges
and scores come out in the same rows and spellings on both protocols. A result shouldn't depend on
a negotiation you never see. SQLLY.SERVERINFO and Test Connection's server details
say which protocol a connection uses, and whether it's encrypted.
A name on every connection
Query tabs, the Profiler's MONITOR connection and the Pub/Sub viewer all run
CLIENT SETNAME with sqlly:<connection name>:<process id>,
for example sqlly:prod-cache-eu:48213. In CLIENT LIST or the Activity
Monitor's Sessions tab you can tell which connections are SQLly's, which saved connection opened them,
and which one is the Profiler. A server or ACL that refuses CLIENT SETNAME just
leaves them unnamed. On a server without RESP3, the Pub/Sub viewer's connection can't be named.
A command timeout that never re-runs anything
A command that gets no reply within the connection's Query timeout (30 seconds when blank, no limit when 0) fails with a message that names it:
HGETALL got no reply within 30 s (the connection's command timeout; set the query timeout to 0 for no limit). It may still be running on the server, so it was not retried.
Blocking commands get the wait you asked for on top, so BLPOP jobs 20 is never cut
short at 30 seconds of silence. The Profiler and Pub/Sub viewer wait without a limit, because
silence is normal there. The important decision is the last sentence of that message. A command
that timed out may still finish on the server, so SQLly never runs it again for you. An automatic
retry of INCR or LPUSH is a duplicate write.
SSH tunnels that stay up, and come back
A reported issue against Redis Insight, #6292, asks for an SSH keepalive option because idle tunnels get cut by firewalls and NAT gateways. That's a real-world problem for every tool that tunnels, so SQLly's tunnels now send an SSH keepalive after 30 seconds of silence. Set Keepalive (seconds) in the tunnel settings to change the interval, or 0 to turn keepalives off.
If the SSH session drops anyway, SQLly notices and the next run opens a fresh tunnel instead of using a dead local port. That run's Messages say:
Tunnel re-established: the SSH session had dropped, so a new tunnel was opened for this run. Nothing was re-run; if a command was running when the tunnel dropped, check whether it took effect before running it again.
Same rule as the timeout: reconnect automatically, never repeat a command automatically.
TLS: trusting what you already trust
"Also trust the OS keychain roots"
In Verify CA and Verify full, a Redis connection checks the server against the certificate authorities built into SQLly, the same list on every machine. Many companies install their own CA on employees' computers instead of handing out PEM files. Tick Also trust the OS keychain roots and the connection also trusts the macOS keychain, the Windows certificate store or the Linux CA bundle, so it verifies without a CA file. The option never loosens anything. If the store can't be read, the built-in list stays in force, and a certificate nobody trusts is still refused.
A reported issue against Redis Insight, #6472, describes Entra ID sign-in to Azure Managed Redis failing behind a TLS-inspecting proxy, which is the situation where a company CA lives in the OS store. To be precise about what our option covers: it applies to the Redis connection's own TLS. We haven't tested SQLly's Entra sign-in behind a TLS-inspecting proxy, and we haven't run against a live Azure Managed Redis, ElastiCache or MemoryDB instance yet.
Pasting rediss:// never lowers verification
Pasting a rediss:// URL turns TLS on as Require, because the scheme
says "encrypted", not "verified". If the connection already uses Verify CA or Verify full, the
paste never lowers it. A redis:// URL means plaintext. Losing TLS on the way in is
worse than losing it loudly. A reported Redis Insight issue,
#6019, describes Redis Cloud
autodiscovery importing databases without TLS, which then failed with read
ECONNRESET. SQLly's importer for Redis Insight exports always keeps the TLS settings and
certificates.
The TLS modes were checked live against a TLS-only Redis 7.4.11 server: rediss://
maps to Require; Require, Verify CA with a CA file and Verify full with a CA file all connect and
round-trip 00014; and Verify full without the CA file is refused before any command
runs.
Two bugs we fixed on the way
- A CA file replaced the built-in roots instead of adding to them. Adding your private CA to a Redis connection stopped servers with public certificates from verifying. A Redis CA file now adds to the built-in list, as it already did for PostgreSQL.
- Verify full through an SSH tunnel or proxy checked the wrong name. It checked the certificate against the tunnel's local address, so a correctly issued certificate for
cache.internal.examplefailed against127.0.0.1. It now checks the server name on the connection, and Test Connection's TLS step checks the same name.
Both are the kind of bug a staged test surfaces quickly. When the log says exactly which name was sent and which roots were used, a wrong one is hard to miss.
redis-tls-os-keychain-roots.webp — the Redis connection editor's TLS fields in Verify full with a CA file set and "Also trust the OS keychain roots" tickedLimits
- No live test yet against Azure Managed Redis, ElastiCache or MemoryDB.
- Sentinel profiles refuse SSH tunnels, proxies and relay routes.
- A passphrase-protected client key is refused with a clear message. Export an unencrypted PEM copy.
- The Profiler and the RESP2 Pub/Sub fallback don't re-authenticate Entra mid-capture.
The reference is in Redis (Connection diagnostics) and TLS modes. Download SQLly, open a Redis connection, and press Test with the log open.