How to install and configure Redis on Ubuntu 16.04

Redis is an in-memory data structure store that is commonly used as a database, cache or a message broker. What makes Redis powerful is its optional ability to have data persistence. Therefore your dataset isn’t lost during restarts of the service.

The article below will discuss how to install, configure and provide basic security for Redis. From there, it will go into the basic use cases I use it for on a daily basis, session storing and general caching.

Installation

[[email protected] ~]# apt-get update
[[email protected] ~]# apt-get install redis-server redis-tools

Configuration

Redis listens on port 6379 by default and needs some additional configuration to ensure that it is secured. If you do not protect Redis with a firewall, authentication and have it listen only on a private network, there is a extremely high risk of leaking sensitive data.

First, set Redis to only listen on your private network. Redis does not have any type of encryption built in, it is important that the data is transferred only over private networks or secured tunnels. Set Redis to listen on the private interface by:

[[email protected] ~]# vim /etc/redis/redis.conf
...
bind redis_servers_private_IP
...

If Redis is being installed on a stand alone web server and will not need to accept connections from other clients, then you can set Redis to listen on the local socket instead by commenting out the bind value and setting up a socket by:

[[email protected] ~]# mkdir /var/run/redis
[[email protected] ~]# chown redis:redis /var/run/redis
[[email protected] ~]# vim /etc/redis/redis.conf
...
# bind 127.0.0.1
unixsocket /var/run/redis/redis.sock
unixsocketperm 777

If you do not have a dedicated firewall, use your OS’s built in firewall to only allow in connections from trusted web servers using their internal IP’s. Some quick examples are below:

# iptables
[[email protected] ~]# vim /etc/sysconfig/iptables
...
-A INPUT -p tcp -m tcp --dport 6379 -s client_server_private_IP -m comment --comment "redis" -j ACCEPT
[[email protected] ~]# service iptables restart

# ufw
[[email protected] ~]# ufw allow from client_server_private_IP/32 to any port 6379

To protect Redis further, setup authentication which is a built in security feature. This will force clients to authenticate before being granted access. Use a tool such as apg or pwgen to create a secure password. Set the password within Redis by:

[[email protected] ~]# vim /etc/redis/redis.conf
...
requirepass your_secure_password_here
...

[[email protected] ~]# systemctl restart redis

Then test to ensure the password works by:

# This should fail
[[email protected] ~]# redis_cli
127.0.0.1:6379> set key1 10
(error) NOAUTH Authentication required.

# This should work
[[email protected] ~]# redis-cli
127.0.0.1:6379> auth your_secure_password_here
127.0.0.1:6379> set key1 10
OK
127.0.0.1:6379> get key1
"10"

Next, we need to secure the file permissions for Redis. The redis.conf contains the password for redis, so that file shouldn’t be readable by everyone. We also want to lock down the Redis data directory. Lock down the permissions on Redis by:

[[email protected] ~]# chmod 700 /var/lib/redis
[[email protected] ~]# chown redis:redis /etc/redis/redis.conf
[[email protected] ~]# chmod 600 /etc/redis/redis.conf
[[email protected] ~]# systemctl restart redis

The Official Redis Administration Guide recommends to disable Transparent Huge Pages (THP). This can be performed live by:

