diff --git a/redis.links.menu.yml b/redis.links.menu.yml
new file mode 100644
index 0000000..43c8d43
--- /dev/null
+++ b/redis.links.menu.yml
@@ -0,0 +1,5 @@
+redis.statistics_overview:
+  title: 'Redis'
+  parent: system.admin_reports
+  description: 'Redis usage statistics'
+  route_name: redis.report
diff --git a/redis.routing.yml b/redis.routing.yml
new file mode 100644
index 0000000..df45f33
--- /dev/null
+++ b/redis.routing.yml
@@ -0,0 +1,7 @@
+redis.report:
+  path: '/admin/reports/redis'
+  defaults:
+    _controller: '\Drupal\redis\Controller\ReportController::overview'
+    _title: 'Redis'
+  requirements:
+    _permission: 'access site reports'
diff --git a/src/Controller/ReportController.php b/src/Controller/ReportController.php
new file mode 100644
index 0000000..f42c9fc
--- /dev/null
+++ b/src/Controller/ReportController.php
@@ -0,0 +1,219 @@
+<?php
+
+namespace Drupal\redis\Controller;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Datetime\DateFormatterInterface;
+use Drupal\redis\ClientFactory;
+use Drupal\redis\RedisPrefixTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class ReportController extends ControllerBase {
+
+  use RedisPrefixTrait;
+
+  /**
+   * @var \Redis
+   */
+  protected $redis;
+
+  /**
+   * @var \Drupal\Core\Datetime\DateFormatterInterface
+   */
+  protected $dateFormatter;
+
+  /**
+   * ReportController constructor.
+   *
+   * @param \Drupal\redis\ClientFactory $client_factory
+   */
+  public function __construct(ClientFactory $client_factory, DateFormatterInterface $date_formatter) {
+    $this->redis = $client_factory->getClient();
+    $this->dateFormatter = $date_formatter;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('redis.factory'), $container->get('date.formatter'));
+  }
+
+  /**
+   * Redis report overview.
+   */
+  public function overview() {
+    include_once DRUPAL_ROOT . '/core/includes/install.inc';
+
+    $start = microtime(TRUE);
+
+    $info = $this->redis->info();
+
+    $prefix_length = strlen($this->getPrefix()) + 1;
+
+    $entries_per_bin = array_fill_keys(\Drupal::getContainer()->getParameter('cache_bins'), 0);
+
+    $required_cached_contexts = \Drupal::getContainer()->getParameter('renderer.config')['required_cache_contexts'];
+
+    $render_cache_totals = [];
+    $render_cache_contexts = [];
+    $cache_tags = [];
+    $it = NULL;
+    $i = 0;
+    while ($keys = $this->redis->scan($it, $this->getPrefix() . '*', 10000)) {
+      foreach ($keys as $key) {
+        $i++;
+        $second_colon_pos = Unicode::strpos($key, ':', $prefix_length);
+        if ($second_colon_pos !== FALSE) {
+          $bin = Unicode::substr($key, $prefix_length, $second_colon_pos - $prefix_length);
+          if (isset($entries_per_bin[$bin])) {
+            $entries_per_bin[$bin]++;
+          }
+
+          if ($bin == 'render') {
+            $cache_key = Unicode::substr($key, $second_colon_pos + 1);
+
+            $first_context = Unicode::strpos($cache_key, '[');
+            if ($first_context) {
+              $cache_key_only = Unicode::substr($cache_key, 0, $first_context - 1);
+              if (!isset($render_cache_totals[$cache_key_only])) {
+                $render_cache_totals[$cache_key_only] = 1;
+              }
+              else {
+                $render_cache_totals[$cache_key_only]++;
+              }
+
+              if (preg_match_all('/\[([a-z0-9:_.]+)\]=([^:]*)/', $cache_key, $matches)) {
+                foreach ($matches[1] as $index => $context) {
+                  $render_cache_contexts[$cache_key_only][$context][$matches[2][$index]] = $matches[2][$index];
+                }
+              }
+            }
+          }
+          elseif ($bin == 'cachetags') {
+            $cache_tag = Unicode::substr($key, $second_colon_pos + 1);
+            $cache_tags[$cache_tag] = $this->redis->get($key);
+          }
+        }
+      }
+
+      // Do not process more than 100k cache keys.
+      // @todo Adjust this after more testing or move to a separate page.
+    }
+
+    arsort($entries_per_bin);
+    arsort($render_cache_totals);
+    arsort($cache_tags);
+
+    $per_bin_string = '';
+    foreach ($entries_per_bin as $bin => $entries) {
+      $per_bin_string .= "$bin: $entries<br />";
+    }
+
+    $render_cache_string = '';
+    foreach (array_slice($render_cache_totals, 0, 50) as $cache_key => $total) {
+      $contexts = implode(', ', array_diff(array_keys($render_cache_contexts[$cache_key]), $required_cached_contexts));
+      $render_cache_string .= $contexts ? "$cache_key: $total ($contexts)<br />" : "$cache_key: $total<br />";
+    }
+
+    $cache_tags_string = '';
+    foreach (array_slice($cache_tags, 0, 50) as $cache_tag => $invalidations) {
+      $cache_tags_string .= "$cache_tag: $invalidations<br />";
+    }
+
+    $end = microtime(TRUE);
+    $memory_config = $this->redis->config('get', 'maxmemory*');
+
+    if ($memory_config['maxmemory']) {
+      $memory_value = $this->t('@used_memory / @max_memory (@used_percentage%), maxmemory policy: @policy', [
+        '@used_memory' => $info['used_memory_human'],
+        '@max_memory' => format_size($memory_config['maxmemory']),
+        '@used_percentage' => (int) ($info['used_memory'] / $memory_config['maxmemory'] * 100),
+        '@policy' => $memory_config['maxmemory-policy'],
+      ]);
+    }
+    else {
+      $memory_value = $this->t('@used_memory / unlimited, maxmemory policy: @policy', [
+        '@used_memory' => $info['used_memory_human'],
+        '@policy' => $memory_config['maxmemory-policy'],
+      ]);
+    }
+
+    $requirements = [
+      'version' => [
+        'title' => $this->t('Version'),
+        'value' => $info['redis_version'],
+      ],
+      'clients' => [
+        'title' => $this->t('Connected clients'),
+        'value' => $info['connected_clients'],
+      ],
+      'dbsize' => [
+        'title' => $this->t('Keys'),
+        'value' => $this->redis->dbSize()
+      ],
+      'memory' => [
+        'title' => $this->t('Memory'),
+        'value' => $memory_value,
+      ],
+      'uptime' => [
+        'title' => $this->t('Uptime'),
+        'value' => $this->dateFormatter->formatInterval($info['uptime_in_seconds']),
+      ],
+      'read_write' => [
+        'title' => $this->t('Read/Write'),
+        'value' => $this->t('@read read (@percent_read%), @write written (@percent_write%), @commands commands in @connections connections.', [
+          '@read' => format_size($info['total_net_output_bytes']),
+          '@percent_read' => round(100 / ($info['total_net_output_bytes'] + $info['total_net_input_bytes']) * $info['total_net_output_bytes']),
+          '@write' => format_size($info['total_net_input_bytes']),
+          '@percent_write' => round(100 / ($info['total_net_output_bytes'] + $info['total_net_input_bytes']) * $info['total_net_input_bytes']),
+          '@commands' => $info['total_commands_processed'],
+          '@connections' => $info['total_connections_received'],
+        ]),
+      ],
+      'per_bin' => [
+        'title' => $this->t('Keys per cache bin'),
+        'value' => ['#markup' => $per_bin_string],
+      ],
+      'render_cache' => [
+        'title' => $this->t('Render cache entries with most variations'),
+        'value' => ['#markup' => $render_cache_string],
+      ],
+      'cache_tags' => [
+        'title' => $this->t('Most invalidated cache tags'),
+        'value' => ['#markup' => $cache_tags_string],
+      ],
+      'cache_tag_totals' => [
+        'title' => $this->t('Total cache tag invalidations'),
+        'value' => ['#markup' => $this->t('@count tags with @invalidations invalidations.', [
+          '@count' => count($cache_tags),
+          '@invalidations' => array_sum($cache_tags),
+        ])],
+      ],
+    ];
+
+    // Warnings/hints.
+    if ($memory_config['maxmemory-policy'] == 'noeviction') {
+      $requirements['memory']['severity_status'] = 'warning';
+      $requirements['memory']['description'] = $this->t('It is recommended to configure the maxmemory policy to e.g. volatile-lru.');
+    }
+    if (count($cache_tags) == 0) {
+      $requirements['cache_tag_totals']['severity_status'] = 'warning';
+      $requirements['cache_tag_totals']['description'] = $this->t('No cache tags found, make sure that the redis cache tag checksum service is used.');
+      unset($requirements['cache_tags']);
+    }
+
+    $build['report'] = [
+      '#theme' => 'status_report',
+      '#requirements' => $requirements,
+    ];
+
+    $build['time_spent']['#markup'] = $this->t('Processed @count keys in @time seconds.', ['@count' => $i, '@time' => round(($end - $start), 4)]);
+
+
+    return $build;
+  }
+
+}
