diff --git a/composer.json b/composer.json
index 0801632ab68e49005dfccc7bbff7d23a7a23d6e9..11f8232b6be2f3e4142b4ac822c00b95c9d4e760 100644
--- a/composer.json
+++ b/composer.json
@@ -47,6 +47,9 @@
         "phpunit/phpunit": "~4.8",
         "satooshi/php-coveralls": "^1.0"
     },
+    "suggests": {
+        "predis/predis": "1.1.1"
+    },
     "support": {
         "issues": "https://github.com/simplesamlphp/simplesamlphp/issues",
         "source": "https://github.com/simplesamlphp/simplesamlphp"
diff --git a/config-templates/config.php b/config-templates/config.php
index 65f9c1fe15f8b9a2f315ac3495c1d7c23bcd8577..9b69ee3ac74ef811277a80afaece4844dace97d7 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -975,6 +975,7 @@ $config = array(
      * - 'phpsession': Limited datastore, which uses the PHP session.
      * - 'memcache': Key-value datastore, based on memcache.
      * - 'sql': SQL datastore, using PDO.
+     * - 'redis': Key-value datastore, based on redis.
      *
      * The default datastore is 'phpsession'.
      *
@@ -1000,4 +1001,15 @@ $config = array(
      * The prefix we should use on our tables.
      */
     'store.sql.prefix' => 'SimpleSAMLphp',
+
+    /*
+     * The hostname and port of the Redis datastore instance.
+     */
+    'store.redis.host' => 'localhost',
+    'store.redis.port' => 6379,
+
+    /*
+     * The prefix we should use on our Redis datastore.
+     */
+    'store.redis.prefix' => 'simpleSAMLphp',
 );
diff --git a/lib/SimpleSAML/Store.php b/lib/SimpleSAML/Store.php
index 5ee6e43760d93df31f4d2651cbbd6c857bac4cc8..8f25b59c9194f2fc20843e7c30a428e33aa5f01e 100644
--- a/lib/SimpleSAML/Store.php
+++ b/lib/SimpleSAML/Store.php
@@ -51,6 +51,9 @@ abstract class Store
             case 'sql':
                 self::$instance = new Store\SQL();
                 break;
+            case 'redis':
+                self::$instance = new Store\Redis();
+                break;
             default:
                 // datastore from module
                 try {
diff --git a/lib/SimpleSAML/Store/Redis.php b/lib/SimpleSAML/Store/Redis.php
new file mode 100644
index 0000000000000000000000000000000000000000..579724b9561295c4a8fbb0a8c62babe68938abf1
--- /dev/null
+++ b/lib/SimpleSAML/Store/Redis.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SimpleSAML\Store;
+
+use \SimpleSAML_Configuration as Configuration;
+use \SimpleSAML\Store;
+
+/**
+ * A data store using Redis to keep the data.
+ *
+ * @package SimpleSAMLphp
+ */
+class Redis extends Store
+{
+    /**
+     * Initialize the Redis data store.
+     */
+    public function __construct($redis = null)
+    {
+        assert('is_null($redis) || is_subclass_of($redis, "Predis\\Client")');
+
+        if (is_null($redis)) {
+            $config = Configuration::getInstance();
+
+            $host = $config->getString('store.redis.host', 'localhost');
+            $port = $config->getInteger('store.redis.port', 6379);
+            $prefix = $config->getString('store.redis.prefix', 'simpleSAMLphp');
+
+            $redis = new \Predis\Client(
+                array(
+                    'scheme' => 'tcp',
+                    'host' => $host,
+                    'post' => $port,
+                ),
+                array(
+                    'prefix' => $prefix,
+                )
+            );
+        }
+
+        $this->redis = $redis;
+    }
+
+    /**
+     * Deconstruct the Redis data store.
+     */
+    public function __destruct()
+    {
+        if (method_exists($this->redis, 'disconnect')) {
+            $this->redis->disconnect();
+        }
+    }
+
+    /**
+     * Retrieve a value from the data store.
+     *
+     * @param string $type The type of the data.
+     * @param string $key The key to retrieve.
+     *
+     * @return mixed|null The value associated with that key, or null if there's no such key.
+     */
+    public function get($type, $key)
+    {
+        assert('is_string($type)');
+        assert('is_string($key)');
+
+        $result = $this->redis->get("{$type}.{$key}");
+
+        if ($result === false) {
+            return null;
+        }
+
+        return unserialize($result);
+    }
+
+    /**
+     * Save a value in the data store.
+     *
+     * @param string $type The type of the data.
+     * @param string $key The key to insert.
+     * @param mixed $value The value itself.
+     * @param int|null $expire The expiration time (unix timestamp), or null if it never expires.
+     */
+    public function set($type, $key, $value, $expire = null)
+    {
+        assert('is_string($type)');
+        assert('is_string($key)');
+        assert('is_null($expire) || (is_int($expire) && $expire > 2592000)');
+
+        $serialized = serialize($value);
+
+        if (is_null($expire)) {
+            $this->redis->set("{$type}.{$key}", $serialized);
+        } else {
+            $this->redis->setex("{$type}.{$key}", $expire, $serialized);
+        }
+    }
+
+    /**
+     * Delete an entry from the data store.
+     *
+     * @param string $type The type of the data
+     * @param string $key The key to delete.
+     */
+    public function delete($type, $key)
+    {
+        assert('is_string($type)');
+        assert('is_string($key)');
+
+        $this->redis->del("{$type}.{$key}");
+    }
+}
diff --git a/tests/lib/SimpleSAML/Store/RedisTest.php b/tests/lib/SimpleSAML/Store/RedisTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7eecdf310b86e757b1fabff755036954470d9a10
--- /dev/null
+++ b/tests/lib/SimpleSAML/Store/RedisTest.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace SimpleSAML\Test\Store;
+
+use \SimpleSAML_Configuration as Configuration;
+use \SimpleSAML\Store;
+
+/**
+ * Tests for the Redis store.
+ *
+ * For the full copyright and license information, please view the LICENSE file that was distributed with this source
+ * code.
+ *
+ * @package simplesamlphp/simplesamlphp
+ */
+class RedisTest extends \PHPUnit_Framework_TestCase
+{
+    protected function setUp()
+    {
+        $this->config = array();
+
+        $this->mocked_redis = $this->getMockBuilder('Predis\Client')
+                                   ->setMethods(array('get', 'set', 'setex', 'del', 'disconnect'))
+                                   ->disableOriginalConstructor()
+                                   ->getMock();
+
+        $this->mocked_redis->method('get')
+                           ->will($this->returnCallback(array($this, 'getMocked')));
+
+        $this->mocked_redis->method('set')
+                           ->will($this->returnCallback(array($this, 'setMocked')));
+
+        $this->mocked_redis->method('setex')
+                           ->will($this->returnCallback(array($this, 'setexMocked')));
+
+        $this->mocked_redis->method('del')
+                           ->will($this->returnCallback(array($this, 'delMocked')));
+
+        $nop = function () {
+            return;
+        };
+
+        $this->mocked_redis->method('disconnect')
+                           ->will($this->returnCallback($nop));
+
+        $this->redis = new Store\Redis($this->mocked_redis);
+    }
+
+    public function getMocked($key)
+    {
+        return array_key_exists($key, $this->config) ? $this->config[$key] : false;
+    }
+
+    public function setMocked($key, $value)
+    {
+        $this->config[$key] = $value;
+    }
+
+    public function setexMocked($key, $expire, $value)
+    {
+        // Testing expiring data is more trouble than it's worth for now
+        $this->setMocked($key, $value);
+    }
+
+    public function delMocked($key)
+    {
+        unset($this->config[$key]);
+    }
+
+    /**
+     * @covers \SimpleSAML\Store::getInstance
+     * @covers \SimpleSAML\Store\Redis::__construct
+     * @test
+     */
+    public function testRedisInstance()
+    {
+        $config = Configuration::loadFromArray(array(
+            'store.type' => 'redis',
+            'store.redis.prefix' => 'phpunit_',
+        ), '[ARRAY]', 'simplesaml');
+
+        $store = Store::getInstance();
+
+        $this->assertInstanceOf('SimpleSAML\Store\Redis', $store);
+
+        $this->clearInstance($config, '\SimpleSAML_Configuration');
+        $this->clearInstance($store, '\SimpleSAML\Store');
+    }
+
+    /**
+     * @covers \SimpleSAML\Store\Redis::get
+     * @covers \SimpleSAML\Store\Redis::set
+     * @test
+     */
+    public function testInsertData()
+    {
+        $value = 'TEST';
+
+        $this->redis->set('test', 'key', $value);
+        $res = $this->redis->get('test', 'key');
+        $expected = $value;
+
+        $this->assertEquals($expected, $res);
+    }
+
+    /**
+     * @covers \SimpleSAML\Store\Redis::get
+     * @covers \SimpleSAML\Store\Redis::set
+     * @test
+     */
+    public function testInsertExpiringData()
+    {
+        $value = 'TEST';
+
+        $this->redis->set('test', 'key', $value, $expire = 80808080);
+        $res = $this->redis->get('test', 'key');
+        $expected = $value;
+
+        $this->assertEquals($expected, $res);
+    }
+
+    /**
+     * @covers \SimpleSAML\Store\Redis::get
+     * @test
+     */
+    public function testGetEmptyData()
+    {
+        $res = $this->redis->get('test', 'key');
+
+        $this->assertNull($res);
+    }
+
+    /**
+     * @covers \SimpleSAML\Store\Redis::get
+     * @covers \SimpleSAML\Store\Redis::set
+     * @test
+     */
+    public function testOverwriteData()
+    {
+        $value1 = 'TEST1';
+        $value2 = 'TEST2';
+
+        $this->redis->set('test', 'key', $value1);
+        $this->redis->set('test', 'key', $value2);
+        $res = $this->redis->get('test', 'key');
+        $expected = $value2;
+
+        $this->assertEquals($expected, $res);
+    }
+
+    /**
+     * @covers \SimpleSAML\Store\Redis::get
+     * @covers \SimpleSAML\Store\Redis::set
+     * @covers \SimpleSAML\Store\Redis::delete
+     * @test
+     */
+    public function testDeleteData()
+    {
+        $this->redis->set('test', 'key', 'TEST');
+        $this->redis->delete('test', 'key');
+        $res = $this->redis->get('test', 'key');
+
+        $this->assertNull($res);
+    }
+
+    protected function clearInstance($service, $className)
+    {
+        $reflectedClass = new \ReflectionClass($className);
+        $reflectedInstance = $reflectedClass->getProperty('instance');
+        $reflectedInstance->setAccessible(true);
+        $reflectedInstance->setValue($service, null);
+        $reflectedInstance->setAccessible(false);
+    }
+}