<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ramansah</title>
    <description>The latest articles on DEV Community by Ramansah (@ramansah).</description>
    <link>https://dev.to/ramansah</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4006638%2Fb5a25202-3633-46b8-8f8c-00c38bfa8b37.png</url>
      <title>DEV Community: Ramansah</title>
      <link>https://dev.to/ramansah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ramansah"/>
    <language>en</language>
    <item>
      <title>Redis with Docker Compose: Persistence, Security, and Production-Ready Configuration</title>
      <dc:creator>Ramansah</dc:creator>
      <pubDate>Mon, 29 Jun 2026 06:19:52 +0000</pubDate>
      <link>https://dev.to/ramansah/redis-with-docker-compose-persistence-security-and-production-ready-configuration-58jb</link>
      <guid>https://dev.to/ramansah/redis-with-docker-compose-persistence-security-and-production-ready-configuration-58jb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://bckinfo.com/redis-docker-compose-persistence-security/" rel="noopener noreferrer"&gt;bckinfo.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Redis with Docker Compose: Persistence, Security, and Production-Ready Configuration
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why Redis Containers Lose Data&lt;/li&gt;
&lt;li&gt;RDB vs AOF: Choosing a Persistence Strategy&lt;/li&gt;
&lt;li&gt;Basic Setup with Docker Compose&lt;/li&gt;
&lt;li&gt;Full Persistence Configuration&lt;/li&gt;
&lt;li&gt;Securing Redis in Docker&lt;/li&gt;
&lt;li&gt;Setting Resource Limits&lt;/li&gt;
&lt;li&gt;Redis in a Multi-Service Stack&lt;/li&gt;
&lt;li&gt;Backing Up and Restoring a Redis Volume&lt;/li&gt;
&lt;li&gt;Common Issues and Quick Fixes&lt;/li&gt;
&lt;li&gt;Closing Notes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Redis is one of the most common services to run in Docker — it's fast to spin up, lightweight, and perfect for caching, session storage, and queues. But that same simplicity hides a trap: by default, Redis in Docker stores everything in memory, and the moment a container is removed, &lt;strong&gt;all of that data disappears&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This guide walks through setting up Redis with Docker Compose the right way — covering persistence, authentication, resource limits, and the health checks you need before putting it anywhere near production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Redis Containers Lose Data
&lt;/h2&gt;

&lt;p&gt;A Docker container is meant to be disposable. That's a feature for stateless services, but it's a liability for a database like Redis. If you start a plain Redis container without a mounted volume, here's what happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The container writes its dataset only inside its own writable layer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker compose down&lt;/code&gt; or &lt;code&gt;docker rm&lt;/code&gt; removes that layer entirely.&lt;/li&gt;
&lt;li&gt;The next time the container starts, Redis initializes with an empty dataset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This single oversight accounts for a large share of "we lost our session data" incidents in small teams running Redis in containers for the first time. The fix is straightforward once you understand the two persistence mechanisms Redis offers.&lt;/p&gt;

&lt;h2&gt;
  
  
  RDB vs AOF: Choosing a Persistence Strategy
&lt;/h2&gt;

&lt;p&gt;Redis supports two persistence models, and production setups typically combine both:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RDB (Redis Database snapshots)&lt;/strong&gt;&lt;br&gt;
Point-in-time snapshots of the dataset, saved at intervals you define. Fast to restore, but you can lose any writes that happened after the last snapshot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AOF (Append Only File)&lt;/strong&gt;&lt;br&gt;
Every write operation is logged to disk as it happens. Slower to restore on a large dataset, but far safer — with &lt;code&gt;appendfsync everysec&lt;/code&gt;, you lose at most one second of writes.&lt;/p&gt;

&lt;p&gt;For most production workloads, &lt;strong&gt;enable both&lt;/strong&gt;: AOF for durability, RDB for fast snapshot-based backups.&lt;/p&gt;
&lt;h2&gt;
  
  
  Basic Setup with Docker Compose
&lt;/h2&gt;

&lt;p&gt;Start with a minimal but persistent configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6379:6379"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-data:/data&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server --appendonly yes&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line here is &lt;code&gt;volumes: - redis-data:/data&lt;/code&gt;. The official Redis image is already configured to write its dataset to &lt;code&gt;/data&lt;/code&gt;, so mounting a named volume there is enough to survive a container removal. Named volumes are preferred over bind mounts for this purpose — they're portable across hosts and Docker manages their lifecycle for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Persistence Configuration
&lt;/h2&gt;