[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag

And disable Transparent Huge Pages (THP) at boot time by making a systemd unit file:

[[email protected] ~]# vim /etc/systemd/system/disable-thp.service
[Unit]
Description=Disable Transparent Huge Pages (THP)

[Service]
Type=simple
ExecStart=/bin/sh -c "echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled && echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag"

[Install]
WantedBy=multi-user.target

[[email protected] ~]# systemctl daemon-reload
[[email protected] ~]# systemctl start disable-thp
[[email protected] ~]# systemctl enable disable-thp

The Official Redis Administration Guide also recommends setting the following sysctl:

[[email protected] ~]# sysctl vm.overcommit_memory=1
[[email protected] ~]# vim /etc/sysctl.conf
...
vm.overcommit_memory = 1
...

Redis Configurations

Now that Redis is installed and secured, its time to tune it for your application’s needs. There are typically 2 types of Redis cache configurations I see:
Session store
Database cache or full page cache

The session cache allows for a single locations for your application to store sessions that would normally be stored by PHP on the local file system. It is important to ensure that the data is saved to disk so you don’t lose all the sessions between restarts of Redis. This is one of the primary advantages of using Redis over Memcached.

The full page cache is great for caching SQL queries or for serving as a full page cache for applications such as Magento or WordPress, caching images, videos, html, css or js. Generally this type of cache doesn’t need to be persistent across restarts of Redis.

Redis Configurations – Session store

When using Redis as a session store, you want to ensure that the Redis data persists between restarts of Redis. Otherwise your users could be left wondering why their shopping carts all of a sudden vanished. The example below will have disk persistence enabled with a memory limit of 1.5G (1536M).

These settings may or may not work for you! Adjust your settings to meet your environments requirements!

[[email protected] ~]# vim /etc/redis/redis.conf
...
## Ensure disk persistence is enabled
save 900 1
save 300 10
save 60 10000
...
## Set the max memory
maxmemory 1536mb
...

[[email protected] ~]# service redis restart

Redis Configurations – Database cache or full page cache

When using Redis for database caching or as a FPC for applications like WordPress or Magento, I disable disk persistence. This means the cache will only be stored in memory and lost whenever redis restart. I also set the memory limit to 1.5G (1536M) for starters and adjust accordingly from there. Since I am only storing cached data, I can avoid out of memory issues by allowing Redis to automatically remove the oldest cache entries using the maxmemory-policy allkeys-lru. Read up on the Redis supported eviction policies here.

These settings may or may not work for you! Adjust your settings to meet your environments requirements! Remember, this example assumes everything in Redis can be lost when the service restarts and the eviction policy will remove the least used keys out of all the data. I typically find this works for Magento and WordPress redis setups:

[[email protected] ~]# vim /etc/redis/redis.conf
...
## Disable disk persistence
#save 900 1
#save 300 10
#save 60 10000
...
## Set the max memory
maxmemory 1536mb
...
## Update the eviction policy
maxmemory-policy allkeys-lru
...

[[email protected] ~]# systemctl restart redis

Multiple Redis Configurations

Redis has the ability to utilize multiple caching configurations with their own settings. The only requirement is that each instance of Redis listens on a unique port, has a unique pid, and of course has its own config and startup script. In the example below, we are going to have 2 redis instances running called redis-sessions and redis-cache. To avoid confusion, we will disable the original redis instance.

# Create 2 new configs and modify the values accordingly
[[email protected] ~]# cp /etc/redis/redis.conf /etc/redis/redis-session.conf
[[email protected] ~]# vim /etc/redis/redis-session.conf
...
pidfile /var/run/redis-session.pid
port 6379
logfile /var/log/redis/redis-session.log
dir /var/lib/redis-session
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-session.sock
unixsocketperm 777
...

[[email protected] ~]# cp /etc/redis/redis.conf /etc/redis/redis-cache.conf
[[email protected] ~]# vim /etc/redis/redis-cache.conf
...
pidfile /var/run/redis-cache.pid
port 6380
logfile /var/log/redis/redis-cache.log
dir /var/lib/redis-cache
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-cache.sock
unixsocketperm 777
...

# Create directories and secure the permissions
[[email protected] ~]# mkdir /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chown redis:redis /var/lib/redis-session /var/lib/redis-cache /etc/redis/redis-session.conf /etc/redis/redis-cache.conf
[[email protected] ~]# chmod 700 /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chmod 600 /etc/redis/redis-session.conf /etc/redis/redis-cache.conf
[[email protected] ~]# cd /etc/redis
[[email protected] ~]# cp -Rp redis-server.post-down.d redis-session.post-down.d && cp -Rp redis-server.post-up.d redis-session.post-up.d && cp -Rp redis-server.pre-down.d redis-session.pre-down.d && cp -Rp redis-server.pre-up.d  redis-session.pre-up.d 
[[email protected] ~]# cp -Rp redis-server.post-down.d redis-cache.post-down.d && cp -Rp redis-server.post-up.d redis-cache.post-up.d && cp -Rp redis-server.pre-down.d redis-cache.pre-down.d && cp -Rp redis-server.pre-up.d  redis-cache.pre-up.d 

# Create startup files
[[email protected] ~]# cp /etc/systemd/system/redis.service /etc/systemd/system/redis-session.service
[[email protected] ~]# vim /etc/systemd/system/redis-session.service
...
ExecStart=/usr/bin/redis-server /etc/redis/redis-session.conf
PIDFile=/var/run/redis/redis-session.pid
ExecStartPre=-/bin/run-parts --verbose /etc/redis/redis-session.pre-up.d
ExecStartPost=-/bin/run-parts --verbose /etc/redis/redis-session.post-up.d
ExecStop=-/bin/run-parts --verbose /etc/redis/redis-session.pre-down.d
ExecStopPost=-/bin/run-parts --verbose /etc/redis/redis-session.post-down.d
ReadWriteDirectories=-/var/lib/redis-session
Alias=redis-session.service
...
[[email protected] ~]# cp /etc/systemd/system/redis.service /etc/systemd/system/redis-cache.service
[[email protected] ~]# vim /etc/systemd/system/redis-cache.service
...
ExecStart=/usr/bin/redis-server /etc/redis/redis-cache.conf
PIDFile=/var/run/redis/redis-cache.pid
ExecStartPre=-/bin/run-parts --verbose /etc/redis/redis-cache.pre-up.d
ExecStartPost=-/bin/run-parts --verbose /etc/redis/redis-cache.post-up.d
ExecStop=-/bin/run-parts --verbose /etc/redis/redis-cache.pre-down.d
ExecStopPost=-/bin/run-parts --verbose /etc/redis/redis-cache.post-down.d
ReadWriteDirectories=-/var/lib/redis-cache
Alias=redis-cache.service
...

# Stop and disable old instance, start new instances.  Error during 'systemctl enable' appears safe to ignore as service still starts on boot.  But confirm for yourself!
[[email protected] ~]# systemctl daemon-reload
[[email protected] ~]# systemctl stop redis.service && systemctl disable redis.service
[[email protected] ~]# systemctl enable redis-session.service && systemctl start redis-session.service
[[email protected] ~]# systemctl enable redis-cache.service && systemctl start redis-cache.service

# Finally, edit the /etc/redis/redis-session.conf and /etc/redis/redis-cache.conf using the instructions earlier in this article for configuring sessions and db cache.

Client setup

The typical use cases I run into on a day to day basis are clients using Redis for their PHP application. Redis can be used to store cached content, or it can be used to centrally store sessions. Therefore these examples will be PHP focused.

Client setup – General data caching

For storing data, there is nothing that needs to be configured on the client side. The application code itself is what controls storing content within Redis.

Client setup – Storing sessions in Redis

To have Redis act as a central server for sessions, some additional configuration is needed on each client web server. Install php-memcache for your version of PHP. Assuming the default PHP version is installed from the package manager, you can install it by:

[[email protected] ~]# apt-get update
[[email protected] ~]# apt-get install php-redis redis-tools
[[email protected] ~]# service apache2 graceful
[[email protected] ~]# php -m |grep redis

Then update the php.ini as follows:

session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=your_secure_password_here"

Test to ensure sessions are now being stored in Redis:

[[email protected] ~]# vim /var/www/html/test-sessions.php
<?php
session_start();
?>
Created a session

Then run the following on the command line and confirm the returned numbers increment as shown below:

[[email protected] ~]# curl localhost/test-sessions.php && redis-cli -a your_secure_password_here keys '*' |grep SESSION | wc -l
10
Created a session
11

Troubleshooting

Confirm Redis is online:

[[email protected] ~]# redis-cli ping

How to connect using redis-cli when redis is running on a different server or using a different port:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here

Sometimes you may need to flush the Redis cache. Before doing this, make sure you are connecting to the right instance of Redis since there could be multiple instances running. An example is below:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here
FLUSHALL

To get some useful stats on Redis, run:

[[email protected] ~]# redis-cli
info

To get memory specific stats, run:

[[email protected] ~]# redis-cli
info memory
127.0.0.1:6379> info memory
# Memory
used_memory:488315720
used_memory_human:465.69M
used_memory_rss:499490816
used_memory_peak:505227288
used_memory_peak_human:481.82M
used_memory_lua:36864
mem_fragmentation_ratio:1.02
mem_allocator:jemalloc-3.6.0

To increase the memory limit assigned to Redis without restarting the service, the example shown below will increase the memory allocated dynamically from 1G to 2G without a restart of the Redis:

[[email protected] ~]# redis-cli
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "1000000000"
127.0.0.1:6379> config set maxmemory 2g
OK
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "2000000000"

Regarding performance issues with Redis, there are a number of factors that need to be account for, too many for this article. Redis published an excellent article that goes into various things that could cause latency with Redis at:
https://redis.io/topics/latency

How to install and configure Redis on Ubuntu 14.04

Redis is an in-memory data structure store that is commonly used as a database, cache or a message broker. What makes Redis powerful is its optional ability to have data persistence. Therefore your dataset isn’t lost during restarts of the service.

The article below will discuss how to install, configure and provide basic security for Redis. From there, it will go into the basic use cases I use it for on a daily basis, session storing and general caching.

Installation

[[email protected] ~]# apt-get update
[[email protected] ~]# apt-get install redis-server redis-tools

Configuration

Redis listens on port 6379 by default and needs some additional configuration to ensure that it is secured. If you do not protect Redis with a firewall, authentication and have it listen only on a private network, there is a extremely high risk of leaking sensitive data.

First, set Redis to only listen on your private network. Redis does not have any type of encryption built in, it is important that the data is transferred only over private networks or secured tunnels. Set Redis to listen on the private interface by:

[[email protected] ~]# vim /etc/redis/redis.conf
...
bind redis_servers_private_IP
...

If Redis is being installed on a stand alone web server and will not need to accept connections from other clients, then you can set Redis to listen on the local socket instead by commenting out the bind value and setting up a socket by:

[[email protected] ~]# mkdir /var/run/redis
[[email protected] ~]# chown redis:redis /var/run/redis
[[email protected] ~]# vim /etc/redis/redis.conf
...
# bind 127.0.0.1
unixsocket /var/run/redis/redis.sock
unixsocketperm 777

If you do not have a dedicated firewall, use your OS’s built in firewall to only allow in connections from trusted web servers using their internal IP’s. Some quick examples are below:

# iptables
[[email protected] ~]# vim /etc/sysconfig/iptables
...
-A INPUT -p tcp -m tcp --dport 6379 -s client_server_private_IP -m comment --comment "redis" -j ACCEPT
[[email protected] ~]# service iptables restart

# ufw
[[email protected] ~]# ufw allow from client_server_private_IP/32 to any port 6379

To protect Redis further, setup authentication which is a built in security feature. This will force clients to authenticate before being granted access. Use a tool such as apg or pwgen to create a secure password. Set the password within Redis by:

[[email protected] ~]# vim /etc/redis/redis.conf
...
requirepass your_secure_password_here
...

[[email protected] ~]# service redis restart

Then test to ensure the password works by:

# This should fail
[[email protected] ~]# redis_cli
127.0.0.1:6379> set key1 10
(error) NOAUTH Authentication required.

# This should work
[[email protected] ~]# redis-cli
127.0.0.1:6379> auth your_secure_password_here
127.0.0.1:6379> set key1 10
OK
127.0.0.1:6379> get key1
"10"

Next, we need to secure the file permissions for Redis. The redis.conf contains the password for redis, so that file shouldn’t be readable by everyone. We also want to lock down the Redis data directory. Lock down the permissions on Redis by:

[[email protected] ~]# chmod 700 /var/lib/redis
[[email protected] ~]# chown redis:redis /etc/redis/redis.conf
[[email protected] ~]# chmod 600 /etc/redis/redis.conf
[[email protected] ~]# service redis restart

The Official Redis Administration Guide recommends to disable Transparent Huge Pages (THP). This can be performed live by:

[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag

And disable Transparent Huge Pages (THP) at boot time by entering the following before the ‘exit0’:

[[email protected] ~]# vim /etc/rc.local
...
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
   echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
   echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi
...

The Official Redis Administration Guide also recommends setting the following sysctl:

[[email protected] ~]# sysctl vm.overcommit_memory=1
[[email protected] ~]# vim /etc/sysctl.conf
...
vm.overcommit_memory = 1
...

Redis Configurations

Now that Redis is installed and secured, its time to tune it for your application’s needs. There are typically 2 types of Redis cache configurations I see:
Session store
Database cache or full page cache

The session cache allows for a single locations for your application to store sessions that would normally be stored by PHP on the local file system. It is important to ensure that the data is saved to disk so you don’t lose all the sessions between restarts of Redis. This is one of the primary advantages of using Redis over Memcached.

The full page cache is great for caching SQL queries or for serving as a full page cache for applications such as Magento or WordPress, caching images, videos, html, css or js. Generally this type of cache doesn’t need to be persistent across restarts of Redis.

Redis Configurations – Session store

When using Redis as a session store, you want to ensure that the Redis data persists between restarts of Redis. Otherwise your users could be left wondering why their shopping carts all of a sudden vanished. The example below will have disk persistence enabled with a memory limit of 1.5G (1536M).

These settings may or may not work for you! Adjust your settings to meet your environments requirements!

[[email protected] ~]# vim /etc/redis/redis.conf
...
## Ensure disk persistence is enabled
save 900 1
save 300 10
save 60 10000
...
## Set the max memory
maxmemory 1536mb
...

[[email protected] ~]# service redis restart

Redis Configurations – Database cache or full page cache

When using Redis for database caching or as a FPC for applications like WordPress or Magento, I disable disk persistence. This means the cache will only be stored in memory and lost whenever redis restart. I also set the memory limit to 1.5G (1536M) for starters and adjust accordingly from there. Since I am only storing cached data, I can avoid out of memory issues by allowing Redis to automatically remove the oldest cache entries using the maxmemory-policy allkeys-lru. Read up on the Redis supported eviction policies here.

These settings may or may not work for you! Adjust your settings to meet your environments requirements! Remember, this example assumes everything in Redis can be lost when the service restarts and the eviction policy will remove the least used keys out of all the data. I typically find this works for Magento and WordPress redis setups:

[[email protected] ~]# vim /etc/redis/redis.conf
...
## Disable disk persistence
#save 900 1
#save 300 10
#save 60 10000
...
## Set the max memory
maxmemory 1536mb
...
## Update the eviction policy
maxmemory-policy allkeys-lru
...

[[email protected] ~]# service redis restart

Multiple Redis Configurations

Redis has the ability to utilize multiple caching configurations with their own settings. The only requirement is that each instance of Redis listens on a unique port, has a unique pid, and of course has its own config and startup script. In the example below, we are going to have 2 redis instances running called redis-sessions and redis-cache. To avoid confusion, we will disable the original redis instance.

# Create 2 new configs and modify the values accordingly
[[email protected] ~]# cp /etc/redis/redis.conf /etc/redis/redis-session.conf
[[email protected] ~]# vim /etc/redis/redis-session.conf
...
pidfile /var/run/redis/redis_session.pid
port 6379
logfile /var/log/redis/redis-session.log
dir /var/lib/redis-session
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-session.sock
unixsocketperm 777
...

[[email protected] ~]# cp /etc/redis/redis.conf /etc/redis/redis-cache.conf
[[email protected] ~]# vim /etc/redis/redis-cache.conf
...
pidfile /var/run/redis_cache.pid
port 6380
logfile /var/log/redis/redis-cache.log
dir /var/lib/redis-cache
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-cache.sock
unixsocketperm 777
...

# Create directories and secure the permissions
[[email protected] ~]# mkdir /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chown redis:redis /var/lib/redis-session /var/lib/redis-cache /etc/redis/redis-session.conf /etc/redis/redis-cache.conf
[[email protected] ~]# chmod 700 /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chmod 600 /etc/redis/redis-session.conf /etc/redis/redis-cache.conf

# Create startup files
[[email protected] ~]# cp /etc/init.d/redis-server /etc/init.d/redis-session
[[email protected] ~]# vim /etc/init.d/redis-session
...
DAEMON_ARGS=/etc/redis/redis-session.conf
PIDFILE=$RUNDIR/redis-session.pid
...
[[email protected] ~]# cp /etc/init.d/redis-server /etc/init.d/redis-cache
[[email protected] ~]# vim /etc/init.d/redis-cache
...
DAEMON_ARGS=/etc/redis/redis-cache.conf
PIDFILE=$RUNDIR/redis-cache.pid
...

# Stop and disable old instance, start new instances
[[email protected] ~]# service redis-server stop && update-rc.d redis-server disable
[roo[email protected] ~]# service redis-session start && update-rc.d redis-session defaults
[[email protected] ~]# service redis-cache start && update-rc.d redis-cache defaults

# Finally, edit the /etc/redis/redis-session.conf and /etc/redis/redis-cache.conf using the instructions earlier in this article for configuring sessions and db cache.

Client setup

The typical use cases I run into on a day to day basis are clients using Redis for their PHP application. Redis can be used to store cached content, or it can be used to centrally store sessions. Therefore these examples will be PHP focused.

Client setup – General data caching

For storing data, there is nothing that needs to be configured on the client side. The application code itself is what controls storing content within Redis.

Client setup – Storing sessions in Redis

To have Redis act as a central server for sessions, some additional configuration is needed on each client web server. Install php-memcache for your version of PHP. Assuming the default PHP version is installed from the package manager, you can install it by:

[[email protected] ~]# apt-get update
[[email protected] ~]# apt-get install php-redis redis-tools
[[email protected] ~]# service apache2 graceful
[[email protected] ~]# php -m |grep redis

Then update the php.ini as follows:

session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=your_secure_password_here"

Test to ensure sessions are now being stored in Redis:

[[email protected] ~]# vim /var/www/html/test-sessions.php
<?php
session_start();
?>
Created a session

Then run the following on the command line and confirm the returned numbers increment as shown below:

[[email protected] ~]# curl localhost/test-sessions.php && redis-cli -a your_secure_password_here keys '*' |grep SESSION | wc -l
10
Created a session
11

Troubleshooting

Confirm Redis is online:

[[email protected] ~]# redis-cli ping

How to connect using redis-cli when redis is running on a different server or using a different port:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here

Sometimes you may need to flush the Redis cache. Before doing this, make sure you are connecting to the right instance of Redis since there could be multiple instances running. An example is below:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here
FLUSHALL

To get some useful stats on Redis, run:

[[email protected] ~]# redis-cli
info

To get memory specific stats, run:

[[email protected] ~]# redis-cli
info memory
127.0.0.1:6379> info memory
# Memory
used_memory:488315720
used_memory_human:465.69M
used_memory_rss:499490816
used_memory_peak:505227288
used_memory_peak_human:481.82M
used_memory_lua:36864
mem_fragmentation_ratio:1.02
mem_allocator:jemalloc-3.6.0

To increase the memory limit assigned to Redis without restarting the service, the example shown below will increase the memory allocated dynamically from 1G to 2G without a restart of the Redis:

[[email protected] ~]# redis-cli
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "1000000000"
127.0.0.1:6379> config set maxmemory 2g
OK
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "2000000000"

Regarding performance issues with Redis, there are a number of factors that need to be account for, too many for this article. Redis published an excellent article that goes into various things that could cause latency with Redis at:
https://redis.io/topics/latency

How to install and configure Redis on CentOS 7

Redis is an in-memory data structure store that is commonly used as a database, cache or a message broker. What makes Redis powerful is its optional ability to have data persistence. Therefore your dataset isn’t lost during restarts of the service.

The article below will discuss how to install, configure and provide basic security for Redis. From there, it will go into the basic use cases I use it for on a daily basis, session storing and general caching.

Installation

[[email protected] ~]# yum install epel-release
[[email protected] ~]# yum install redis
[[email protected] ~]# systemctl enable redis
[[email protected] ~]# systemctl start redis.service
[[email protected] ~]# redis-cli ping

Configuration

Redis listens on port 6379 by default and needs some additional configuration to ensure that it is secured. If you do not protect Redis with a firewall, authentication and have it listen only on a private network, there is a extremely high risk of leaking sensitive data.

First, set Redis to only listen on your private network. Redis does not have any type of encryption built in, it is important that the data is transferred only over private networks or secured tunnels. Set Redis to listen on the private interface by:

[[email protected] ~]# vim /etc/redis.conf
...
bind redis_servers_private_IP
...

If Redis is being installed on a stand alone web server and will not need to accept connections from other clients, then you can set Redis to listen on the local socket instead by commenting out the bind value and setting up a socket by:

[[email protected] ~]# mkdir /var/run/redis
[[email protected] ~]# chown redis:redis /var/run/redis
[[email protected] ~]# vim /etc/redis.conf
...
# bind 127.0.0.1
unixsocket /var/run/redis/redis.sock
unixsocketperm 777

If you do not have a dedicated firewall, use your OS’s built in firewall to only allow in connections from trusted web servers using their internal IP’s. Some quick examples are below:

# iptables
[[email protected] ~]# vim /etc/sysconfig/iptables
...
-A INPUT -p tcp -m tcp --dport 6379 -s client_server_private_IP -m comment --comment "redis" -j ACCEPT
[[email protected] ~]# service iptables restart

# firewalld
[[email protected] ~]# firewall-cmd --permanent --new-zone=redis
[[email protected] ~]# firewall-cmd --permanent --zone=redis --add-port=6379/tcp
[[email protected] ~]# firewall-cmd --permanent --zone=redis --add-source=client_server_private_IP
[[email protected] ~]# firewall-cmd --reload

To protect Redis further, setup authentication which is a built in security feature. This will force clients to authenticate before being granted access. Use a tool such as apg or pwgen to create a secure password. Set the password within Redis by:

[[email protected] ~]# vim /etc/redis.conf
...
requirepass your_secure_password_here
...

[[email protected] ~]# systemctl restart redis

Then test to ensure the password works by:

# This should fail
[[email protected] ~]# redis_cli
127.0.0.1:6379> set key1 10
(error) NOAUTH Authentication required.

# This should work
[[email protected] ~]# redis-cli
127.0.0.1:6379> auth your_secure_password_here
127.0.0.1:6379> set key1 10
OK
127.0.0.1:6379> get key1
"10"

Next, we need to secure the file permissions for Redis. The redis.conf contains the password for redis, so that file shouldn’t be readable by everyone. We also want to lock down the Redis data directory. Lock down the permissions on Redis by:

[[email protected] ~]# chown redis:redis /var/lib/redis
[[email protected] ~]# chmod 700 /var/lib/redis
[[email protected] ~]# chown redis:redis /etc/redis.conf
[[email protected] ~]# chmod 600 /etc/redis.conf
[[email protected] ~]# systemctl restart redis

The Official Redis Administration Guide recommends to disable Transparent Huge Pages (THP). This can be performed live by:

[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag

And disable Transparent Huge Pages (THP) at boot time by making a systemd unit file:

[[email protected] ~]# vim /etc/systemd/system/disable-thp.service
[Unit]
Description=Disable Transparent Huge Pages (THP)

[Service]
Type=simple
ExecStart=/bin/sh -c "echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled && echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag"

[Install]
WantedBy=multi-user.target

[[email protected] ~]# systemctl daemon-reload
[[email protected] ~]# systemctl start disable-thp
[[email protected] ~]# systemctl enable disable-thp

The Official Redis Administration Guide also recommends setting the following sysctl:

[[email protected] ~]# sysctl vm.overcommit_memory=1
[[email protected] ~]# vim /etc/sysctl.conf
...
vm.overcommit_memory = 1
...

Redis Configurations

Now that Redis is installed and secured, its time to tune it for your application’s needs. There are typically 2 types of Redis cache configurations I see:
Session store
Database cache or full page cache

The session cache allows for a single locations for your application to store sessions that would normally be stored by PHP on the local file system. It is important to ensure that the data is saved to disk so you don’t lose all the sessions between restarts of Redis. This is one of the primary advantages of using Redis over Memcached.

The full page cache is great for caching SQL queries or for serving as a full page cache for applications such as Magento or WordPress, caching images, videos, html, css or js. Generally this type of cache doesn’t need to be persistent across restarts of Redis.

Redis Configurations – Session store

When using Redis as a session store, you want to ensure that the Redis data persists between restarts of Redis. Otherwise your users could be left wondering why their shopping carts all of a sudden vanished. The example below will have disk persistence enabled with a memory limit of 1.5G (1536M).

These settings may or may not work for you! Adjust your settings to meet your environments requirements!

[[email protected] ~]# vim /etc/redis.conf
...
## Ensure disk persistence is enabled
save 900 1
save 300 10
save 60 10000
...
## Set the max memory
maxmemory 1536mb
...

[[email protected] ~]# systemctl restart redis

Redis Configurations – Database cache or full page cache

When using Redis for database caching or as a FPC for applications like WordPress or Magento, I disable disk persistence. This means the cache will only be stored in memory and lost whenever redis restart. I also set the memory limit to 1.5G (1536M) for starters and adjust accordingly from there. Since I am only storing cached data, I can avoid out of memory issues by allowing Redis to automatically remove the oldest cache entries using the maxmemory-policy allkeys-lru. Read up on the Redis supported eviction policies here.

These settings may or may not work for you! Adjust your settings to meet your environments requirements! Remember, this example assumes everything in Redis can be lost when the service restarts and the eviction policy will remove the least used keys out of all the data. I typically find this works for Magento and WordPress redis setups:

[[email protected] ~]# vim /etc/redis.conf
...
## Disable disk persistence
#save 900 1
#save 300 10
#save 60 10000
...
## Set the max memory
maxmemory 1536mb
...
## Update the eviction policy
maxmemory-policy allkeys-lru
...

[[email protected] ~]# systemctl restart redis

Multiple Redis Configurations

Redis has the ability to utilize multiple caching configurations with their own settings. The only requirement is that each instance of Redis listens on a unique port, has a unique pid, and of course has its own config and startup script. In the example below, we are going to have 2 redis instances running called redis-sessions and redis-cache. To avoid confusion, we will disable the original redis instance.

# Create 2 new configs and modify the values accordingly
[[email protected] ~]# cp /etc/redis.conf /etc/redis-session.conf
[[email protected] ~]# vim /etc/redis-session.conf
...
pidfile /var/run/redis_session.pid
port 6379
logfile /var/log/redis/redis-session.log
dir /var/lib/redis-session
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-session.sock
unixsocketperm 777
...

[[email protected] ~]# cp /etc/redis.conf /etc/redis-cache.conf
...
pidfile /var/run/redis_cache.pid
port 6380
logfile /var/log/redis/redis-cache.log
dir /var/lib/redis-cache
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-cache.sock
unixsocketperm 777
...

# Create directories and secure the permissions
[[email protected] ~]# mkdir /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chown redis:redis /var/lib/redis-session /var/lib/redis-cache /etc/redis-session.conf /etc/redis-cache.conf
[[email protected] ~]# chmod 700 /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chmod 600 /etc/redis-session.conf /etc/redis-cache.conf

# Create startup files
[[email protected] ~]# cp /usr/lib/systemd/system/redis.service /usr/lib/systemd/system/redis-session.service
[[email protected] ~]# vim /usr/lib/systemd/system/redis-session.service
...
ExecStart=/usr/bin/redis-server /etc/redis-session.conf --daemonize no
...
[[email protected] ~]# cp /usr/lib/systemd/system/redis.service /usr/lib/systemd/system/redis-cache.service
[[email protected] ~]# vim /usr/lib/systemd/system/redis-cache.service
...
ExecStart=/usr/bin/redis-server /etc/redis-cache.conf --daemonize no
...

# Stop and disable old instance, start new instances
[[email protected] ~]# systemctl daemon-reload
[[email protected] ~]# systemctl stop redis.service && systemctl disable redis.service
[[email protected] ~]# systemctl enable redis-session.service && systemctl start redis-session.service
[[email protected] ~]# systemctl enable redis-cache.service && systemctl start redis-cache.service

# Finally, edit the /etc/redis-session.conf and /etc/redis-cache.conf using the instructions earlier in this article for configuring sessions and db cache.

Client setup

The typical use cases I run into on a day to day basis are clients using Redis for their PHP application. Redis can be used to store cached content, or it can be used to centrally store sessions. Therefore these examples will be PHP focused.

Client setup – General data caching

For storing data, there is nothing that needs to be configured on the client side. The application code itself is what controls storing content within Redis.

Client setup – Storing sessions in Redis

To have Redis act as a central server for sessions, some additional configuration is needed on each client web server. Install php-memcache for your version of PHP. Assuming the default PHP version is installed from the package manager, you can install it by:

[[email protected] ~]# yum install php-pecl-redis
[[email protected] ~]# service httpd graceful
[[email protected] ~]# php -m |grep redis

Then update the php.ini as follows:

session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=your_secure_password_here"

On CentOS and Red Hat servers, depending on what version of PHP was installed and how, you may have to update another file as it will override the php.ini. Only change this if the values exist and are configured for files:

[[email protected] ~]# vim /etc/httpd/conf.d/php.conf
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=your_secure_password_here"

Test to ensure sessions are now being stored in Redis:

[[email protected] ~]# vim /var/www/html/test-sessions.php
<?php
session_start();
?>
Created a session

Then run the following on the command line and confirm the returned numbers increment as shown below:

[[email protected] ~]# curl localhost/test-sessions.php && redis-cli -a your_secure_password_here keys '*' |grep SESSION | wc -l
10
Created a session
11

Troubleshooting

Confirm Redis is online:

[[email protected] ~]# redis-cli ping

How to connect using redis-cli when redis is running on a different server or using a different port:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here

Sometimes you may need to flush the Redis cache. Before doing this, make sure you are connecting to the right instance of Redis since there could be multiple instances running. An example is below:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here
FLUSHALL

To get some useful stats on Redis, run:

[[email protected] ~]# redis-cli
info

To get memory specific stats, run:

[[email protected] ~]# redis-cli
info memory
127.0.0.1:6379> info memory
# Memory
used_memory:488315720
used_memory_human:465.69M
used_memory_rss:499490816
used_memory_peak:505227288
used_memory_peak_human:481.82M
used_memory_lua:36864
mem_fragmentation_ratio:1.02
mem_allocator:jemalloc-3.6.0

To increase the memory limit assigned to Redis without restarting the service, the example shown below will increase the memory allocated dynamically from 1G to 2G without a restart of the Redis:

[[email protected] ~]# redis-cli
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "1000000000"
127.0.0.1:6379> config set maxmemory 2g
OK
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "2000000000"

Regarding performance issues with Redis, there are a number of factors that need to be account for, too many for this article. Redis published an excellent article that goes into various things that could cause latency with Redis at:
https://redis.io/topics/latency

How to install and configure Redis on CentOS 6

Redis is an in-memory data structure store that is commonly used as a database, cache or a message broker. What makes Redis powerful is its optional ability to have data persistence. Therefore your dataset isn’t lost during restarts of the service.

The article below will discuss how to install, configure and provide basic security for Redis. From there, it will go into the basic use cases I use it for on a daily basis, session storing and general caching.

Installation

[[email protected] ~]# yum install epel-release
[[email protected] ~]# yum install redis
[[email protected] ~]# chkconfig redis on
[[email protected] ~]# service redis start
[[email protected] ~]# redis-cli ping

Configuration

Redis listens on port 6379 by default and needs some additional configuration to ensure that it is secured. If you do not protect Redis with a firewall, authentication and have it listen only on a private network, there is a extremely high risk of leaking sensitive data.

First, set Redis to only listen on your private network. Redis does not have any type of encryption built in, it is important that the data is transferred only over private networks or secured tunnels. Set Redis to listen on the private interface by:

[[email protected] ~]# vim /etc/redis.conf
...
bind redis_servers_private_IP
...

If Redis is being installed on a stand alone web server and will not need to accept connections from other clients, then you can set Redis to listen on the local socket instead by commenting out the bind value and setting up a socket by:

[[email protected] ~]# mkdir /var/run/redis
[[email protected] ~]# chown redis:redis /var/run/redis
[[email protected] ~]# vim /etc/redis.conf
...
# bind 127.0.0.1
unixsocket /var/run/redis/redis.sock
unixsocketperm 777

If you do not have a dedicated firewall, use your OS’s built in firewall to only allow in connections from trusted web servers using their internal IP’s. Some quick examples are below:

# iptables
[[email protected] ~]# vim /etc/sysconfig/iptables
...
-A INPUT -p tcp -m tcp --dport 6379 -s client_server_private_IP -m comment --comment "redis" -j ACCEPT
[[email protected] ~]# service iptables restart

To protect Redis further, setup authentication which is a built in security feature. This will force clients to authenticate before being granted access. Use a tool such as apg or pwgen to create a secure password. Set the password within Redis by:

[[email protected] ~]# vim /etc/redis.conf
...
requirepass your_secure_password_here
...

[[email protected] ~]# service redis restart

Then test to ensure the password works by:

# This should fail
[[email protected] ~]# redis_cli
127.0.0.1:6379> set key1 10
(error) NOAUTH Authentication required.

# This should work
[[email protected] ~]# redis-cli
127.0.0.1:6379> auth your_secure_password_here
127.0.0.1:6379> set key1 10
OK
127.0.0.1:6379> get key1
"10"

Next, we need to secure the file permissions for Redis. The redis.conf contains the password for redis, so that file shouldn’t be readable by everyone. We also want to lock down the Redis data directory. Lock down the permissions on Redis by:

[[email protected] ~]# chown redis:redis /var/lib/redis
[[email protected] ~]# chmod 700 /var/lib/redis
[[email protected] ~]# chown redis:redis /etc/redis.conf
[[email protected] ~]# chmod 600 /etc/redis.conf
[[email protected] ~]# service redis restart

The Official Redis Administration Guide recommends disabling Transparent Huge Pages (THP). This can be performed live by:

[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
[[email protected] ~]# echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag

And disable Transparent Huge Pages (THP) at boot time by entering the following before the ‘exit0’:

[[email protected] ~]# vim /etc/rc.local
...
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
   echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
   echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi
...

The Official Redis Administration Guide also recommends setting the following sysctl:

[[email protected] ~]# sysctl vm.overcommit_memory=1
[[email protected] ~]# vim /etc/sysctl.conf
...
vm.overcommit_memory = 1
...

Redis Configurations

Now that Redis is installed and secured, its time to tune it for your application’s needs. There are typically 2 types of Redis cache configurations I see:
Session store
Database cache or full page cache

The session cache allows for a single locations for your application to store sessions that would normally be stored by PHP on the local file system. It is important to ensure that the data is saved to disk so you don’t lose all the sessions between restarts of Redis. This is one of the primary advantages of using Redis over Memcached.

The full page cache is great for caching SQL queries or for serving as a full page cache for applications such as Magento or WordPress, caching images, videos, html, css or js. Generally this type of cache doesn’t need to be persistent across restarts of Redis.

Redis Configurations – Session store

When using Redis as a session store, you want to ensure that the Redis data persists between restarts of Redis. Otherwise your users could be left wondering why their shopping carts all of a sudden vanished. The example below will have disk persistence enabled with a memory limit of 1.5G (1536M).

These settings may or may not work for you! Adjust your settings to meet your environments requirements!

[[email protected] ~]# vim /etc/redis.conf
...
## Ensure disk persistence is enabled
save 900 1
save 300 10
save 60 10000
...
## Set the max memory
maxmemory 1536mb
...

[[email protected] ~]# service redis restart

Redis Configurations – Database cache or full page cache

When using Redis for database caching or as a FPC for applications like WordPress or Magento, I disable disk persistence. This means the cache will only be stored in memory and lost whenever Redis restart. I also set the memory limit to 1.5G (1536M) for starters and adjust accordingly from there. Since I am only storing cached data, I can avoid out of memory issues by allowing Redis to automatically remove the oldest cache entries using the maxmemory-policy allkeys-lru. Read up on the Redis supported eviction policies here.

These settings may or may not work for you! Adjust your settings to meet your environments requirements! Remember, this example assumes everything in Redis can be lost when the service restarts and the eviction policy will remove the least used keys out of all the data. I typically find this works for Magento and WordPress Redis setups:

[[email protected] ~]# vim /etc/redis.conf
...
## Disable disk persistence
#save 900 1
#save 300 10
#save 60 10000
...
## Set the max memory
maxmemory 1536mb
...
## Update the eviction policy
maxmemory-policy allkeys-lru
...

[[email protected] ~]# service redis restart

Multiple Redis Configurations

Redis has the ability to utilize multiple caching configurations with their own settings. The only requirement is that each instance of Redis listens on a unique port, has a unique pid, and of course has its own config and startup script. In the example below, we are going to have 2 Redis instances running called redis-sessions and redis-cache. To avoid confusion, we will disable the original Redis instance.

# Create 2 new configs and modify the values accordingly
[[email protected] ~]# cp /etc/redis.conf /etc/redis-session.conf
[[email protected] ~]# vim /etc/redis-session.conf
...
pidfile /var/run/redis_session.pid
port 6379
logfile /var/log/redis/redis-session.log
dir /var/lib/redis-session
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-session.sock
unixsocketperm 777
...

[[email protected] ~]# cp /etc/redis.conf /etc/redis-cache.conf
...
pidfile /var/run/redis_cache.pid
port 6380
logfile /var/log/redis/redis-cache.log
dir /var/lib/redis-cache
...
# If unixsocket is uncommented, then update to:
unixsocket /var/run/redis/redis-cache.sock
unixsocketperm 777
...

# Create directories and secure the permissions
[[email protected] ~]# mkdir /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chown redis:redis /var/lib/redis-session /var/lib/redis-cache /etc/redis-session.conf /etc/redis-cache.conf
[[email protected] ~]# chmod 700 /var/lib/redis-session /var/lib/redis-cache
[[email protected] ~]# chmod 600 /etc/redis-session.conf /etc/redis-cache.conf

# Create startup files
[[email protected] ~]# cp /etc/init.d/redis /etc/init.d/redis-session
[[email protected] ~]# vim /etc/init.d/redis-session
...
pidfile="/var/run/redis/redis_session.pid"
REDIS_CONFIG="/etc/redis-session.conf"
...
[[email protected] ~]# cp /etc/init.d/redis /etc/init.d/redis-cache
[[email protected] ~]# vim /etc/init.d/redis-cache
...
pidfile="/var/run/redis/redis_cache.pid"
REDIS_CONFIG="/etc/redis-cache.conf"
...

# Stop and disable old instance, start new instances
[[email protected] ~]# service redis stop && chkconfig redis off
[[email protected] ~]# service redis-session start && chkconfig redis-session on
[[email protected] ~]# service redis-cache start && chkconfig redis-session on

# Finally, edit the /etc/redis-session.conf and /etc/redis-cache.conf using the instructions earlier in this article for configuring sessions and db cache.

Client setup

The typical use cases I run into on a day to day basis are clients using Redis for their PHP application. Redis can be used to store cached content, or it can be used to centrally store sessions. Therefore these examples will be PHP focused.

Client setup – General data caching

For storing data, there is nothing that needs to be configured on the client side. The application code itself is what controls storing content within Redis.

Client setup – Storing sessions in Redis

To have Redis act as a central server for sessions, some additional configuration is needed on each client web server. Install php-memcache for your version of PHP. Assuming the default PHP version is installed from the package manager, you can install it by:

[[email protected] ~]# yum install php-pecl-redis
[[email protected] ~]# service httpd graceful
[[email protected] ~]# php -m |grep redis

Then update the php.ini as follows:

session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=your_secure_password_here"

On CentOS and Red Hat servers, depending on what version of PHP was installed and how, you may have to update another file as it will override the php.ini. Only change this if the values exist and are configured for files:

[[email protected] ~]# vim /etc/httpd/conf.d/php.conf
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=your_secure_password_here"

Test to ensure sessions are now being stored in Redis:

[[email protected] ~]# vim /var/www/html/test-sessions.php
<?php
session_start();
?>
Created a session

Then run the following on the command line and confirm the returned numbers increment as shown below:

[[email protected] ~]# curl localhost/test-sessions.php && redis-cli -a your_secure_password_here keys '*' |grep SESSION | wc -l
10
Created a session
11

Troubleshooting

Confirm Redis is online:

[[email protected] ~]# redis-cli ping

How to connect using redis-cli when redis is running on a different server or using a different port:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here

Sometimes you may need to flush the Redis cache. Before doing this, make sure you are connecting to the right instance of Redis since there could be multiple instances running. An example is below:

[[email protected] ~]# redis-cli -h ip_of_redis_server -p port_number_here
FLUSHALL

To get some useful stats on Redis, run:

[[email protected] ~]# redis-cli
info

To get memory specific stats, run:

[[email protected] ~]# redis-cli
info memory
127.0.0.1:6379> info memory
# Memory
used_memory:488315720
used_memory_human:465.69M
used_memory_rss:499490816
used_memory_peak:505227288
used_memory_peak_human:481.82M
used_memory_lua:36864
mem_fragmentation_ratio:1.02
mem_allocator:jemalloc-3.6.0

To increase the memory limit assigned to Redis without restarting the service, the example shown below will increase the memory allocated dynamically from 1G to 2G without a restart of the Redis:

[[email protected] ~]# redis-cli
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "1000000000"
127.0.0.1:6379> config set maxmemory 2g
OK
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "2000000000"

Regarding performance issues with Redis, there are a number of factors that need to be account for, too many for this article. Redis published an excellent article that goes into various things that could cause latency with Redis at:
https://redis.io/topics/latency

How to install and configure Memcached

Memcached is commonly used to alleviate backend database contention by temporarily storing recently requested database records in memory. As a result, the database traffic is reduced as Memcached is able to pull the records from cache.

Installing Memcached is quick. However there are a number of steps that need to be taken to secure Memcached. Memcached has the potential to store a lot of sensitive information, so its critical that the service is locked down properly to prevent outside access or data leakage.

Installation

# CentOS 6
[[email protected] ~]# yum install memcached
[[email protected] ~]# chkconfig memcached on
[[email protected] ~]# service memcached start

# CentOS 7
[[email protected] ~]# yum install memcached
[[email protected] ~]# systemctl enable memcached
[[email protected] ~]# systemctl start memcached

# Ubuntu 14.04 and 16.04
[[email protected] ~]# apt-get update
[[email protected] ~]# apt-get install memcached

Configuration

Memcached listens on port 11211 by default and does not have any type of restrictions built in to prevent it from being queried from the public internet. If you do not protect Memcached with a firewall, there is a extremely high risk of leaking sensitive data. In recent years, unprotected memcached servers have also been exploited to launch DDoS amplification attacks.

If you do not have a dedicated firewall, use your OS’s built in firewall to only allow in connections for trusted web servers using their internal IP’s. Some quick examples are below:

# iptables
[[email protected] ~]# vim /etc/sysconfig/iptables
...
-A INPUT -p tcp -m tcp --dport 11211 -s client_server_private_IP -m comment --comment "memcached" -j ACCEPT
service iptables restart

# firewalld
[[email protected] ~]# firewall-cmd --permanent --new-zone=memcached
[[email protected] ~]# firewall-cmd --permanent --zone=memcached --add-port=11211/tcp
[[email protected] ~]# firewall-cmd --permanent --zone=memcached --add-source=client_server_private_IP
[[email protected] ~]# firewall-cmd --reload

# ufw
[[email protected] ~]# ufw allow from client_server_private_IP/32 to any port 11211

When configuring memcached itself, there are a few options to set. In my case, to help limit exposure to the service, I want to set Memcached to only listen on my private network interface and disable UDP. I’ll also set max connections to be 16384 and set the max cachesize to be 1.5G (1536M). Adjust the connections and cachesize as needed for your situation. Below is the example using the options above:

# CentOS 6 and 7
[[email protected] ~]# vim /etc/sysconfig/memcached

# Ubuntu 14.04 and 16.04
[[email protected] ~]# vim /etc/memcached.conf

The configuration options to use to reflect the requirements above:

PORT="11211"
USER="memcached"
MAXCONN="16384"
CACHESIZE="1536"
OPTIONS="-l memcached_servers_private_IP -U 0"

Then restart memcached

[[email protected] ~]# service memcached restart

Client setup

The typical use cases I run into on a day to day basis are clients using memcached for their PHP application. Memcached can be used to store cached content, or it can be used to centrally store sessions. Therefore these examples will be PHP focused.

Client setup – General data caching

For storing data, there is nothing that needs to be configured on the client side. The application code itself is what controls storing content within Memcached. To ensure memcached can be reached to store content, create the following test script:

[[email protected] ~]# vim /var/www/html/test-memcached.php
if (class_exists('Memcache')) {
    $meminstance = new Memcache();
} else {
    $meminstance = new Memcached();
}

$meminstance->addServer("memcached_servers_private_IP", 11211);

$result = $meminstance->get("test");

if ($result) {
    echo $result;
} else {
    echo "No matching key found.  Refresh the browser to add it!";
    $meminstance->set("test", "Successfully retrieved the data!") or die("Couldn't save anything to memcached...");
}

Then run it in your browser. You can further confirm it works by running it on the command line and confirming ‘cmd_set’ increments by one indicating it was able to store the object:

[[email protected] ~]# echo stats | nc localhost 11211 | grep cmd_set; curl localhost/test-memcached.php; echo stats | nc localhost 11211 |grep cmd_set
STAT cmd_set 2
No matching key found.  Refresh the browser to add it!STAT cmd_set 3

[[email protected] ~]# echo stats | nc localhost 11211 | grep cmd_set; curl localhost/test-memcached.php; echo stats | nc localhost 11211 |grep cmd_set
STAT cmd_set 3
Successfully retrieved the data!STAT cmd_set 3

Digital Ocean has a good article that goes into far more detail on various ways to test/use memcached:
https://www.digitalocean.com/community/tutorials/how-to-install-and-use-memcache-on-ubuntu-14-04

Client setup – Storing sessions in memcached

To have memcached act as a central server for sessions, some additional configuration is needed on each client web server. Install php-memcache for your version of PHP. Assuming the default PHP version is installed from the package manager, you can install it by:

# Red Hat / CentOS:
[[email protected] ~]# yum install php-pecl-memcached
[[email protected] ~]# service httpd graceful
[[email protected] ~]# php -m |grep memcached

# Ubuntu
[[email protected] ~]# apt-get update
[[email protected] ~]# apt-get install php-pecl-memcached
[[email protected] ~]# service apache2 graceful
[[email protected] ~]# php -m |grep memcached

Then update php.ini on both server as follows:

session.save_handler = memcached
session.save_path="memcached_servers_private_IP:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

On CentOS and Red Hat servers, depending on what version of PHP was installed and how, you may have to update another file as it will override the php.ini. Only change this if the values exist and are configured for files:

[[email protected] ~]# vim /etc/httpd/conf.d/php.conf
php_value session.save_handler "memcached"
php_value session.save_path    "memcached_servers_private_IP:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

Test to ensure sessions are now being stored in memcached:

[[email protected] ~]# vim /var/www/html/test-sessions.php
<?php
session_start();
?>
Created a session

Perform the test by running the following on the command line and confirming ‘cmd_set’ increases, indicating it was able to store the session:

[[email protected] ~]# echo stats | nc localhost 11211 | grep cmd_set; curl localhost/session.php; echo stats | nc localhost 11211 |grep cmd_set
STAT cmd_set 17
Created a session
STAT cmd_set 19

Client setup – Distributed sessions across multiple Memcached instances

Building solutions that can withstand failure is always recommended. Having a Memcached server go offline that is storing sessions will most likely result in unhappy customers. Memcached does not have a built in mechanism to replicate data between multiple memcached servers. This functionality is instead done on the client side.

To allow another Memcached server to take over connections without replicating the session data, update the php.ini on the web servers with the snippet below and restart Apache:

memcache.hash_strategy = consistent
session.save_handler = memcache
memcache.allow_failover = 1
session.save_path="memcached_servers_private_IP:11211?persistent=1&weight=1&timeout=1&retry_interval=15,memcached_servers_private_IP:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

If you wanted to have automatic failure and also ensure that the sessions are replicated to each Memcached server, update the php.ini on the web servers with the snippet below and restart Apache:

memcache.hash_strategy = consistent
memcache.session_redundancy=3
memcache.allow_failover = 1
session.save_handler = memcache
session.save_path="memcached_servers_private_IP:11211?persistent=1&weight=1&timeout=1&retry_interval=15,memcached_servers_private_IP:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

Important note: To determine what memcache.session_redundacy should be set to, simply total up all the Memcached servers and add 1 to that total. So in the example above, I have 2 Memcached servers. Therefore, the memcache.session_redundacy should be set to 3.

Troubleshooting

Confirm the web server can reach the memcached server:

[[email protected] ~]# telnet memcached_servers_private_IP 11211

Verify traffic is being sent from the web servers to the memcached server:

[[email protected] ~]# tcpdump -i any port 11211

Checking memcached stats:

[[email protected] ~]# echo stats | nc localhost 11211

To see some of the more commonly used stats, use:

[[email protected] ~]# echo stats | nc localhost 11211 |grep -E 'total_connections|curr_connections|limit_maxbytes|bytes'

Check to see if you may need to increase the size of the cache. If bytes is reaching the total memory allocation defined by ‘limit_maxbytes’, you may need to increase the cachesize setting:

[[email protected] ~]# echo stats | nc localhost 11211 |grep -E 'bytes|limit_maxbytes' |grep -v bytes_ |grep -v _bytes

To flush all the data within memcache:

[[email protected] ~]# echo flush_all | nc localhost 11211

To retrieve the version of memcached:

[[email protected] ~]# echo version | nc localhost 11211

Varnish 3 – Installation and configuration

Varnish is a HTTP reverse proxy that can be installed in front of the web server to provide caching. If the VCL’s are properly configured for your site, Varnish can greatly offset the backend server load many times over.

In this guide, it is assumed you already have a running LAMP stack, and Apache’s vhost configurations are stored in /etc/httpd/vhost.d/. At the end of this guide if all goes well, you will be running Varnish 3, with Varnish listening for inbound connections on port 80, and passing any backend connections that cannot be served via cache to Apache on port 8080.

CentOS 6 – Installation and initial configuration

Install the varnish-release package repository, then install:

[[email protected] ~]# rpm --nosignature -ivh https://repo.varnish-cache.org/redhat/varnish-3.0.el6.rpm
[[email protected] ~]# yum -y install varnish

Now update your Apache ports and vhosts to 8080 since Varnish will be listening on port 80:

[[email protected] ~]# sed -i "s/Listen 80\$/Listen 8080/g" /etc/httpd/ports.conf
[[email protected] ~]# sed -i "s/NameVirtualHost \*:80\$/NameVirtualHost \*:8080/g" /etc/httpd/ports.conf
[[email protected] ~]# sed -i "s/:80>/:8080>/g" /etc/httpd/vhost.d/*

Configure Varnish to pass connections back to Apache on port 8080:

[[email protected] ~]# sed -i 's/port = "80"/port = "8080"/g' /etc/varnish/default.vcl

Then update Varnish so it listens on port 80:

[[email protected] ~]# sed -i 's/VARNISH_LISTEN_PORT=6081$/VARNISH_LISTEN_PORT=80/g' /etc/sysconfig/varnish

Finally, restart Apache and Varnish:

[[email protected] ~]# service httpd restart
[[email protected] ~]# service varnish start
[[email protected] ~]# chkconfig varnish on

Ubuntu 12.04 / 14.04 – Installation and initial configuration

First, setup the Varnish repos:

# Ubuntu 12.04
[[email protected] ~]# curl -sL http://repo.varnish-cache.org/debian/GPG-key.txt | apt-key add -
[[email protected] ~]# echo "deb http://repo.varnish-cache.org/ubuntu/ precise varnish-3.0" > /etc/apt/sources.list.d/varnish.list

# Ubuntu 14.04
[[email protected] ~]# curl -sL http://repo.varnish-cache.org/debian/GPG-key.txt | apt-key add -
[[email protected] ~]# echo "deb http://repo.varnish-cache.org/ubuntu/ trusty varnish-3.0" > /etc/apt/sources.list.d/varnish.list

Now install Varnish:

[[email protected] ~]# apt-get update
[[email protected] ~]# apt-get install varnish

Next update your Apache ports and vhosts to 8080 since Varnish will be listening on port 80:

[[email protected] ~]# sed -i "s/Listen 80\$/Listen 8080/g" /etc/apache2/ports.conf
[[email protected] ~]# sed -i "s/NameVirtualHost \*:80\$/NameVirtualHost \*:8080/g" /etc/apache2/ports.conf
[[email protected] ~]# sed -i "s/:80>/:8080>/g" /etc/apache2/sites-available/*

Configure Varnish to pass connections back to Apache on port 8080:

[[email protected] ~]# sed -i 's/port = "80"/port = "8080"/g' /etc/varnish/default.vcl

Then update Varnish so it listens on port 80:

[[email protected] ~]# sed -i 's/^DAEMON_OPTS="-a :6081/DAEMON_OPTS="-a :80/g' /etc/default/varnish
[[email protected] ~]# sed -i 's/START=no/START=yes/' /etc/default/varnish
[[email protected] ~]# service apache2 restart
[[email protected] ~]# service varnish restart

Varnish VCL configuration examples

All of the tunings take place within the vcl’s. For the purpose of this guide, we are going to just use the default varnish configuration file in /etc/varnish/default.vcl for our examples.

How to enable basic caching of static resources:

sub vcl_recv {
...
if (req.url ~ "\.(html|gif|jpg|jpeg|png|js|css)$") {
         unset req.http.cookie;
         return(lookup);
     }
 return(pass);
 }
...

If the request is coming in from CloudFlare or a load balancer, here is how to set the real IP of the client:

sub vcl_recv {
...
     if (req.restarts == 0) {
        if (req.http.x-forwarded-for) {
            set req.http.X-Forwarded-For =
                req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
     }
...

Here is an example of how to exclude things like phpmyadmin, apc.php, and server-status from being cached:

sub vcl_recv {
...
if (req.url ~ "(?i)/(phpmyadmin|apc.php|server-status)") {
      return(pass);
    }
...

Here is how you can exclude a specific URL from being cached:

sub vcl_recv {
...
     if (req.url ~ "^/example") {
     return (pass);
     }
...

Perhaps you have 30 domains on the server, and you need one of them to be excluded from the cache. Or maybe your actively working on the site. Here is how you can prevent the domain from being served through varnish:

sub vcl_recv {
...
    if (req.http.host ~ "^(www.)?domain.com") {
    return (pass);
    }
...

If you find a script running via your browser, and suspect it is timing out due to varnish, you can adjust the timeout on that specific script by:

sub vcl_recv {
...
if (req.url == "^/bigscript.php") {
    set bereq.first_byte_timeout = 10m;
}
...

Here is an example of how to never cache PUT and DELETE requests for a domain:

sub vcl_recv {
...
if ( req.http.host == "subdomain.domain.com" ) {
    if (req.method == "PUT" || req.method == "POST" || req.method == "DELETE")
    {
        return(pass);
    }
}
...

Varnish Troubleshooting

One of the most common errors I see on sites utilizing Varnish is a error message:

Error 503 Service Unavailable
Guru Meditation:
XID: 1234567

Typically Varnish is not the problem, but instead its something else such as Apache or PHP-FPM (aka the backend) not being available. If you can replicate the error in your browser, then run the following command so you can see if you can catch the issue as its happening in the logs:

[[email protected] ~]# varnishlog -d -c -m TxStatus:503

This will return a bunch of output. You are most interesting in the lines surrounding ‘FetchError’ as shown below:

   11 SessionOpen  c 192.168.1.56 60015 :80
   11 ReqStart     c 192.168.1.56 60015 311889525
   11 RxRequest    c GET
   11 RxURL        c /
   11 RxProtocol   c HTTP/1.1
   11 RxHeader     c Host: example.com
   11 RxHeader     c Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
   11 RxHeader     c Connection: keep-alive
   11 RxHeader     c Cookie: wordpress_test_cookie=WP+Cookie+check; wp-settings-time-1=1455921695
   11 RxHeader     c User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4
   11 RxHeader     c Accept-Language: en-us
   11 RxHeader     c DNT: 1
   11 RxHeader     c Accept-Encoding: gzip, deflate
   11 VCL_call     c recv pass
   11 VCL_call     c hash
   11 Hash         c /
   11 Hash         c example.com
   11 VCL_return   c hash
   11 VCL_call     c pass pass
   11 FetchError   c no backend connection
   11 VCL_call     c error deliver
   11 VCL_call     c deliver deliver
   11 TxProtocol   c HTTP/1.1
   11 TxStatus     c 503
   11 TxResponse   c Service Unavailable
   11 TxHeader     c Server: Varnish
   11 TxHeader     c Content-Type: text/html; charset=utf-8
   11 TxHeader     c Retry-After: 5
   11 TxHeader     c Content-Length: 418
   11 TxHeader     c Accept-Ranges: bytes
   11 TxHeader     c Date: Fri, 19 Feb 2016 23:04:03 GMT
   11 TxHeader     c X-Varnish: 311889525
   11 TxHeader     c Age: 0
   11 TxHeader     c Via: 1.1 varnish
   11 TxHeader     c Connection: close
   11 Length       c 418
   11 ReqEnd       c 311889525 1455923043.127803802 1455923043.128304243 0.000658751 0.000423908 0.000076532

And in the example above, where is has ‘FetchError’, it gave the 503 as Apache was not running. Which was why is says: “no backend connection’.