Operations — 10 min read

Running Redis on your own server: cache, persistence, and the port you must not leave open

Redis installs in one command and runs in two very different modes. What the default persistence actually does, why an unauthenticated Redis on 0.0.0.0 is the classic breach, and the maxmemory decision every cache has to make.

Published
October 5, 2026
Reading time
10 min read
Servor Team
Operations
Tags
Redis · Cache
Lire cet article en français

One binary, two databases

Almost nobody installs Redis for its own sake. What people want is either a cache — a fast, disposable place to keep computed answers — or a store that survives a restart and holds data you actually care about. Redis can be both, and the same apt-get install -y redis-server gives you either one, which is precisely the trap. The install is trivial; deciding which of the two you are running is the decision that matters, and Redis will not make it for you.

So the thesis is narrow: the risk in a Redis install is never the install, it is running a store when you meant a cache, or leaving a cache open to the internet. A misread of that first choice loses data on the next reboot. A misread of the second hands a stranger a root-adjacent process on a public port. Both are configuration, not packaging.

Before the first package: read the machine

A server is rarely as empty as it looks, and a second Redis stacked on a running one is a confusing mess. Reconnaissance is the difference between installing a service and colliding with one.

  • Is Redis already here, and which version? command -v redis-server && redis-server --version. If it is, you are configuring, not installing — and reinstalling over a working instance risks restarting it at a moment you did not choose.
  • Is a service already running? systemctl is-active redis-server 2>/dev/null || systemctl is-active redis 2>/dev/null. The unit is named redis-server on Debian and Ubuntu and redis on the RHEL family, and assuming the wrong one is a classic own goal.
  • Is port 6379 already taken? ss -tlnH 'sport = :6379' tells you what owns the port — and, just as important, which interface it is bound to. A process already listening on 0.0.0.0:6379 is a finding, not a footnote.

1. Install, but only if it is missing

On Debian and Ubuntu the package is redis-server; on the RHEL family it is redis. The install itself is the least interesting step, and the only rule that matters is the one that is easy to skip under time pressure: if Redis is already installed and serving, do not reinstall. Verify the version, confirm the service, and move on to the part that changes the outcome.

The proof that this step worked is not “the command exited 0”. It is a version string from redis-server --version — one concrete fact a human or a model can read without guessing.

2. Enable it, then prove it answers

Enable the daemon so it survives a reboot — systemctl enable --now redis-server — and then, crucially, prove it actually responds rather than merely being active. The honest verification is redis-cli ping returning PONG: it exercises the whole path, socket and server, in one round trip. A unit that is active but does not answer PING is a server you have not actually tested.

3. Decide cache or store — because the default is not obvious

This is the step every guide skips and every incident traces back to. Redis has two persistence mechanisms, and which is on by default is not something you should be guessing at.

  • RDB snapshots write a point-in-time dump on a schedule. Fast, compact, and lossy by definition: everything written since the last snapshot is gone if the process dies. Perfectly fine for a cache; quietly unacceptable for a store.
  • AOF (append-only file) logs every write, so recovery replays them. Far more durable, at the cost of a larger file and more I/O. This is the mode you want when the data in Redis is data you cannot recompute.

The mistake is treating Redis as a durable store while running snapshot defaults tuned for a cache, and discovering the gap only after a crash. Decide the role first, then set persistence to match it — and if it is genuinely a cache, own that the data is disposable rather than pretending otherwise.

Persistence is not free, and the cost surprises people. To write a snapshot, Redis fork()s, and copy-on-write means a write-heavy instance can briefly need close to double its memory during the save. Size the box for that peak, not just for the resident data set. A store that runs fine until the exact moment it tries to persist is a store that will fail when you can least afford it.

4. The port: the one that becomes a crypto-miner

The single most consequential Redis mistake is exposing it. An unauthenticated Redis bound to 0.0.0.0 is one of the most reliably exploited services on the public internet: an attacker connects, writes a payload, and turns your database host into a crypto-miner or a pivot. There is no clever nuance here — the fix is two settings, applied together:

  • Bind to 127.0.0.1. If the only thing talking to Redis is an app on the same machine, it never needs a public interface. Bind to loopback and the entire class of internet-facing attacks disappears.
  • Set requirepass. If Redis genuinely must be reachable off-box, it must have a password, and the connection must be protected in transit. A public Redis with no auth is not a risk you manage, it is a breach you have not noticed yet.

For a cache, add the two settings that keep it a cache: a maxmemory ceiling so it cannot eat the box, and an eviction policy — allkeys-lru is the sane default for a pure cache — so it discards old keys instead of refusing writes when full.

What Redis does not do for you

Redis is not a durable message queue with delivery guarantees; treating a list as a broker works until a crash between snapshots quietly drops messages. A single node is not high-availability — that needs replication and Sentinel or Cluster, and a decision you make on purpose, not a default you inherit. It does not encrypt data at rest, and older builds do not encrypt in transit without TLS configured. And nothing about installing Redis backs anything up: if you chose the store role, the persistence file is yours to protect. Convenience is not durability.

The check that proves the posture, not just the install

In order: redis-cli ping returning PONG for a live server; ss -tlpn confirming Redis is on 127.0.0.1:6379 and not, quietly, 0.0.0.0; if a password was set, an AUTH that succeeds and an unauthenticated command that is refused; redis-cli config get maxmemory and the eviction policy read back as what you intended; and systemctl is-enabled so it survives a reboot. Prove the bind address, because that is the line between a private cache and a public incident.

Where Servor fits

Servor ships Redis as a recipe — one of 150+ vetted playbooks — and the recipe shape is what keeps a service with a dangerous default from being careless. It is not a frozen script fired blindly: it starts with reconnaissance, reading whether Redis is already installed, whether the service is up, and what already holds port 6379, so the run adapts to a real server instead of assuming an empty one. A cheap model drives it, and every step it proposes carries its own verify — the version string, the redis-cli ping returning PONG — so success is proven, not asserted. The playbook is idempotent: an existing Redis is checked and reported rather than reinstalled over.

The guardrails are written in precisely because the default is dangerous: if a password is provided the recipe configures requirepass and binds to localhost, and it never leaves Redis open to 0.0.0.0 without authentication. The dangerous configuration is the one the recipe refuses to produce.

And the execution never leaves the zero-knowledge path: every command is signed in your browser with a key derived from your vault key, relayed verbatim, and verified by the agent on the machine before it runs. The agent reaches the control plane over an outbound connection, so a Redis you have correctly bound to loopback stays reachable to manage without ever being exposed. If Redis is backing an app you are putting into production, pair it with the discipline in the PostgreSQL-in-production piece and the approve-sign-verify loop from the Plan-Execute-Verify piece. Browse the rest of the recipe catalogue, or see how the operational half fits together in our server operations guide.

Next step

Run it, don't just read it.

Free for two servers, no card. Every command is signed in your browser before it runs — our servers relay it, they cannot forge it.

Continue reading

Archive →