&lt;p&gt;For more control, mount a custom &lt;code&gt;redis.conf&lt;/code&gt; instead of relying on command-line flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# redis.conf
&lt;/span&gt;
&lt;span class="c"&gt;# Network
&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="m"&gt;6379&lt;/span&gt;
&lt;span class="n"&gt;protected&lt;/span&gt;-&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# Security
&lt;/span&gt;&lt;span class="n"&gt;requirepass&lt;/span&gt; &lt;span class="n"&gt;your_secure_password_here&lt;/span&gt;

&lt;span class="c"&gt;# Memory management
&lt;/span&gt;&lt;span class="n"&gt;maxmemory&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;&lt;span class="n"&gt;mb&lt;/span&gt;
&lt;span class="n"&gt;maxmemory&lt;/span&gt;-&lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;allkeys&lt;/span&gt;-&lt;span class="n"&gt;lru&lt;/span&gt;

&lt;span class="c"&gt;# AOF persistence
&lt;/span&gt;&lt;span class="n"&gt;appendonly&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;appendfsync&lt;/span&gt; &lt;span class="n"&gt;everysec&lt;/span&gt;
&lt;span class="n"&gt;auto&lt;/span&gt;-&lt;span class="n"&gt;aof&lt;/span&gt;-&lt;span class="n"&gt;rewrite&lt;/span&gt;-&lt;span class="n"&gt;percentage&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;auto&lt;/span&gt;-&lt;span class="n"&gt;aof&lt;/span&gt;-&lt;span class="n"&gt;rewrite&lt;/span&gt;-&lt;span class="n"&gt;min&lt;/span&gt;-&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="n"&gt;mb&lt;/span&gt;

&lt;span class="c"&gt;# RDB persistence
&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="m"&gt;900&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;save&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;stop&lt;/span&gt;-&lt;span class="n"&gt;writes&lt;/span&gt;-&lt;span class="n"&gt;on&lt;/span&gt;-&lt;span class="n"&gt;bgsave&lt;/span&gt;-&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;rdbcompression&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;rdbchecksum&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# Logging
&lt;/span&gt;&lt;span class="n"&gt;loglevel&lt;/span&gt; &lt;span class="n"&gt;notice&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mount it in Docker Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6379:6379"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-data:/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./redis.conf:/usr/local/etc/redis/redis.conf:ro&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server /usr/local/etc/redis/redis.conf&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mounting the config file as read-only (&lt;code&gt;:ro&lt;/code&gt;) prevents Redis itself from accidentally modifying it, and keeps your configuration under version control instead of buried in command-line arguments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing Redis in Docker
&lt;/h2&gt;

&lt;p&gt;Redis has no authentication enabled by default, and it has been a recurring target for cryptomining botnets that scan the internet for exposed instances on port 6379. A few non-negotiable steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Always set &lt;code&gt;requirepass&lt;/code&gt;.&lt;/strong&gt; Never run Redis with &lt;code&gt;ALLOW_EMPTY_PASSWORD&lt;/code&gt; outside of a local, throwaway development environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never expose port 6379 directly to the internet.&lt;/strong&gt; If remote access is genuinely needed, put it behind a VPN or SSH tunnel — not a public port mapping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disable dangerous commands&lt;/strong&gt; in production, such as &lt;code&gt;FLUSHALL&lt;/code&gt;, &lt;code&gt;FLUSHDB&lt;/code&gt;, and &lt;code&gt;CONFIG&lt;/code&gt;, using &lt;code&gt;rename-command&lt;/code&gt; in &lt;code&gt;redis.conf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run as a non-root user.&lt;/strong&gt; The official and Bitnami images already drop privileges by default — don't override this with &lt;code&gt;SKIP_DROP_PRIVS&lt;/code&gt; unless you have a specific reason to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the same defense-in-depth mindset covered in our Docker Container Security Best Practices guide — Redis just adds a database-specific layer to those general container hardening principles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Resource Limits
&lt;/h2&gt;

&lt;p&gt;An unbounded Redis instance can consume all available host memory under heavy load, taking down other services on the same machine. Set explicit limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1G&lt;/span&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256M&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server --maxmemory 800mb --maxmemory-policy allkeys-lru&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the two limits work at different layers: &lt;code&gt;deploy.resources.limits.memory&lt;/code&gt; is enforced by Docker itself (the container gets OOM-killed if it crosses this), while &lt;code&gt;maxmemory&lt;/code&gt; inside Redis tells Redis to start evicting keys &lt;em&gt;before&lt;/em&gt; that happens — using &lt;code&gt;allkeys-lru&lt;/code&gt; as a sensible general-purpose eviction policy for caching workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redis in a Multi-Service Stack
&lt;/h2&gt;

