
🌐 Also available in: Deutsch · Français · Español
Deep Dive, part of the series around the Ansible Tour. Level: advanced Linux administration. The tour explains the roles; the deep dives explain the technology underneath them – how it works, why it is here, what exactly this setup does with it, how to run it day to day, and what else there would have been.
Redis is the smallest service in this setup – a few megabytes of memory, a fraction of a CPU core – and both applications depend on it. The blog uses it as an object cache (part 3), Nextcloud for file locking and as a shared cache between processes (part 4). This post explains what Redis does there, why the two instances are secured differently, what the numbers say, and what a review for this article turned up.
🧭 How Redis works
One thread, everything in memory
Redis keeps all data in RAM and executes commands one after another in a single event loop. That sounds like a bottleneck and is the opposite: there are no locks between threads, every command is atomic, and a simple read takes microseconds. Network and system calls cost more than the command itself.
Keys, types, expiry
Every entry is a key with a value – a string, a hash, a list, a set. Each key can carry a time to live; Redis deletes it when it expires. Both applications set an expiry on almost every key, so the data set cleans itself up.
When memory runs out
maxmemory sets a limit; maxmemory-policy decides what happens at the limit. allkeys-lru evicts the least recently used key – right for a pure cache, where every entry can be rebuilt. noeviction refuses new writes instead – right for data that must not silently disappear, such as a lock.
Persistence – or none
Redis can write snapshots (RDB, controlled by save) and an append-only log of all writes (AOF). Both are for data that has to survive a restart. A cache and short-lived locks do not need either: after a restart the cache fills again and the applications take new locks.
Authentication and protected mode
requirepass (or, since Redis 6, ACL users) makes every client authenticate first. Protected mode only refuses connections from outside if Redis listens on all interfaces without a password – as soon as a bind address and no password are configured, it no longer helps.
🛡️ Why it is here
- WordPress object cache. WordPress asks the database for the same options, menus and post metadata on every request. With the plugin Redis Object Cache, the answers come from Redis instead.
- Nextcloud file locking. When two clients write the same file at the same time, Nextcloud needs a lock that every PHP process, in every pod, sees at once. Without it, sync clients overwrite each other. Nextcloud explicitly recommends Redis for this.
- Nextcloud distributed cache. Data shared across processes – app configuration, session-adjacent state – lives there too; the per-process cache is APCu.
🔧 What this repository does
| Blog | Nextcloud servers | |
|---|---|---|
| Where | Pod in K3s (redis:7.4-alpine) | On the host, AlmaLinux package (Redis 6.2) |
| Role | blog_k3s_deploy | next_packages + next_redis |
| Used for | Object cache | File locking + distributed cache |
| Memory limit | 80 MB, allkeys-lru | none, noeviction |
| Persistence | none (--save "", AOF off) | RDB snapshots (package default) |
| Password | none | requirepass from the vault |
| Reached by | ClusterIP Service, NetworkPolicy: only WordPress pods | Node IP and 127.0.0.1, nftables: only the pod network |
| SELinux | container_t with own MCS categories | redis_t |
The blog: a disposable cache in a locked pod
command: ["redis-server", "--save", "", "--appendonly", "no",
"--maxmemory", "80mb", "--maxmemory-policy", "allkeys-lru"]
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
capabilities: { drop: [ALL] }Everything in this Redis can be thrown away, and it is configured that way: no disk writes, a memory ceiling with LRU eviction, a read-only root file system, no capabilities. It has no password because nothing outside the pod network can reach it and inside it only the WordPress pods may – a NetworkPolicy denies every other pod, including the database and the ingress controller.
Nextcloud: why the password
On the Nextcloud servers Redis runs on the host, and the pods reach it through a Kubernetes Service that points to the node IP. That means Redis listens on the public address of the server. Between the internet and Redis there was, at first, exactly one nftables rule. The role next_redis adds requirepass as a second layer, because one rule failing silently is not hypothetical here: fail2ban once sat dead on the blog for two days without anyone noticing. The password reaches Nextcloud through a Kubernetes Secret mounted as redis.config.php – not through the ConfigMap that holds the rest of the configuration, because ConfigMaps are not meant for secrets.
- name: Redis | Ensure Redis requires authentication
ansible.builtin.lineinfile:
path: /etc/redis/redis.conf
regexp: '^requirepass\s+'
line: 'requirepass {{ nextcloud_redis_pass }}'
no_log: true # the line contains the password
notify: Restart Redis
- ansible.builtin.meta: flush_handlers # restart NOW, the next role needs itThe flush_handlers is not decoration: the roles after it run occ commands in the pod, which connect to Redis through the node IP. Without the immediate restart, Redis would still listen on 127.0.0.1 only at that moment.
noeviction on purpose
The host Redis has no memory limit and the default policy noeviction. For locks that is correct: an evicted lock would be a lock that silently stopped protecting a file. The data set is tiny anyway (see below), and the server has plenty of memory.
📊 Measured
| Blog | Nextcloud A | Nextcloud B | |
|---|---|---|---|
| Version | 7.4.11 | 6.2.24 | 6.2.24 |
| Memory used | 14.8 MB | 1.7 MB | 1.3 MB |
| Keys | 19,079 | 56 | 83 |
| Hit rate since restart | 87.6 % | 85.9 % | 64.4 % |
| Evicted keys | 0 | 0 | 0 |
| Slow log entries | 1 | 3 | 0 |
The two workloads look nothing alike. The blog cache holds tens of thousands of small objects; Nextcloud holds a few dozen locks and cache entries that are created and expire constantly – over 11,000 keys expired on the larger server within a day. The lower hit rate on the smaller Nextcloud server is the nature of locking, not a problem: “is this file locked?” is a miss whenever the answer is “no”. The fragmentation ratio Redis reports for the host instances (7 to 9) looks alarming and means nothing at this size – a few megabytes of process overhead divided by one or two megabytes of data.
The blog uses Predis, a client written in PHP, because the official WordPress image does not ship the phpredis extension. The C extension would be faster; with an 88 % hit rate and page cache in front, the difference does not show in response times.
🔍 What the review for this article found
Collecting the facts for this post turned up four things the setup had not done – none dangerous, all worth knowing. All four were fixed on 20 October 2026.
- No systemd sandbox for the host Redis. MariaDB on the same servers got a hardening drop-in (no capabilities, read-only file system, system call filter); Redis runs only with the package unit. SELinux still confines it to
redis_t, so it is not unprotected – but the second layer that MariaDB has is missing. The outline of this very article claimed it existed. Fixed: the same kind of drop-in as for MariaDB – with one trap on the way: the sandbox options implyNoNewPrivileges, and under it SELinux refused the switch intoredis_t; Redis did not start for two minutes. A small local module (init_nnp_daemon_domain(redis_t)) allows exactly that transition. - Snapshots nobody needs. The host Redis keeps the package default
save 3600 1 300 100 60 10000and writesdump.rdbregularly – for locks with a time to live and a cache that rebuilds itself. Restored after a reboot, stale locks would even have to expire first. Fixed:save "", as on the blog, and the olddump.rdbremoved. - A rotated password would not have reached Nextcloud. The connection settings come from a Secret, mounted with
subPath– and a subPath mount never updates in a running pod. The pod is rolled when its configuration changes, via a checksum annotation, but the Secret was not part of that checksum. Rotating the password would have restarted Redis with the new one while Nextcloud kept the old one until its next accidental restart: no locks, error pages. Fixed: the Secret is now part of the checksum. The last manual piece – the push daemon notify_push reads the password fromconfig.php, where the installer once wrote it – is automated too: the playbook copies the password from the Secret intoconfig.phpwhenever they differ. - Monit does not watch it. Monit restarts and reports K3s, sshd and the monitoring services, but not the host Redis or MariaDB of the Nextcloud servers. If Redis stopped, Nextcloud would fail first and Grafana would notice second. Fixed: both are in Monit’s list now.
🧰 Day-to-day administration
# host (Nextcloud): password from the config, never on the command line
export REDISCLI_AUTH="$(awk '/^requirepass/{print $2}' /etc/redis/redis.conf)"
redis-cli INFO stats | grep -E 'keyspace_(hits|misses)|evicted'
redis-cli INFO memory | grep used_memory_human
redis-cli SLOWLOG GET 5
redis-cli --latency # Ctrl-C to stop
# blog pod
k3s kubectl -n wordpress exec deploy/redis -- redis-cli INFO keyspace
wp redis status # inside the WordPress container, as www-dataMONITORprints every command live – useful for a few seconds, expensive and full of data on a busy instance.- Flushing. On the blog,
wp cache flushempties the object cache safely. On Nextcloud, aFLUSHALLdrops all locks as well; do it only with the instance in maintenance mode. - Rotating the password. Change it in the vault, run the playbook:
next_rediswrites it intoredis.confand restarts Redis,next_k3s_deployupdates the Secret and – since the fix above – rolls the Nextcloud pod in the same run. notify_push gets it in the same run. - Updates. The host package gets security fixes through dnf-automatic, which restarts Redis afterwards; the blog image follows the pin in
versions.yml.
🆘 Cheat sheet: symptom → command
The short version of this article for the moment something is wrong. Run on the host as root; <…> are placeholders. occ and wp are the shell functions from the Nextcloud and WordPress deep dives.
| Symptom | Command |
|---|---|
| Is it running, and confined? | systemctl status redis |
| Talk to it without the password on the command line | export REDISCLI_AUTH="$(awk '/^requirepass/{print $2}' /etc/redis/redis.conf)" |
| Is the cache doing its job? | redis-cli INFO stats | grep -E 'keyspace_(hits|misses)|evicted' |
| Memory full? | redis-cli INFO memory | grep -E "used_memory_human|maxmemory_human" |
| Slow commands, high latency | redis-cli SLOWLOG GET 5 |
| No push notifications in Nextcloud | k3s kubectl -n nextcloud logs deploy/notify-push --tail=20 |
| The blog cache | k3s kubectl -n wordpress exec deploy/redis -- redis-cli INFO keyspace |
🔭 What comes next
- Valkey. After Redis changed its licence in 2024, the Linux Foundation fork Valkey became the default in several distributions. It is a drop-in replacement; a switch would be a package or image change.
- Metrics. A redis_exporter would put hit rate and memory into Grafana instead of a manual
INFO.
⚖️ Alternatives – and why Redis
| Option | Strength | Why not here |
|---|---|---|
| Memcached | Simple, fast cache | No locking semantics Nextcloud can use; would leave locking in the database. |
| APCu only | In-process, no network | Not shared between PHP processes or pods – useless for locking. Nextcloud uses it as the local cache in addition. |
| Database locking | No extra service | Nextcloud’s fallback; adds load on the busiest component and is slower. |
| Valkey / KeyDB | Forks with different governance or threading | Valkey is the likely next step; KeyDB is less active. |
| Redis | What both applications expect and test against, tiny footprint | Chosen. |
Redis is the kind of component that is easy to forget because it never complains. The review for this article was a reminder that “it works” and “it is set up the way the rest is” are two different statements – and that the second one is worth checking for the small services too.


