You Probably Don't Need Redis — Caching for Small Apps and Bots
If you're running one small app — a bot, a modest API, a side project — you probably don't need Redis. A dictionary with expiry in your own process is faster than a network round trip, costs nothing, needs no second container, and can't be attacked from the internet. Redis earns its place when several processes need to share state, and that's a genuinely different problem from "I want caching." Before you add a Redis container, it's worth being clear about which of the two you actually have, because the second one carries a security cost that's easy to underestimate on a platform with no private networking.
What you're probably using it for
Four common jobs, and what a single process can do instead:
| Job | In-process alternative |
|---|---|
| Cache API responses | A dict/Map with a TTL, or cachetools / lru-cache |
| Rate limiting | A per-user timestamp deque in memory |
| Deduplicate work | A set of recently-seen IDs, bounded |
| Job queue | Your language's async queue, or a table in the database you already have |
For a bot answering commands, all four are memory structures. The Redis version does the same thing, plus serialization, plus a socket, plus a second thing to keep running.
In-process is also just faster. A dict lookup is nanoseconds; a Redis GET over a network is a round trip. People add Redis for performance and then measure a slowdown on cache hits, because the cache was never the bottleneck.
The one property you lose is that memory dies with the process. For a cache that's fine by definition — a cold cache after a restart is a slow minute, not a bug. If losing it is a bug, you didn't want a cache, you wanted storage.
When you actually need it
Redis stops being optional when:
- Two or more processes share state. A bot plus a web dashboard that must see the same rate-limit counters. Separate containers can't share a dict.
- State must survive a restart and it's genuinely ephemeral-shaped — sessions, locks, short-lived tokens — so a relational table feels wrong.
- You need the data structures. Sorted sets for leaderboards, streams, pub/sub across processes. These are real reasons; "I need a cache" is not.
Note that the first two are the only ones a small app usually hits, and both have a simpler answer available: if you already run Postgres, a table with an expiry column handles sessions and counters perfectly well at small scale. A SQLite file on a volume does the same for a single app. Neither is glamorous; both are one less service.
If you do run Redis here, understand the exposure
This is the part that changes the calculation, and it's worth stating plainly rather than in a footnote.
Redis is designed for trusted networks. Its own security documentation is direct about it: it's not a good idea to expose a Redis instance directly to the internet, or to any environment where untrusted clients can reach the port (Redis security docs). Protected mode exists specifically because so many people did exactly that.
There is no private networking on this platform. Every port binding is publicly reachable — that's true for Postgres too, but Redis is a much more attractive target, because an unauthenticated instance hands over the whole dataset and, historically, a path to running code on the host.
The exposure is not theoretical. Security researchers put the number of internet-exposed Redis instances in the hundreds of thousands, with tens of thousands requiring no authentication at all, and honeypots report scanners arriving within minutes of an instance appearing (Imperva).
A password helps, but AUTH is cleartext without TLS. Redis's docs note the AUTH command is sent unencrypted like any other, so it doesn't protect against anyone able to observe the network. On a public port with no TLS, your Redis password crosses the internet in the open.
So if you run one:
- Set a long
requirepass— Redis is fast enough to brute-force against, and the docs recommend a very long password precisely because nobody has to type it. - Enable TLS, or accept that credentials and data are readable in transit.
- Remove the port binding when you're not using it. A Redis with no exposed port isn't reachable.
- Don't put anything in it you'd mind losing or leaking. Treat it as a cache, not a store.
- Seriously consider a managed Redis with TLS and network restrictions instead. This is one case where paying someone else is a reasonable call.
A decision you can make in one minute
- One app, want caching? In-process. Nothing to deploy.
- One app, need it to survive restarts? SQLite on a volume, or the database you already run.
- Two apps sharing counters or sessions? A shared table first. Redis only if the access pattern genuinely needs it.
- Need sorted sets, streams, or pub/sub across processes? Redis — and read the section above before exposing it.
The point isn't that Redis is bad. It's that most small apps reach for it out of habit, inherit a network hop and a public port, and get nothing they couldn't have had from twenty lines of their own code.
FAQ
Do I need Redis for a Discord bot?
Almost certainly not. A bot is a single long-running process, so caches, rate limits, and dedupe sets can live in memory. Redis matters when a second process needs the same state.
Is an in-process cache faster than Redis?
Yes, for a single process — a local lookup avoids serialization and a network round trip entirely. Redis wins on sharing, not speed.
What if my cache needs to survive restarts?
Then it isn't a cache. Use SQLite on a volume, or a table in your existing database.
Is it safe to expose Redis on a public port?
Not without care. Redis's own documentation advises against exposing instances to the internet, and unauthenticated instances are scanned and compromised within minutes. If you must, use a long password and TLS, and remove the port when unused.
Does a password make an exposed Redis safe?
It helps a great deal, but AUTH is sent unencrypted without TLS, so the password is observable in transit on a public connection. Add TLS or use a managed instance.
What should I use for rate limiting in a bot?
An in-memory structure keyed by user, holding recent timestamps. It's a few lines and needs nothing deployed alongside it.
Wrapping up
"Add Redis" is one of those defaults that gets applied long before the problem it solves shows up. For one process, a dict with a TTL is faster, free, and unreachable from the internet. Add the network hop when you actually have two things that need to agree — and when you do, decide deliberately how that port is protected, because on a platform without private networking that decision is yours to make rather than one the infrastructure makes for you.