The correct way to close a Redis server is to ask it to stop: redis-cli SHUTDOWN SAVE if you are talking to it directly, sudo systemctl stop redis if it was installed as a service, docker stop if it is a container, or brew services stop redis on a Mac. All four send the same graceful shutdown sequence, which writes the dataset to disk, flushes the append-only file, closes client connections and exits cleanly. The command to avoid is kill -9, which skips all of that and discards every write since the last save. If you only remember one thing, remember that Redis keeps your data in memory and the shutdown is the moment it gets written down.
This looks like a trivial task and it is, right up until the process was holding an hour of session data or a queue of jobs that had not yet been persisted. The mechanics below take a minute; the reason to follow them is that Redis is a memory store with optional persistence, and a careless stop is a data-loss event.
Table of contents
- What a graceful shutdown actually does
- Method one: redis-cli SHUTDOWN
- Method two: the service manager
- Method three: Docker and Compose
- On a Mac and on Windows
- Why kill -9 is the one to avoid
- Confirming it stopped, and restarting
- How this fits the rest of the stack
- FAQ
What a graceful shutdown actually does
When Redis receives a shutdown request, whether from the SHUTDOWN command or from a SIGTERM, it runs through a fixed sequence:
- Stops accepting new client connections.
- If it is a replica, waits briefly for the primary to acknowledge; if it is a primary, gives replicas a moment to catch up.
- If RDB persistence is enabled, or
SAVEwas requested, writes a snapshot of the entire dataset todump.rdb. - If AOF is enabled, flushes the append-only file to disk with
fsync. - Removes the PID file, closes the listening socket, and exits with status 0.
Every method in this post ends up here. The differences are only about who sends the signal and what happens afterwards, such as whether a service manager restarts it.
Method one: redis-cli SHUTDOWN
Talking to the server directly is the most explicit route and the one that lets you choose the persistence behaviour.
# save a snapshot, then stop -- the production default
redis-cli SHUTDOWN SAVE
# stop without writing a snapshot (persistence disabled, or you have a reason)
redis-cli SHUTDOWN NOSAVE
# remote or password-protected instance
redis-cli -h 10.0.0.5 -p 6379 -a "$REDIS_PASSWORD" SHUTDOWN SAVE
The options matter. SAVE forces an RDB snapshot even if snapshots are turned off in the config, which is the safe choice when you are not sure. NOSAVE skips it, which is right for a pure cache where the data is disposable and a slow snapshot would delay the stop. Newer versions add NOW to skip the replica grace period, FORCE to ignore a failed save or an in-progress AOF rewrite, and ABORT to cancel a shutdown that is waiting on replicas.
One thing catches people out: if the snapshot fails, for example because the disk is full, plain SHUTDOWN refuses to exit and returns an error rather than losing data. That is the correct behaviour. Fix the disk or use SHUTDOWN NOSAVE deliberately, but do not reach for kill -9 at that point.
Method two: the service manager
On a Linux server where Redis came from the distribution’s package, it runs under systemd and the service manager owns its lifecycle. Stopping through it means the manager knows the service is down and will not try to restart it.
sudo systemctl stop redis-server # Debian and Ubuntu
sudo systemctl stop redis # RHEL, Fedora, Arch
# confirm
systemctl status redis-server
redis-cli ping # should fail to connect
The unit name varies between distributions, and unit not loaded means you have the wrong one; systemctl list-units | grep -i redis finds it. Behind the scenes systemd sends SIGTERM, which Redis handles as a graceful shutdown with whatever persistence the config specifies, waits for the process to exit, and escalates to SIGKILL only if it has not stopped within the unit’s timeout. If your dataset is large and the snapshot takes longer than that timeout, raise TimeoutStopSec in the unit rather than accepting the kill.
If you stop it this way and it comes back a few seconds later, something else is managing it: a supervisor, a container orchestrator, or Restart=always in a unit that someone also enabled a second copy of. Stop the manager, not the process.
Method three: Docker and Compose
A containerised Redis stops when the container stops, and docker stop sends SIGTERM to the process, waits ten seconds by default, and then sends SIGKILL. For a large dataset that ten seconds is not enough to write a snapshot, and the kill is exactly the data loss you were avoiding.
# give the snapshot time to complete
docker stop -t 60 redis
# compose
docker compose stop redis
# or ask Redis itself, which is the most reliable
docker exec redis redis-cli SHUTDOWN SAVE
Two container-specific traps. First, if the image’s entrypoint runs Redis through a shell without exec, the signal reaches the shell and not Redis, and the process is killed after the timeout every time; check with docker top redis that PID 1 is redis-server. Second, docker rm -f and docker compose down on a container without a volume mounted at /data deletes the snapshot along with the container. The stop can be perfect and the data still gone.
On a Mac and on Windows
With Homebrew, brew services stop redis stops the launchd-managed instance; redis-cli SHUTDOWN works too but launchd may restart it if the service is set to keep alive. If you started Redis in a terminal with redis-server, Ctrl+C in that terminal sends the interrupt and Redis shuts down gracefully, saving first if configured. On Windows, Redis is typically running in WSL or in Docker, and the Linux instructions apply inside either.
Why kill -9 is the one to avoid
kill -9 sends SIGKILL, which the kernel delivers without giving the process a chance to run any code. Redis does not get to write the snapshot, does not get to fsync the AOF, and does not get to tell replicas anything. Everything written since the last successful save is gone, and with the default snapshot policy that can be anything from one to fifteen minutes of writes, or all of them if snapshots were never configured.
The graceful signal is kill -TERM <pid> or plain kill <pid>, which is identical to what systemd and Docker send and what SHUTDOWN does internally. The only time SIGKILL is appropriate is when the process is genuinely hung and unresponsive to SIGTERM, and even then, check whether it is simply busy writing a large snapshot before deciding it is hung.
Confirming it stopped, and restarting
redis-cli ping # Could not connect -- good
pgrep -a redis-server # no output -- good
ss -ltnp | grep 6379 # nothing listening -- good
# start again
sudo systemctl start redis-server
docker start redis
brew services start redis
If ping still returns PONG after a stop, you are talking to a different instance than the one you stopped, usually a second copy on another port or a container that was restarted by its orchestrator. redis-cli INFO server shows the port, the PID and the config file of whichever one answered, which settles it.
How this fits the rest of the stack
A Redis instance is usually the cache or the queue beside a service and a relational database, and the reason its shutdown needs care is that it is holding state the rest of the stack depends on. RunxBuild does not offer managed Redis; the managed databases are MySQL and Postgres. A Docker service running Redis with a volume attached is the equivalent of the container recipe above, with the deploy logs and the restart behaviour visible in the dashboard, and the RunxBuild hosting calculator puts that container next to the service and the database that use it so the whole stack is priced together.
Useful related references:
- Redis vs DynamoDB: Cache, Database, or Both
- ioredis vs node-redis: Which to Ship With
- Redis Pipeline: How to Batch Commands, When to Use It, and Why It Is Not the Same as a Transaction
- Databases on RunxBuild
FAQ
What is the safest command to close a Redis server?
redis-cli SHUTDOWN SAVE. It forces a snapshot of the dataset to disk, flushes the append-only file, closes client connections and exits. Use sudo systemctl stop redis-server or docker stop -t 60 redis when the process is managed by systemd or Docker; both send the same graceful signal.
What is the difference between SHUTDOWN SAVE and SHUTDOWN NOSAVE?
SAVE writes an RDB snapshot before exiting even if snapshots are disabled in the config. NOSAVE skips the snapshot. Use SAVE unless the instance is a disposable cache and a slow snapshot would delay the stop for no benefit.
Why does SHUTDOWN return an error and refuse to stop?
Because the snapshot failed, typically due to a full disk or a permissions problem on the data directory, and Redis will not exit while it cannot persist the data. Fix the underlying problem or, if the data is disposable, use SHUTDOWN NOSAVE. Do not use kill -9.
Does docker stop lose Redis data?
It can. docker stop waits ten seconds before sending SIGKILL, which may not be enough to write a large snapshot. Use docker stop -t 60 or run docker exec redis redis-cli SHUTDOWN SAVE first, and make sure /data is a mounted volume so the snapshot survives the container being removed.
How do I check whether Redis has actually stopped?
redis-cli ping should fail to connect, pgrep -a redis-server should print nothing, and ss -ltnp | grep 6379 should show no listener. If ping still answers, a different instance is running; redis-cli INFO server shows which one.