Redis 8 is the latest major release of the high‑performance in‑memory data store trusted for caching, message brokering, and real‑time workloads. In this hands‑on guide, you’ll learn how to use Ansible to install Redis 8 on Rocky Linux 9 and harden it for production. We’ll cover repository setup (Remi), secure configuration (passwords, binding, protected mode, disabling risky commands), service management, firewall and SELinux considerations, verification, and optional performance tuning.
If you prefer Ubuntu, check out my earlier Ansible guide for Redis 6: How to Use Ansible to Install and Configure Redis 6 on Ubuntu 20.04.
What you’ll build
- Automated install: Enable the Remi repository and deploy Redis 8 with Ansible.
- Security hardening: Bind to the right interface, enforce authentication, enable protected mode, and disable dangerous commands.
- Operational readiness: Manage the systemd service, correct permissions, and optional firewall rules.
- Verification: Validate with
redis-cli
and systemd status checks.
Prerequisites
- A Rocky Linux 9 server with sudo privileges.
- Ansible installed on your control machine.
- SSH access to the target host(s).
- A secure password you’ll use for Redis authentication.
Tip to install Ansible locally with pip:
Why Ansible for Redis on Rocky 9?
- Idempotent and repeatable deployments across environments.
- Secure by default: Playbooks can enforce strong defaults and prevent drift.
- Speed: Reprovision or update at any time with minimal effort.
Repository setup: Why Remi for Redis 8?
Rocky Linux 9’s default AppStream often lags behind Redis’s latest stable. Remi provides fresh, well‑maintained Redis 8 builds for Enterprise Linux 9. We’ll import Remi’s GPG key, install the repository, and enable the Redis 8 module stream.
The Ansible playbook (explained)
Below is a complete playbook with sane, secure defaults. Each block is annotated with what it does and why it matters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
| ---
- name: Install and secure Redis 8 on Rocky Linux 9
hosts: redis_servers
become: yes
gather_facts: yes
vars:
redis_version: "8.0"
# CHANGE THIS IN PRODUCTION. Consider using Ansible Vault or a secret manager.
redis_password: "ChangeMe-Strong-Long#2025!"
# Bind to a specific interface or leave as 127.0.0.1 for local-only
redis_bind_address: "127.0.0.1"
# Optionally allow a trusted subnet through firewalld, e.g. "10.10.0.0/16"
allowed_cidr: ""
pre_tasks:
- name: Ensure dnf metadata and packages are up to date
ansible.builtin.dnf:
update_cache: yes
state: latest
tasks:
# --- Repositories ---
- name: Install EPEL repository (required by some dependencies)
ansible.builtin.dnf:
name: epel-release
state: present
- name: Import Remi GPG key
ansible.builtin.rpm_key:
key: https://rpms.remirepo.net/enterprise/9/RPM-GPG-KEY-remi
state: present
- name: Install Remi repository for Rocky 9
ansible.builtin.dnf:
name: https://rpms.remirepo.net/enterprise/remi-release-9.rpm
state: present
disable_gpg_check: no
- name: Enable Redis 8 module from Remi
ansible.builtin.command:
cmd: dnf -y module enable redis:remi-8.0
register: enable_module
changed_when: "'enabled' in enable_module.stdout or 'Nothing to do' not in enable_module.stdout"
failed_when: enable_module.rc not in [0]
# --- Install Redis 8 ---
- name: Install Redis 8
ansible.builtin.dnf:
name: redis
state: present
# --- Secure Configuration ---
- name: Ensure redis.conf has a stable pidfile
ansible.builtin.lineinfile:
path: /etc/redis/redis.conf
regexp: '^pidfile'
line: 'pidfile /var/run/redis/redis-server.pid'
notify: [restart redis]
- name: Bind Redis to the desired interface
ansible.builtin.lineinfile:
path: /etc/redis/redis.conf
regexp: '^bind\s+'
line: "bind {{ redis_bind_address }}"
notify: [restart redis]
- name: Ensure protected-mode is enabled
ansible.builtin.lineinfile:
path: /etc/redis/redis.conf
regexp: '^protected-mode'
line: 'protected-mode yes'
state: present
notify: [restart redis]
- name: Require a strong password
ansible.builtin.lineinfile:
path: /etc/redis/redis.conf
regexp: '^(#\s*)?requirepass'
line: "requirepass {{ redis_password }}"
state: present
notify: [restart redis]
- name: Disable dangerous administrative commands
ansible.builtin.blockinfile:
path: /etc/redis/redis.conf
marker: "# {mark} Ansible managed disabled commands"
block: |
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command SHUTDOWN ""
notify: [restart redis]
- name: Use systemd supervision (best with service managers)
ansible.builtin.lineinfile:
path: /etc/redis/redis.conf
regexp: '^supervised'
line: 'supervised systemd'
notify: [restart redis]
- name: Ensure correct permissions on redis.conf
ansible.builtin.file:
path: /etc/redis/redis.conf
owner: redis
group: redis
mode: '0640'
notify: [restart redis]
# --- Firewall (optional but recommended) ---
- name: Open Redis port 6379 only for allowed CIDR (if defined)
when: allowed_cidr | length > 0
ansible.builtin.command:
cmd: firewall-cmd --add-rich-rule='rule family=ipv4 source address={{ allowed_cidr }} port protocol=tcp port=6379 accept' --permanent
register: fw_rule
changed_when: "'success' in fw_rule.stdout"
failed_when: fw_rule.rc not in [0, 252] # 252 when rule already exists
- name: Reload firewalld to apply rules
when: allowed_cidr | length > 0
ansible.builtin.command: firewall-cmd --reload
# --- SELinux (defaults work with 6379; adjust if you change the port) ---
# Example if you change the port:
# - name: Allow custom Redis port in SELinux
# community.general.seport:
# ports: 6380
# proto: tcp
# setype: redis_port_t
# state: present
# --- Service Management ---
- name: Enable and start Redis
ansible.builtin.systemd:
name: redis
enabled: yes
state: started
handlers:
- name: restart redis
ansible.builtin.systemd:
name: redis
state: restarted
|
Why these hardening steps?
- Bind address: Restricts where Redis listens (default is 127.0.0.1). Expose only if needed.
- Protected mode: Blocks dangerous commands from unauthenticated or remote clients.
requirepass
: Enforces authentication. Use a long, unique password (store via Vault).- Disable FLUSH/CONFIG/SHUTDOWN*: Prevent accidental or malicious data loss/admin actions.
- Permissions on
redis.conf
: Prevents credential leakage. - Systemd supervision: Integrates process lifecycle with the OS for robust operation.
- Firewalld/SELinux: Defense‑in‑depth around the network and OS policy layers.
Inventory file
Create hosts.yaml
and make sure Ansible can SSH to your Rocky 9 host.
1
2
3
4
5
6
7
| all:
children:
redis_servers:
hosts:
rocky9-redis:
ansible_host: 10.2.11.10
ansible_user: rocky
|
Adjust ansible_user
and IP to your environment. You can also use SSH keys and group_vars.
Running the playbook
Save the playbook as setup-redis-rocky9.yml
and run:
1
| ansible-playbook -i hosts.yaml setup-redis-rocky9.yml -vv
|
For secrets, prefer Ansible Vault:
1
2
| ansible-vault create group_vars/redis_servers/vault.yml
# add: redis_password: "your-strong-secret"
|
Then load it in the playbook or via vars_files
.
Verification
1
2
| systemctl status redis --no-pager
ss -ltpn | grep 6379
|
1
2
| redis-cli -a 'your-strong-secret' ping
# PONG
|
- Test from a remote machine (only if you exposed the port and firewall allows it):
1
| redis-cli -h <redis_host> -a 'your-strong-secret' PING
|
- Confirm disabled commands:
1
2
| redis-cli -a 'your-strong-secret' FLUSHALL
# (error) ERR unknown command 'FLUSHALL'
|
- Disable Transparent Huge Pages (THP) and set
vm.overcommit_memory=1
if you push high memory load. These can reduce latency spikes. - Tune
tcp-keepalive
, maxmemory
+ eviction policy if using Redis as a cache. - Persist with RDB/AOF based on your durability/SLA requirements.
Example tasks (optional):
1
2
3
4
5
6
7
8
9
10
| - name: Set vm.overcommit_memory=1
ansible.posix.sysctl:
name: vm.overcommit_memory
value: '1'
state: present
reload: yes
- name: Disable THP at boot (grubby)
ansible.builtin.command: grubby --update-kernel=ALL --args="transparent_hugepage=never"
when: ansible_facts['os_family'] == 'RedHat'
|
Reboot required for THP change.
Troubleshooting
- Unable to connect remotely: Verify
bind
, protected-mode
, requirepass
, firewall, and SELinux. - Module enable issues: Ensure Remi repo is installed and network egress is allowed.
- Authentication errors: Confirm the password in
redis.conf
matches your variable.
Conclusion
You now have a production‑ready Redis 8 on Rocky Linux 9, deployed with Ansible and hardened with strong defaults. The approach is idempotent and easy to re‑run, ensuring consistency across environments. For Ubuntu users or more Ansible patterns, see my earlier Redis guides linked above.