Redis is an in-memory key-value store that answers reads in well under a millisecond, which makes it a natural cache in front of a slower database or API. In this tutorial you will install Redis on Ubuntu 24.04, configure it specifically as a cache (a fixed memory budget, least-recently-used eviction and no disk persistence), protect it with a password and check that your application is actually getting cache hits.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS.
- A non-root user with
sudoprivileges. - Enough free RAM for the cache you plan to run. This guide uses a 256 MB cache, which fits comfortably on a 1 GB server.
The application that uses the cache can run on the same server (the simplest and safest setup) or on another server in the same private network. Both cases are covered in Step 4.
Step 1 - Installing Redis
Ubuntu 24.04 ships Redis 7 in its main repository, which is well suited for caching and receives security updates through the normal apt upgrade cycle. Install it:
sudo apt update
sudo apt install redis-server
The package starts the redis-server service and enables it at boot. Check that it is running:
sudo systemctl status redis-server
● redis-server.service - Advanced key-value store
Loaded: loaded (/usr/lib/systemd/system/redis-server.service; enabled; preset: enabled)
Active: active (running) since ...
Then send a PING with the command-line client:
redis-cli ping
PONG
By default Redis listens only on 127.0.0.1 and ::1 on port 6379, so it is not reachable from the internet yet.
Step 2 - Configuring Redis as a cache
Out of the box, Redis behaves like a database: it has no memory limit and periodically saves a snapshot of all data to disk. For a cache you want the opposite: a hard memory ceiling, automatic eviction of old keys when that ceiling is reached, and no disk writes, because cached data can always be rebuilt from the source.
Open the main configuration file:
sudo nano /etc/redis/redis.conf
Find each of the following directives (use Ctrl+W in nano to search) and set them to these values. If a directive is commented out with #, remove the #:
# Maximum memory Redis may use for data
maxmemory 256mb
# When the limit is reached, evict the least recently used keys
maxmemory-policy allkeys-lru
# Disable RDB snapshots: a cache does not need to survive a restart
save ""
# Keep the append-only file disabled (this is the default)
appendonly no
A few notes on these choices:
maxmemory: size it to leave room for the operating system and your application. A common rule is no more than 50 to 70 percent of the RAM on a dedicated cache server.maxmemory-policy allkeys-lru: any key can be evicted, starting with the ones not used recently. Usevolatile-lruinstead if the same Redis instance also stores keys without a TTL that must never be evicted, such as sessions or queues.save "": removes all snapshot rules. Without it, Redis forks and writes the whole dataset to/var/lib/redis/dump.rdbat intervals, which costs memory and disk I/O for no benefit in a pure cache.
Save the file and restart Redis to apply the changes:
sudo systemctl restart redis-server
Confirm that the new values are active:
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG GET save
1) "maxmemory"
2) "268435456"
1) "maxmemory-policy"
2) "allkeys-lru"
1) "save"
2) ""
Step 3 - Tuning the kernel for Redis
When Redis starts, it checks a kernel setting and logs a warning if memory overcommit is disabled. Look at the log:
sudo journalctl -u redis-server -n 30 --no-pager
If you see WARNING Memory overcommit must be enabled!, enable overcommit permanently with a sysctl drop-in file:
echo 'vm.overcommit_memory = 1' | sudo tee /etc/sysctl.d/60-redis.conf
sudo sysctl --system
Verify the value and restart Redis so it no longer logs the warning:
sysctl vm.overcommit_memory
sudo systemctl restart redis-server
vm.overcommit_memory = 1
Step 4 - Securing access with a password
Even when Redis listens only on localhost, any local process or user can read and change the cache. Setting a password closes that gap and is mandatory if you ever expose Redis to other servers.
Generate a strong random password:
openssl rand -base64 32
Open the configuration file again:
sudo nano /etc/redis/redis.conf
Find the requirepass directive, uncomment it and set the generated value, replacing your_strong_password:
requirepass your_strong_password
The configuration file contains the password, so make sure only the redis user can read it (this is the Ubuntu default, but it is worth checking):
sudo chown redis:redis /etc/redis/redis.conf
sudo chmod 640 /etc/redis/redis.conf
sudo systemctl restart redis-server
Now an unauthenticated command fails:
redis-cli ping
(error) NOAUTH Authentication required.
To authenticate without leaving the password in your shell history, use --askpass, which prompts for it:
redis-cli --askpass ping
Please input password: ********
PONG
Allowing an application on another server
Skip this subsection if your application runs on the same server as Redis.
If the application runs on another server, bind Redis to the private IP address of this server as well. In /etc/redis/redis.conf, change the bind line, replacing your_private_ip with this server's private address:
bind 127.0.0.1 -::1 your_private_ip
Restart Redis and allow port 6379 only from the application server, replacing app_server_ip:
sudo systemctl restart redis-server
sudo ufw allow from app_server_ip to any port 6379 proto tcp
WarningNever bind Redis to a public IP address or open port 6379 to everyone. Redis traffic is not encrypted by default, so keep it on a private network.
From the application server you can test the connection with redis-cli -h your_private_ip --askpass ping (install the redis-tools package there to get redis-cli).
Step 5 - Testing the cache behavior
Caches rely on keys expiring. Store a value with a 60-second TTL, as an application would do after an expensive database query:
redis-cli --askpass
Inside the Redis prompt, run:
SET product:42 '{"id":42,"name":"Keyboard","price":49.90}' EX 60
GET product:42
TTL product:42
OK
"{\"id\":42,\"name\":\"Keyboard\",\"price\":49.90}"
(integer) 57
After 60 seconds GET product:42 returns (nil) and the application would rebuild the value from the database. Type exit to leave the prompt.
This is the cache-aside pattern that most frameworks implement for you:
- The application asks Redis for the key.
- On a hit, it returns the cached value.
- On a miss, it queries the database, stores the result in Redis with a TTL and returns it.
Always set a TTL on cache keys. With allkeys-lru Redis will evict keys when memory runs out anyway, but a TTL keeps stale data from living forever.
Example with Python
To see the pattern in code, install the Redis client for Python from the Ubuntu repository:
sudo apt install python3-redis
Create a small test script:
nano ~/cache_test.py
import json
import os
import time
import redis
r = redis.Redis(host="127.0.0.1", port=6379, password=os.environ["REDIS_PASSWORD"])
def slow_database_query(product_id):
time.sleep(1) # simulate a slow query
return {"id": product_id, "name": "Keyboard", "price": 49.90}
def get_product(product_id):
key = f"product:{product_id}"
cached = r.get(key)
if cached is not None:
return json.loads(cached), "hit"
product = slow_database_query(product_id)
r.set(key, json.dumps(product), ex=300)
return product, "miss"
for _ in range(3):
start = time.perf_counter()
_, result = get_product(7)
print(f"{result}: {(time.perf_counter() - start) * 1000:.1f} ms")
Run it, passing the password through an environment variable. read -rs reads it silently from the keyboard, so it never appears on screen or in your shell history:
read -rs REDIS_PASSWORD && export REDIS_PASSWORD
python3 ~/cache_test.py
miss: 1003.2 ms
hit: 0.4 ms
hit: 0.3 ms
Step 6 - Monitoring memory and hit rate
A cache is only useful if most lookups are hits. Redis tracks hits and misses since the last restart:
redis-cli --askpass INFO stats | grep -E 'keyspace_(hits|misses)|evicted_keys'
keyspace_hits:18452
keyspace_misses:1310
evicted_keys:0
The hit rate is hits / (hits + misses), here about 93 percent. As a rough guide:
- A low hit rate usually means TTLs are too short or keys are too specific to be reused.
- A steadily growing
evicted_keysmeans the cache is full and dropping data it still needs. Increasemaxmemoryif the server has room.
Check memory usage against the limit:
redis-cli --askpass INFO memory | grep -E '^(used_memory_human|maxmemory_human|mem_fragmentation_ratio)'
used_memory_human:41.27M
maxmemory_human:256.00M
mem_fragmentation_ratio:1.12
To watch commands in real time while you debug an application, use redis-cli --askpass --stat, which prints keys, memory and requests per second every second. Press Ctrl+C to stop it.
Troubleshooting
OOM command not allowed when used memory > 'maxmemory': the eviction policy isnoeviction(the default), so Redis refuses writes when full. CheckCONFIG GET maxmemory-policyand setallkeys-lruas shown in Step 2.- Service fails to start after editing the config: a typo in
redis.confstops Redis. Runsudo journalctl -u redis-server -n 50 --no-pager; the log names the line that could not be parsed. Could not connect to Redis at your_private_ip:6379: Connection refusedfrom another server: thebindline does not include the private IP, or UFW blocks the port. Check withsudo ss -tlnp | grep 6379andsudo ufw status.
Conclusion
Redis is now running on Ubuntu 24.04 as a dedicated cache: it uses at most 256 MB, evicts the least recently used keys when full, never writes to disk and requires a password. From here you can point your framework's cache backend (Django, Laravel, Rails or Express session stores) at 127.0.0.1:6379, add Redis metrics to your monitoring system, or move the cache to its own server on a private network when your application grows.
