Valkey is a drop-in Redis fork that apt installs in one line. The defaults are safe to start with; memory and crashes need a few lines of config.
The README of your app says "needs Redis". Fine. On Ubuntu 24.04 you can have something that talks exactly like it from one apt line: Valkey, the open-source fork. I put it on a fresh VPS and did the boring things in order: lock the door, set a memory ceiling, then kill the process with kill -9 and see what's left of the data. Everything below is output from that run.
In short.
apt install valkey-server valkey-toolsgives you a service bound to localhost. Addrequirepassanyway. Setmaxmemoryand a policy, because out of the box there is no limit. Switch onappendonly yesif losing the last few minutes of data would hurt. Leave protected mode alone.
Think of a dictionary that never leaves RAM. You hand it a key, it hands back the value in well under a millisecond, and every so often it dumps a copy to disk so a reboot isn't a blank slate. I mostly meet it as session storage, job queues, rate-limit counters, or a cache bolted in front of a database that's too slow. History in one line: forked from Redis 7.2.4 in 2024, now run under the Linux Foundation. Day to day nothing changes for you. Same commands, same client libraries, same config names, and the server says as much itself: its own output reports redis_version:7.2.4.
Ubuntu 24.04 ships both, which is a little funny. redis-server is 7.0.15; valkey-server is 7.2.13, pulled from universe via noble-updates. Choose one. Both grab port 6379, and your app only needs a single store.
Two packages: the server, and valkey-tools with the client valkey-cli, the benchmark and the file checkers.
sudo apt update && sudo apt install -y valkey-server valkey-toolsInstalling creates a valkey user, enables a systemd unit and starts it. Look inside the unit and you'll find Restart=always: crash it and it comes back, and nothing runs as root. Which version did we get, and who is listening? (If the ss flags look like noise, this article decodes them.)
The first line is valkey-server --version, the other two come from ss -ltnp | grep 6379. Both sockets are loopback. From the machine itself you can connect, from anywhere else you can't, and during setup that is what you want. The shipped config spells it out: bind 127.0.0.1 -::1 and protected-mode yes.
Read the startup log once. It's a plain file, not only journal:
sudo tail -n 20 /var/log/valkey/valkey-server.logOur log opened with a complaint: # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. The cure it suggests, vm.overcommit_memory = 1 in /etc/sysctl.conf, I did not apply; this box has 0. Consider it a note from the log, not a tested fix.
Start valkey-cli and poke at it three ways: a value with an expiry, a counter, and a lookup after the expiry has passed. That's roughly what a session store or a rate limiter does all day.
EX 3 means three seconds to live, and ttl counts down. -2 means the key is gone. -1 would mean it lives forever, so hold on to that for the memory section. incr makes the key (starting at 1) if it's missing, then adds one per call, atomically, so two simultaneous requests can't eat each other's count.
Localhost-only is decent protection, but add a password anyway. A local process can talk to an open Valkey, and so can any container that lands on the host network next month. Don't invent one, generate it:
openssl rand -hex 24Open /etc/valkey/valkey.conf in an editor, add requirepass your-generated-string and restart:
sudo systemctl restart valkey-serverA normal restart is a polite one. The server writes its data to disk on the way out (the log says Saving the final RDB snapshot before exiting), so nothing is lost. Now the same client as before:
Four lines, three lessons. Without the password every command bounces. The -a flag works, and the warning is there for a reason: while I had a valkey-cli -a ... session open, ps aux showed the password in full, and on a shared machine anyone can read that. The way to keep it off the command line is an environment variable, and here is the catch. In the 24.04 package it's the old name, REDISCLI_AUTH. The sensible-looking VALKEYCLI_AUTH is ignored without a word, and the client behaves as if there were no password at all. I lost a few minutes to that. It's valkey-cli 7.2.4 from this package, newer builds may have fixed it. valkey-benchmark ignores the variable too and wants -a.
Your app uses the same password in its connection string or settings. Keep it in an environment file or a secrets store, not in the repository.
The usual story of an exposed key-value store has no clever exploit in it. Someone edits bind so a second server can connect, forgets everything else, and the store ends up on a public address with no password. Valkey has a safety net for this called protected mode, and I wanted to see what it really does. Two empty network namespaces play "the server" and "another machine", so the real service on the host was never exposed.
The address 10.77.0.1 is a private one from the lab. The first result surprised me. Even with bind 0.0.0.0, protected mode refused outside connections as long as there was no password. The net works. The second block shows life without it: a stranger writes keys and reads the server's own configuration. A stranger could just as easily read or wipe all your data, so I stopped there. Protected mode can be switched off with protected-mode no. Don't. The third block is the real fix: with a password, the door stays shut even when the socket is open.
Need access from another machine? Best answer: no direct access. Use an SSH tunnel and leave Valkey on loopback. If the app really runs on a second server, bind to the private address only, set the password, and let exactly one client through the firewall, as in the nftables guide. I haven't tested TLS, so encrypting a link across the public internet is out of scope here.
People skip this setting, and it bites. Out of the box maxmemory is 0, meaning no limit, and the policy is noeviction. A cache allowed to grow forever eats RAM until the kernel's OOM killer picks a victim. The victim is often the biggest process around: Valkey itself, or the database next to it (here is how to tell that from a CPU problem).
Two settings fix it. maxmemory is the ceiling, maxmemory-policy is what happens at the ceiling. I picked a deliberately tiny 20 MB and wrote 30,000 values of 1 KB each, once per policy:
Three runs, each from an empty database. The rows:
noeviction is the default. At the ceiling, new writes are refused with OOM command not allowed while reads carry on: key:1 was still there, the last key never made it in. It suits data you can't throw away, a job queue say, provided your app copes with the error.allkeys-lru. Every write succeeds, and the server quietly deletes the least recently used keys to make room. After the run key:1 was gone and key:30000 was there. That's the one for a pure cache, where a lost entry costs one recomputation.volatile-lru evicts only keys that have a lifetime. Ours had none, so it had nothing it was allowed to delete and behaved exactly like noeviction. This is the trap. You choose it for "safer eviction" and still get OOM errors. Use it only if you give every cache key a TTL.A note on the number: 20 MB held roughly 14,700 values of 1 KB, so each entry costs noticeably more than its payload. Leave headroom. Valkey isn't alone on the server, and writing a snapshot can briefly need extra memory. If the machine is already tight, a swap file buys time, it doesn't replace a sane ceiling.
By default Valkey takes a snapshot (RDB) when enough writes have piled up. config get save returns 3600 1 300 100 60 10000: after an hour with one change, five minutes with a hundred, a minute with ten thousand. Whatever came after the last snapshot is gone if the process dies hard. The alternative is the append-only file (AOF), a log of every write that is flushed to disk about once a second. The unit restarts a dead server, so I killed the process with kill -9, which gives it no chance to save, and looked at both modes:
After the kill, the manual save had protected one key and the next write was gone. With appendonly yes the key lived. The shipped config has appendfsync everysec, so after a power loss or a kernel crash you could lose about the last second of writes. I didn't simulate that, and a plain kill -9 doesn't show it.
AOF or not? It depends on what you store. A cache can be rebuilt, so the default snapshots do, and turning persistence off wouldn't be crazy. Sessions, queues and counters you'd miss deserve appendonly yes. The price is some disk space and a little write speed.
One trap on the way. CONFIG SET changes the running server and nothing else. I ran config set maxmemory 64mb, restarted the service, and maxmemory was 0 again. The only lasting place is /etc/valkey/valkey.conf (the documentation describes CONFIG REWRITE for saving runtime changes, which I didn't try). Experiment with CONFIG SET, then write the final values into the file.
Put these lines into /etc/valkey/valkey.conf, replacing the matching defaults, and restart. The memory size is an example: pick yours from what the rest of the server needs.
allkeys-lru is for a cache. For a queue, use noeviction. After the restart, ask for each value back with valkey-cli config get maxmemory and config get maxmemory-policy, and check with ss that the socket is still local.
The bundled benchmark is good for a smoke test after setup. Not a hardware review.
valkey-benchmark -a '<password>' -q -n 100000 -c 20 -t set,getOn our 2-vCPU box the client and the server shared the CPUs over loopback. We got roughly 40,000 to 50,000 SET and GET a second (43,000 and 48,000 in the run shown), median latency near a quarter of a millisecond. With -P 16, sixteen commands per batch, it went to several hundred thousand (365,000 and 529,000 in that run). Repeat runs differ by tens of percent. A real app lands between the two, depending on how many round trips it makes per request. A lone Valkey on a small VPS is rarely what slows you down. The network hop per command usually is.
For nearly every app, yes. Same commands, same protocol, same client libraries. Test your own app first if it's production, and never run both on one port.
A requirepass is set and the client didn't send it. Put it in the connection string, send AUTH after connecting, or use REDISCLI_AUTH with valkey-cli from the 24.04 package.
That depends on the policy. With noeviction writes fail and reads keep working. With allkeys-lru old keys are dropped. With no maxmemory at all, nothing stops it until the kernel does.
With default snapshots, everything since the last one. With appendonly yes, about a second after a power loss or kernel crash, a little more if the disk stalls. A clean restart through systemd loses nothing.
No. Keep it on loopback and use an SSH tunnel or a private network. If you must bind a network address, set a password, keep protected mode on and restrict the port in the firewall.
requirepass, and hand it to valkey-cli through REDISCLI_AUTH, not -a.maxmemory: allkeys-lru for a cache, noeviction for a queue, never volatile-lru without TTLs.CONFIG SET.