The Redis error "LOADING Redis is loading the dataset in memory" means the server is running but has not finished reading its data from disk, or from the master, back into RAM. Until it finishes, it rejects almost every command with this error. Nothing is broken, and the honest fix is often to wait. The work is in knowing how long, keeping health checks from killing Redis halfway through, and making the next load shorter.
$ redis-cli GET user:42
(error) LOADING Redis is loading the dataset in memory
When Redis loads, and what still works
There are two common moments:
- Server start. With persistence enabled, Redis reads the RDB file or the AOF before serving clients. In our experience a few hundred MB load in seconds, while tens of GB can take minutes; the exact time depends on disk speed, CPU and data types.
- Replica full sync. When a replica cannot continue replication incrementally, it receives a full copy from the master. With the default
repl-diskless-load disabled, it first stores the RDB on disk, then flushes its old data and loads the new file. During that load the replica answers LOADING.
The check itself is in server.c: while server.loading is set, any command without the CMD_LOADING flag is rejected with this error. Which commands carry the flag is defined in commands.def, and the list has a few surprises:
- Work during loading:
INFO,ROLE,CONFIG GET,SHUTDOWN,AUTH,HELLO,SELECT,CLIENTsubcommands such asLISTandKILL, and Pub/Sub commands. - Rejected: all normal data commands (
GET,SETand the rest), and alsoPING.PINGhas noCMD_LOADINGflag, so a health check based on it sees an error for the whole load.
Valkey works the same way. Its server.c builds the same error with the server name in it, and PING has the same flags there.
How long will it take?
Because INFO works during loading, Redis tells you its own progress:
Example output (the numbers are illustrative):
$ redis-cli INFO persistence | grep -E '^(async_)?loading'
loading:1
async_loading:0
loading_start_time:1762573620
loading_total_bytes:9663676416
loading_rdb_used_mem:0
loading_loaded_bytes:4187593113
loading_loaded_perc:43.33
loading_eta_seconds:131
The INFO documentation describes loading as the flag that a load of a dump file is ongoing. The loading_* fields appear while it runs: start time, total and loaded bytes, percentage and an ETA. The ETA in server.c is a simple extrapolation from elapsed time and bytes remaining, so treat it as an estimate. It is good enough to answer "seconds or half an hour?" when someone is waiting on you.
The log shows how long previous loads took. That is the number to plan around:
# server start
* DB loaded from disk: 228.415 seconds
* DB loaded from append only file: 311.902 seconds (AOF)
* Done loading RDB, keys loaded: ..., keys expired: ...
# replica full sync
* MASTER <-> REPLICA sync: receiving ... bytes from master to disk
* MASTER <-> REPLICA sync: Loading DB in memory
If startups keep getting slower, the dataset is growing or the disk under it is slow. Both are worth knowing before the next restart, not during it.
How health checks turn loading into an outage
This is the case that turns a few minutes of loading into hours of downtime. A liveness check that runs redis-cli PING and expects PONG fails during loading. On Kubernetes, a failed liveness probe restarts the container. The new process starts loading from the beginning, the probe fails again, and Redis never finishes. The same thing happens with any watchdog or orchestrator that restarts "unhealthy" processes.
The Kubernetes documentation has the right tool: when a startup probe is configured, liveness and readiness probes are not run until it succeeds. Give Redis a startup probe sized to its real load time, and keep the liveness check tolerant of LOADING:
startupProbe: # gives Redis time to load; liveness waits for it
exec:
command: ["sh", "-c", "redis-cli INFO persistence | grep -q '^loading:0'"]
periodSeconds: 10
failureThreshold: 90 # up to ~15 minutes; size it to your real load time
livenessProbe: # "alive" also while loading after a later restart
exec:
command: ["sh", "-c", "redis-cli PING | grep -qE 'PONG|LOADING'"]
readinessProbe: # no traffic until the data is there
exec:
command: ["sh", "-c", "redis-cli PING | grep -q PONG"]
- Startup probe waits for
loading:0. Its total budget (periodSecondstimesfailureThreshold) must be longer than your worst load time from the logs, with a margin. - Liveness accepts
LOADINGas alive. A process that answers at all is not hung. - Readiness wants
PONG, so no client traffic is routed to an instance without data.
Outside Kubernetes, use the same rule in deploy scripts: wait for loading:0 before sending traffic or restarting the next node.
#!/bin/sh
# wait until Redis has finished loading, then continue
until redis-cli INFO persistence | grep -q '^loading:0'; do
redis-cli INFO persistence | grep -E '^loading_(loaded_perc|eta_seconds)'
sleep 5
done
echo "Redis is ready"
What clients see
Client libraries treat LOADING as a separate error type. redis-py, for example, maps the LOADING prefix to BusyLoadingError, a subclass of ConnectionError, in its response parser. That makes it easy to handle on purpose: retry with backoff for a bounded time, or fall back to the primary data source if Redis is a cache. What you do not want is a tight retry loop from every app instance that hammers the server while it loads.
loading_loaded_perc first. If the number is moving, a restart throws away the progress and starts over. Only if the percentage does not move for a long time, and the log shows no progress, is something actually wrong.Making the next load shorter
- Prefer RDB-format loading. Loading an RDB is usually faster than replaying a pure AOF. If you use AOF, keep
aof-use-rdb-preamble yes, which is the default; redis.conf says the RDB format for the base file is always faster and more efficient. Our guide to RDB vs AOF persistence covers the trade-offs. - Keep the dataset honest. Load time grows with what is in memory. Keys that should have expired, forgotten big keys and unbounded collections all cost seconds at every restart.
- Fast local disk. A large RDB on slow network storage loads slowly. Checking the disk also catches the related failure in our MISCONF article, where Redis cannot write its snapshots at all.
- Serve from a replica during restarts. With Sentinel or Cluster, fail over to a loaded replica before restarting the master, instead of letting clients wait for a cold start.
- Avoid unnecessary full syncs. A replication backlog that is too small for the time a replica is disconnected forces full resyncs, and every full sync means another LOADING period on the replica.
For replicas on Redis 7.0 and newer there is one more option. With repl-diskless-load swapdb, the replica parses the new data directly from the socket while it keeps serving the current dataset. The 7.0 redis.conf notes that this does not apply when the replica cannot recognize the master as having a dataset from the same replication history, and that it needs enough memory for both copies, or you risk an OOM kill. During such a load INFO shows async_loading:1, and only commands flagged as unsafe for async loading are rejected.
# replica, Redis 7.0+: keep serving the old dataset during a full sync
repl-diskless-load swapdb
# needs memory for the old and the new dataset at the same time
Key takeaways
- LOADING means Redis is reading its data into memory after a start or a full resync. It is a waiting state, not a failure.
INFO persistenceworks during loading and shows the percentage and an ETA;PINGdoes not.- Health checks that require
PONGcan restart Redis in a loop. Use a startup probe, a LOADING-tolerant liveness check and a strict readiness check. - Never restart a Redis that is still making progress in
loading_loaded_perc. - Shorter loads come from RDB-format files, a lean dataset, fast disks, failover to replicas and fewer full syncs;
swapdbcan hide replica resyncs on 7.0+ if you have the memory.