&lt;p&gt;When Redis is shared by an API and a background worker, startup order matters. A worker that connects before Redis is ready will crash-loop. Use a health check with &lt;code&gt;condition: service_healthy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis-data:/data&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-a"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${REDIS_PASSWORD}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./api&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://:${REDIS_PASSWORD}@redis:6379/0&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;

  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./worker&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://:${REDIS_PASSWORD}@redis:6379/1&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details worth highlighting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logical databases.&lt;/strong&gt; Redis supports 16 logical databases (0–15) on a single instance. Assigning a different database index per service (&lt;code&gt;/0&lt;/code&gt; for the API, &lt;code&gt;/1&lt;/code&gt; for the worker) avoids key collisions without running multiple Redis containers — useful in smaller setups where running separate instances would be overkill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom bridge network.&lt;/strong&gt; Putting Redis on its own &lt;code&gt;backend&lt;/code&gt; network — separate from any network that's exposed to the outside — limits which containers can even attempt to reach it. This pairs directly with the network segmentation principles in our Docker Network Security guide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Backing Up and Restoring a Redis Volume
&lt;/h2&gt;

&lt;p&gt;Even with persistence enabled, you still want backups independent of the running container — protecting against accidental &lt;code&gt;FLUSHALL&lt;/code&gt;, corrupted volumes, or host failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backup script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# backup-redis-volume.sh&lt;/span&gt;
&lt;span class="nv"&gt;VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"redis_redis-data"&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/backups/redis"&lt;/span&gt;
&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d-%H%M%S&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Trigger a snapshot before backing up&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis redis-cli BGSAVE
&lt;span class="nb"&gt;sleep &lt;/span&gt;5

docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VOLUME_NAME&lt;/span&gt;&lt;span class="s2"&gt;:/data:ro"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;&lt;span class="s2"&gt;:/backup"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  alpine &lt;span class="nb"&gt;tar &lt;/span&gt;czf &lt;span class="s2"&gt;"/backup/redis-data-&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /data &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Backup created: &lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;&lt;span class="s2"&gt;/redis-data-&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Restore script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# restore-redis-volume.sh&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;VOLUME_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"redis_redis-data"&lt;/span&gt;

docker compose stop redis

docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VOLUME_NAME&lt;/span&gt;&lt;span class="s2"&gt;:/data"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:/backup:ro"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  alpine &lt;span class="nb"&gt;tar &lt;/span&gt;xzf &lt;span class="s2"&gt;"/backup/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /data

docker compose start redis
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Restore complete from &lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same backup-then-archive pattern is used in our MongoDB Backup article — if you're running both databases in the same stack, you can adapt one scheduled cron job to cover both with minimal changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Issues and Quick Fixes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data gone after &lt;code&gt;docker compose down&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No volume mounted&lt;/td&gt;
&lt;td&gt;Add a named volume at &lt;code&gt;/data&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;WRONGPASS&lt;/code&gt; errors from app&lt;/td&gt;
&lt;td&gt;Password mismatch between &lt;code&gt;redis.conf&lt;/code&gt; and app env var&lt;/td&gt;
&lt;td&gt;Verify &lt;code&gt;REDIS_PASSWORD&lt;/code&gt; matches in both places&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis container OOM-killed&lt;/td&gt;
&lt;td&gt;No memory limit / no eviction policy&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;deploy.resources.limits.memory&lt;/code&gt; and &lt;code&gt;maxmemory-policy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker crash-loops on startup&lt;/td&gt;
&lt;td&gt;Worker starts before Redis is ready&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;healthcheck&lt;/code&gt; + &lt;code&gt;condition: service_healthy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High latency under load&lt;/td&gt;
&lt;td&gt;Synchronous AOF fsync&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;appendfsync everysec&lt;/code&gt; instead of &lt;code&gt;always&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Closing Notes
&lt;/h2&gt;

&lt;p&gt;Running Redis in Docker Compose is simple to get started with — and just as simple to get wrong if persistence and security are treated as an afterthought. The pattern that holds up well in production is consistent: named volumes for &lt;code&gt;/data&lt;/code&gt;, AOF+RDB persistence enabled together, &lt;code&gt;requirepass&lt;/code&gt; always set, explicit memory limits, and a health check gating any service that depends on Redis being ready.&lt;/p&gt;

&lt;p&gt;From here, if your stack also includes MongoDB or another database alongside Redis, the same volume-and-backup discipline applies — see our MongoDB Backup guide for the database-specific details, and our Docker Network Security guide for locking down how these containers talk to each other.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>dockercompose</category>
      <category>redis</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
