How to Connect to Valkey or Redis on Java over TLS/SSL
Encrypting traffic between your application and your data store is no longer optional for most teams — it is a baseline requirement for anything handling user data, and it is mandatory on nearly every managed Valkey and Redis service. The good news is that securing the connection from a Java application is far simpler than it used to be.
Both Valkey and Redis 6+ support TLS natively. That means the old workarounds — wrapping Redis in an stunnel proxy because the server "had no SSL of its own" — are obsolete. With Redisson, the Java client for Valkey and Redis, enabling an encrypted connection is a matter of configuration rather than code. This guide walks through every layer: the one-line quick start, trusting the server certificate, mutual TLS, verification modes, cluster and sentinel topologies, managed-service specifics, and how to debug the handshake errors that trip most people up.
Quick Start: The rediss:// Scheme
The fastest path to an encrypted connection is the TLS scheme. Redisson uses rediss:// for Redis over TLS and valkeys:// for Valkey over TLS (note the double s), in contrast to the plaintext redis:// and valkey:// schemes.
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.api.RMap;
import org.redisson.config.Config;
public class TlsExample {
public static void main(String[] args) {
Config config = new Config();
// rediss:// (Redis) or valkeys:// (Valkey) enables TLS
config.useSingleServer()
.setAddress("rediss://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RMap<String, String> map = redisson.getMap("test");
map.put("key", "value");
String value = map.get("key");
redisson.shutdown();
}
}
If your server presents a certificate signed by a well-known public Certificate Authority (CA) — the common case for many cloud endpoints — that is all you need. The JVM's default truststore already recognizes the CA, the handshake succeeds, and every command travels over an encrypted channel.
Things get more involved when the server uses a private CA, a self-signed certificate, or requires the client to authenticate itself. The rest of this guide covers those cases.
How TLS Works With Valkey and Redis
A quick mental model before the configuration details.
In a standard (one-way) TLS handshake, the server presents a certificate, and the client verifies it against a set of trusted CA certificates. On the Java side, those trusted certificates live in a truststore. This proves to your application that it is talking to the genuine server and not an impostor, and it encrypts everything in transit.
In mutual TLS (mTLS), the client also presents a certificate, which the server verifies. The client's certificate and private key live in a Java keystore. Many enterprise and managed deployments require mTLS so that only approved clients can connect at all.
Redisson exposes both sides through settings on the Config object, and the same settings apply across single-server, cluster, sentinel, and replicated modes.
Trusting the Server Certificate (Truststore)
When the server certificate is signed by a private or self-signed CA, the JVM won't trust it by default and the handshake fails with a path-building error. You fix this by importing the CA certificate into a truststore and pointing Redisson at it.
First, convert the CA certificate (typically a PEM file from your provider) into a Java truststore:
keytool -importcert \
-alias valkey-redis-ca \
-file ca.crt \
-keystore truststore.jks \
-storepass changeit \
-noprompt
Then reference the truststore in Redisson:
import java.io.File;
import org.redisson.config.Config;
Config config = new Config();
config.setSslTruststore(new File("truststore.jks").toURI().toURL());
config.setSslTruststorePassword("changeit");
config.useSingleServer()
.setAddress("rediss://my-server.example.com:6379");
The keystore and truststore setters take a java.net.URL, so toURI().toURL() is the convenient way to point at a file on disk. Note that toURL() declares a checked MalformedURLException, so these calls belong in a method that throws or catches it.
Redisson reads the truststore on each new connection, so certificates can be rotated and reloaded without restarting the application — a useful property in long-running services where certificate lifetimes are short.
The equivalent YAML configuration, with the TLS settings at the root Config level:
singleServerConfig:
address: "rediss://my-server.example.com:6379"
sslTruststore: "file:truststore.jks"
sslTruststorePassword: "changeit"
Mutual TLS (mTLS): Presenting a Client Certificate
If the server requires client authentication, you also need a keystore holding the client certificate and its private key. Providers usually hand these to you as PEM files; the most reliable way to package them for Java is PKCS12:
openssl pkcs12 -export \
-in client.crt \
-inkey client.key \
-name valkey-redis-client \
-out keystore.p12 \
-password pass:changeit
Then supply both the keystore (your identity) and the truststore (who you trust):
Config config = new Config();
// Server trust
config.setSslTruststore(new File("truststore.jks").toURI().toURL());
config.setSslTruststorePassword("changeit");
// Client identity for mTLS
config.setSslKeystore(new File("keystore.p12").toURI().toURL());
config.setSslKeystorePassword("changeit");
config.useSingleServer()
.setAddress("rediss://my-server.example.com:6379");
That is the whole of mTLS from the Java client's perspective. There is no manual SSLContext or SSLSocketFactory wiring to build — a common source of boilerplate and bugs with lower-level clients.
Certificate Verification and Hostname Checks
By default, Redisson performs full verification: it validates the certificate chain and checks that the hostname you connected to matches the certificate. This is controlled by sslVerificationMode, which defaults to STRICT and accepts three values:
-
STRICT— validate the full certificate chain and the hostname. This is the default and the right choice for production. -
CA_ONLY— validate the certificate chain but skip hostname verification. -
NONE— disable certificate verification entirely.
import org.redisson.config.SslVerificationMode;
config.setSslVerificationMode(SslVerificationMode.STRICT);
A word of caution: it is tempting to set NONE to make a stubborn handshake "just work," and many forum answers suggest exactly that. Treat it as a debugging step only. Disabling verification removes the protection against man-in-the-middle attacks that TLS exists to provide. If STRICT fails, the real problem is almost always a missing CA in your truststore or a hostname that doesn't match the certificate's Subject Alternative Name (SAN) — fix that rather than turning verification off.
The hostname mismatch is worth calling out specifically. If you connect by IP address but the certificate only lists DNS names in its SAN, strict verification will reject it. Connect using a hostname that appears in the certificate, or use CA_ONLY if you understand and accept the trade-off.
Tuning the Handshake: Provider, Protocols, Ciphers
For most applications the defaults are correct, but Redisson exposes the knobs you may need in regulated or performance-sensitive environments.
import org.redisson.config.SslProvider;
// JDK (default) or OPENSSL
config.setSslProvider(SslProvider.JDK);
// Restrict to modern protocol versions
config.setSslProtocols(new String[] {"TLSv1.3", "TLSv1.2"});
// Optionally pin the allowed ciphers
config.setSslCiphers(new String[] {"TLS_AES_256_GCM_SHA384"});
The OPENSSL provider is generally faster than the JDK implementation but requires the netty-tcnative-boringssl-static library on the classpath. Pinning sslProtocols to TLS 1.2 and 1.3 is a common compliance requirement and a sensible default regardless.
TLS With Cluster, Sentinel, and Replicated Modes
Because the TLS settings live on the Config object, they apply uniformly no matter which topology you use. Only the address block changes. For a cluster:
Config config = new Config();
config.setSslTruststore(new File("truststore.jks").toURI().toURL());
config.setSslTruststorePassword("changeit");
config.useClusterServers()
.addNodeAddress("rediss://node1.example.com:6379")
.addNodeAddress("rediss://node2.example.com:6379");
Sentinel deployments deserve one extra note: Redisson discovers replica and sentinel nodes dynamically, so every node a client may be redirected to must present a certificate whose SAN covers the address used to reach it. A single wildcard or multi-SAN certificate across the deployment is the cleanest way to avoid hostname-verification failures during failover.
For deeper coverage of each topology, see our guides on connecting to a cluster and connecting to Sentinel.
TLS on Managed Services
Nearly every managed offering enables TLS, and the Redisson configuration is the same in each case — supply the provider's CA in your truststore, connect with rediss:// (or valkeys://), and add a client keystore if the service requires mTLS. The provider-specific part is simply where you download the certificate:
- AWS ElastiCache / MemoryDB — enable in-transit encryption on the cluster, then connect to the TLS endpoint. See our ElastiCache client guide.
- Azure Cache — TLS is on by default; use the SSL port. See our Azure Cache guide.
- Google Cloud Memorystore — download the server CA from the instance and import it into your truststore.
- Redis Cloud / Redis Enterprise — download the CA bundle (and the client certificate/key if client authentication is enabled) from the console, then build your truststore and keystore as shown above.
Encrypting Keystore and Truststore Passwords
Storing plaintext keystore passwords in a configuration file is a weak link in an otherwise encrypted setup. Redisson PRO can encrypt those values with AES, keeping only an {aes}-prefixed ciphertext in the config and the secret key in a separate file.
java -cp redisson-all.jar org.redisson.config.PasswordCipher \
encode changeit secret_key.txt
The command outputs an {aes}... value you drop into the configuration in place of the plaintext:
singleServerConfig:
address: "rediss://my-server.example.com:6379"
sslTruststore: "file:truststore.jks"
sslTruststorePassword: "{aes}djXKclV2zFMc/tZdnntaTx2bRD3eJ1vtJSJFcBfp..."
secretKey: "file:secret_key.txt"
Full details are in our post on password encryption in Redisson.
Troubleshooting Common TLS Errors
A handful of errors account for most failed handshakes. Each maps to a concrete fix.
PKIX path building failed / unable to find valid certification path to requested target. The JVM doesn't trust the server's certificate. Import the server's CA into your truststore and reference it with sslTruststore. This is the single most common TLS error, and it almost always means a missing CA rather than a broken certificate.
No name matching <host> found / No subject alternative names matching IP address. Hostname verification failed. You connected to an address that isn't listed in the certificate's SAN — frequently because you used an IP instead of a DNS name. Connect with a covered hostname, or fall back to CA_ONLY if appropriate.
Received fatal alert: handshake_failure. The client and server couldn't agree on a protocol or cipher. Check that your sslProtocols overlap with what the server allows, and that the server actually has TLS enabled on the port you're hitting.
Connection works but seems to ignore TLS, or "plain socket" errors. You're connecting with redis:///valkey:// instead of rediss:///valkeys://, so the client is opening an unencrypted socket against a TLS-only port. Switch the scheme.
mTLS connection rejected. The server requires a client certificate you didn't supply, or the one you supplied isn't trusted by the server. Confirm sslKeystore and sslKeystorePassword are set and that the client certificate was issued by a CA the server accepts.