What is Redis JSON?
Redis JSON is a native document data type that lets you store JSON in Redis under a single key and then read or update individual fields inside it by path — without fetching, parsing, and rewriting the whole document. It supports the six JSON value types (null, boolean, number, string, object, array) and conforms to the ECMA-404 JSON interchange standard.
How Redis JSON Works
When you store a document with Redis JSON, the server does not keep it as a text blob. It parses the document once and holds it in an optimized internal tree structure. That is the entire point of the data type: because the structure is already parsed, the server can walk directly to a nested field and modify it in place.
You address fields using JSONPath, the same query syntax used by most JSON tooling:
$.user.name # a named field
$.items[0] # the first element of an array
$.items[*].price # every price in the array
$..sku # every sku at any depth
Two syntaxes are in circulation. The enhanced syntax begins with $ and supports wildcards, filter expressions, array slices, unions, and recursive descent. The older restricted syntax begins with . and is far more limited. Both are still accepted, so you will encounter both in older code and documentation — prefer the $ form for anything new.
Because updates happen in place, changing one field in a large document costs roughly what that field costs, not what the document costs. This is the property that makes Redis JSON worth using instead of serializing an object to a string.
Do You Need the Redis JSON Module?
This is the question most developers actually arrive with, and the answer changed in 2025. It now depends on which engine and version you run.
Redis 8.0 and later — built in. Redis 8 merged Redis Stack and Redis Community Edition into a single distribution, Redis Open Source. JSON ships as part of core, alongside Search, time series, and the probabilistic types. There is no module to load and no separate distribution to install. JSON.SET simply works.
Redis 7.x and earlier — module required. JSON came from the RedisJSON module, which you either loaded manually or obtained by running Redis Stack.
Valkey — module required, on every version. Valkey did not fold JSON into core. It is provided by valkey-json, an official module compatible with Valkey 8.0 and above, which you must load explicitly. It is deliberately API-compatible and RDB-compatible with RedisJSON v2, so it behaves as a drop-in replacement and existing client libraries work against it unchanged. It also ships pre-loaded in the valkey-bundle Docker image.
Managed Services
| Service | JSON support | Notes |
|---|---|---|
| AWS ElastiCache | Yes, Redis 6.2+ | Implemented natively, not as a module — INFO Modules returns nothing |
| AWS MemoryDB | Yes, Redis 6.2+ | Metrics exposed as JsonBasedCmds and JsonBasedCmdsLatency |
| Azure (Enterprise tiers) | Yes | Modules must be selected when the cache is created — they cannot be added later |
| Google Memorystore for Valkey | Yes, Valkey 8.0+ | Enabled automatically; API-compatible with JSON module v2 |
| Aiven for Valkey | Yes | Default-on for new services; existing services need a maintenance update |
The ElastiCache detail catches people out regularly: JSON commands work, but because AWS reimplemented the type rather than loading a module, any code or health check that probes INFO Modules to detect JSON support will wrongly conclude it is unavailable.
A Note on Licensing
From Redis 8, JSON is available under your choice of AGPLv3, RSALv2, or SSPLv1. Earlier versions of the RedisJSON module remain under RSALv2 or SSPLv1 only, with no AGPL option. Valkey and valkey-json are BSD-3-Clause licensed, with no tri-license arrangement — which is the practical reason some teams pick Valkey for JSON workloads.
Redis JSON Commands
| Command | Purpose |
|---|---|
JSON.SET | Write a value at a path (creates the key if absent) |
JSON.GET | Read one or more paths, optionally with formatting |
JSON.DEL | Delete the value at a path |
JSON.TYPE | Report the type of the value at a path |
JSON.NUMINCRBY | Atomically increment a number in place |
JSON.ARRAPPEND | Append one or more values to an array |
JSON.ARRINSERT | Insert values into an array before an index |
JSON.MERGE | Merge a JSON value into an existing document |
> JSON.SET user:1 $ '{"name":"Ada","visits":0,"tags":["admin"]}'
OK
> JSON.GET user:1 $.name
"[\"Ada\"]"
> JSON.NUMINCRBY user:1 $.visits 1
"[1]"
> JSON.ARRAPPEND user:1 $.tags '"beta"'
1) (integer) 2
Note that JSON.NUMINCRBY increments the counter server-side and atomically. Doing the same thing with a serialized string would mean a read, a parse, an increment, a re-serialize, and a write — five steps, plus a race condition unless you add a lock.
Redis JSON vs Hash vs Serialized String
There are three ways to put a structured object into Redis, and they are not interchangeable.
| JSON | Hash | Serialized string | |
|---|---|---|---|
| Nested data | Native | Flat only | Yes, opaque |
| Read one field | Yes | Yes | No — full read |
| Update one field | Yes, in place | Yes | No — full rewrite |
| Atomic numeric increment | JSON.NUMINCRBY | HINCRBY | Requires a lock |
| Arrays | Native | No | Yes, opaque |
| Indexing and search | Yes | Yes | No |
| Per-field TTL | No | HEXPIRE (Redis 7.4+) | No |
| Memory overhead | Higher | Lowest | Low |
| Client support | Universal since Redis 8 | Universal | Universal |
Use a Hash when your object is flat and you care about memory. A user record of ten scalar fields does not need JSON, and a Hash will cost less.
Use a serialized string when the object is always read and written whole, and you never need to touch a field independently. Cached API responses fit this well.
Use JSON when the document is nested, when you need partial reads or partial updates, or when you want to index and query by field. The cost is memory overhead; the benefit is that you stop moving whole documents across the network to change one value.
Indexing and Querying JSON Documents
JSON documents can be indexed and queried by field, which turns Redis into something closer to a document store than a cache. You define an index over a key prefix, declare which JSON paths to index and how, and then query with filters, ranges, and full-text matching rather than fetching keys one at a time.
This is what separates Redis JSON from simply having somewhere to park a document. See Redis Search for how indexing works, and How to Search Data Stored in Redis on Java for a worked Java example.
Using Redis JSON From Java
Redis speaks its own wire protocol, so Java applications need a client. Redisson maps JSON documents onto ordinary Java types, so you work with your own POJOs rather than assembling JSONPath strings by hand.
RJsonStore is the primary interface. It holds a keyed collection of JSON documents and behaves much like a Map:
RJsonStore<String, MyObject> store =
redisson.getJsonStore("users", new JacksonCodec<>(MyObject.class));
MyObject user = new MyObject();
user.setName("Ada");
store.set("1", user);
store.set("1", user, Duration.ofSeconds(100)); // with TTL
store.setIfAbsent("1", user);
MyObject loaded = store.get("1");
Map<String, MyObject> many = store.get(Set.of("1", "2"));
Serialization is handled by a codec — JacksonCodec converts between your class and JSON automatically, so no manual serialization code is required. RJsonBucket is available for a single standalone document rather than a keyed collection.
Redisson PRO adds RLocalCachedJsonStore, which keeps documents in application memory and uses a pub/sub channel to invalidate entries across instances when they change. Read operations are served locally, up to 45× faster than a network round trip — see JSON client-side caching on Java for Valkey and Redis for the configuration options.
All of these are available through synchronous, asynchronous, Reactive, and RxJava interfaces. For a full walkthrough, see How to Store JSON with Redis on Java, or the JSON Store documentation.
Technical Considerations and Implementation Pitfalls
Assuming JSON is always cheaper than a Hash. It usually is not, on memory. JSON earns its overhead through nesting and partial access, not through storage efficiency. Measure before migrating a flat structure.
Detecting support by probing for a module. On ElastiCache and MemoryDB there is no module to find, and on Redis 8 there is no module either. Test for the capability by issuing a JSON command, not by inspecting INFO Modules.
ACL changes in Redis 8. JSON and Search commands were folded into the existing ACL categories. A user granted +@read now has access to more commands than on Redis 7 — including FT.SEARCH. Review ACL rules as part of any Redis 8 upgrade.
Choosing modules too late on Azure. Modules are selected at cache creation time and cannot be added afterwards, so a cache created without JSON has to be rebuilt.
Hitting the nesting depth limit. Documents are capped on nesting depth, not just size. On valkey-json the json.max-path-limit config defaults to 128 levels, and exceeding it returns "Document path nesting limit is exceeded". Deeply recursive structures need the limit raised with CONFIG SET json.max-path-limit 256 or a flatter model.
Forgetting the key codec when indexing. If you intend to search by field, the key codec matters. Use the three-argument overload so keys are stored as plain strings the query engine can read:
RJsonStore<String, MyObject> store = redisson.getJsonStore(
"users", StringCodec.INSTANCE, new JacksonCodec<>(MyObject.class));