Data Serialization for Valkey & Redis on Java: Choosing the Right Redisson Codec
Redis and Valkey don't know anything about Java. They store everything as raw bytes, which means every object you cache, queue, or replicate has to be turned into bytes on the way in and rebuilt on the way out. In Redisson, the component that does this is the codec — and the codec you pick quietly decides four things at once: how fast your reads and writes are, how much memory and network they consume, whether other services can read the same data, and how exposed you are to a class of security bug that can end in remote code execution.
Most developers never change the default, and for plenty of applications that's the correct choice. This guide covers how Redisson's codec layer works, the trade-offs between the main codecs, a simple framework for choosing one, and the security pitfall worth understanding before you reach for JDK serialization.
If you want a refresher on the underlying concepts first, see our glossary entries on serialization and deserialization.
How Serialization Works in Redisson
Because Redis and Valkey aren't compatible with Java out of the box, Redisson sits between your code and the server and handles the byte conversion for you. It does this through a pluggable codec layer: a single interface with many interchangeable implementations. You can set a codec once for the entire client, or override it for an individual object.
The default codec is Kryo5Codec, so if you create a client with no extra configuration, that's what you get:
// Kryo5Codec is the default — this is the codec used when you set nothing else
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
To change the codec for the whole client, set it explicitly on the Config object:
// Switch the global codec to JSON for every object created by this client
Config config = new Config();
config.setCodec(new JsonJacksonCodec());
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
You can also assign a codec to a single object, which overrides the global default just for that object:
// Use JSON for this map only, regardless of the client-wide codec
RMap<String, Order> orders = redisson.getMap("orders", new JsonJacksonCodec());
One rule underpins everything: the same codec must be used to write and read a given object. The bytes Redisson stores carry no universal "this is JSON" marker, so if you write a value with one codec and try to read it with another, you'll get corrupt or unreadable data. Keep the codec consistent across every application that touches the same keys.
The codecs covered here live in the org.redisson.codec package, and the full list is in the data serialization reference guide.
The Default: Kryo5Codec
Kryo5Codec is a compact binary codec built on Kryo 5. It's the default because it's a sensible all-rounder: it's typically faster and more space-efficient than standard JDK serialization, it handles arbitrary Java object graphs without requiring you to define a schema, and it's compatible with Android.
For a system that's entirely on the JVM — every producer and consumer of the data is a Java service — Kryo5 is usually the right answer, and you can leave it alone.
The Main Alternatives, and When to Use Them
Redisson ships with a wide range of codecs. These are the ones worth knowing:
-
JSON (
JsonJacksonCodec) — Human-readable, debuggable, and readable by any language. It stores type information in a@classfield so it can reconstruct the exact Java type. The cost is larger payloads and slower processing than a binary codec. Reach for it when interoperability or the ability to inspect values matters more than raw performance. -
JSON without type info (
TypedJsonJacksonCodec) — A Jackson JSON codec that doesn't write the@classtype id. Useful when you want clean, portable JSON and you control the target type on the read side (also relevant to the security discussion below). -
Protobuf (
ProtobufCodec) and Avro (AvroJacksonCodec) — Schema-based binary formats. They're compact and fast like Kryo, but the schema makes them excellent for cross-language interchange and for controlled schema evolution — a good fit when your data is a contract shared across services or teams. -
Binary JSON variants (
SmileJacksonCodec,CborJacksonCodec,MsgPackJacksonCodec,IonJacksonCodec) — These keep the Jackson data model but encode it in a more compact binary form than text JSON. A middle ground between JSON's portability and binary's efficiency. -
Compression wrappers (
LZ4Codec,SnappyCodecV2,ZStdCodec) — These don't serialize objects themselves; they wrap a serialization codec and compress its output, using Kryo5 underneath by default. They trade CPU for smaller values, which pays off for large objects on memory- or network-bound systems. -
JDK serialization (
SerializationCodec) — Standard Java serialization. Mainly a compatibility option for interoperating with existing Java-serialized data. It carries security caveats covered in the next section.
Here's a rough comparison of the most common choices. Treat the speed and size columns as relative guidance and confirm with a quick benchmark on your own data:
| Codec | Format | Speed | Size | Cross-language | Human-readable | Schema evolution |
|---|---|---|---|---|---|---|
| Kryo5 (default) | Binary | Fast | Small | No | No | Manual |
| JSON (Jackson) | Text | Medium | Large | Yes | Yes | Flexible |
| Protobuf | Binary | Fast | Small | Yes | No | Strong |
| Avro | Binary | Fast | Small | Yes | No | Strong |
| JDK (SerializationCodec) | Binary | Slow | Large | No | No | Brittle |
Serialization Security: Avoiding Insecure Deserialization in Java
Codec choice isn't only about performance. Deserialization — rebuilding an object from bytes — is inherently riskier than serialization, because reconstructing an object can trigger logic driven by the incoming data. When those bytes come from an untrusted source and the codec rebuilds arbitrary Java objects, this becomes insecure deserialization, a vulnerability class OWASP groups under software and data integrity failures. In the worst case it leads to remote code execution.
It's tempting to assume a cache is trusted input, but it isn't always. If an attacker can influence the bytes your client reads — an exposed or shared instance, a compromised upstream service, or a malicious server your client is tricked into connecting to — those bytes are untrusted input that your client deserializes automatically. Redisson itself was affected by exactly this scenario in CVE-2023-42809 (fixed in version 3.22.0), where a malicious server could return crafted objects that ran arbitrary code once the client deserialized them. So the first rule is simply to run a current Redisson version.
Practical measures, roughly in order of effectiveness:
-
Keep untrusted parties away from your data. Most of this risk disappears if attackers can't write to your instance or impersonate your server. Require authentication, isolate the network, and encrypt connections — see our guides on connecting to Redis in Java (including SSL via the
rediss://protocol) and password encryption. - Prefer data-only codecs at untrusted boundaries. JSON, Protobuf, and Avro don't reconstruct arbitrary object graphs, making them a safer default for anything that might cross a trust boundary.
-
If you use
SerializationCodec, restrict the classes it will deserialize. Redisson provides a constructor that takes an explicit allow-list, so any class not on the list is rejected with anInvalidClassException:
At the JVM level, JDK serialization filtering (JEP 290) offers a similar class allow-list mechanism.// Only these classes may be deserialized; anything else is rejected Set<String> allowedClasses = Set.of( "com.example.Order", "com.example.OrderLine" ); Codec codec = new SerializationCodec( Thread.currentThread().getContextClassLoader(), allowedClasses ); Config config = new Config(); config.setCodec(codec); - Keep dependencies patched. "Gadget chains" — the class sequences attackers string together to turn deserialization into code execution — live in third-party libraries, so staying current removes the building blocks an attack relies on.
The short version: treat the codec as part of your security posture, not just a performance knob. For data that crosses a trust boundary, combine a current Redisson version, a locked-down instance, and either a data-only codec or an allow-listed SerializationCodec.
Redisson's Codecs: The Bottom Line
Redisson's codec layer gives you four levers — speed, size, interoperability, and security — and lets you pull them globally or per object. The default Kryo5Codec is a strong choice for JVM-only systems and is why most applications never need to change it. Move to JSON, Protobuf, or Avro when you need cross-language access, readability, or schema evolution; add a compression codec for large values; and where data crosses a trust boundary, reach for a data-only codec (or an allow-listed SerializationCodec) rather than one that rebuilds arbitrary Java objects.
Redisson is a high-level Java client for Valkey and Redis with over 60 distributed objects and services, and the same codec system applies across all of them. Try Redisson PRO for free or browse the documentation to go deeper.