DEV Community

DeployLark
DeployLark

Posted on Originally published at deploylark.com

Redis in Docker: Fix Connection Refused, NOAUTH and MISCONF

You add the official redis image to a Compose file, and your app logs Could not connect to Redis at 127.0.0.1:6379: Connection refused. Or it gets DENIED Redis is running in protected mode, or NOAUTH Authentication required. Or Redis works until its first snapshot, and then every write fails with MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk.

This guide reproduces each of those errors with redis:8.10.3 in Docker Compose and fixes them. Every error message and log line below comes from that lab run on October 31, 2026, when redis:latest pulled the same 8.10.3 image. These errors are common. The Stack Overflow question MISCONF Redis is configured to save RDB snapshots had 646,097 views that day, Connecting to Redis running in Docker Container from Host machine had 306,329, and the image repo issue Set password and other options via ENV variable has 53 comments.

The short version:

  • From another container, localhost is that container. Connect to the Compose service name, for example redis.
  • A mounted stock redis.conf listens on 127.0.0.1 only and turns protected mode on. Change bind and set a password.
  • The official image ignores REDIS_PASSWORD and REDIS_ARGS. Pass --requirepass on the command line or use a config or ACL file.
  • redis-cli ping exits 0 on NOAUTH, so a plain ping healthcheck stays healthy. Set REDISCLI_AUTH and check for PONG.
  • MISCONF and appendonlydir: Permission denied mean the Redis user (uid 999) can't write /data. Fix the ownership instead of turning the check off.

Fix Connection refused from another container

Start with the map. Find the message you see in the left column:

Table diagram mapping five Redis Docker errors to their cause and fix: Connection refused on localhost, Connection refused with a mounted redis.conf, DENIED protected mode, NOAUTH or no password asked, and MISCONF or appendonlydir Permission denied

The lab started with the smallest possible service, no config file and no published port:

services:
  redis:
    image: redis:8.10.3
    volumes:
      - redis-data:/data

volumes:
  redis-data:
Enter fullscreen mode Exit fullscreen mode

Don't connect to localhost from the app container

A one-off redis:8.10.3 container on the same Compose network played the app:

$ redis-cli -h localhost PING
Could not connect to Redis at localhost:6379: Connection refused
$ redis-cli -h 127.0.0.1 -p 6379 PING
Could not connect to Redis at 127.0.0.1:6379: Connection refused
$ redis-cli -h redis PING
PONG
Enter fullscreen mode Exit fullscreen mode

Each container has its own loopback interface, so localhost inside the app container is the app container. Redis listens in its own container, which Compose names after the service. Set your app's Redis host to redis (or whatever you called the service), for example redis://redis:6379/0. The container name redislab-redis-1 also answered. The MySQL guide shows the same mistake with ERROR 2002 and 2003.

From the Docker host itself, the port was closed too. docker compose ps listed 6379/tcp with no host mapping, and a TCP connect to 127.0.0.1:6379 on the host was refused. Add ports: ["127.0.0.1:6379:6379"] if a tool on the host needs Redis. Keep the 127.0.0.1 part. Other containers on the network don't need ports at all.

Check bind in a mounted redis.conf

The image docs show how to start Redis with your own redis.conf. If you start from the upstream file, note that the 8.10.3 version from the redis/redis repository contains bind 127.0.0.1 -::1 and protected-mode yes. Mounted as-is, Redis answers inside its own container and refuses everyone else:

Terminal output from an app container: redis-cli to localhost and 127.0.0.1 is refused, the service name redis returns PONG, and after mounting the stock redis.conf the service name is refused too while redis-cli inside the Redis container still returns PONG

CONFIG GET bind inside the container returned 127.0.0.1 -::1. A published port didn't help either: from the host, redis-cli got Error: Connection reset by peer. This is the accepted answer on the 306,329-view question above: the server only listens on loopback. Change the line to bind * -::*, which the same file describes as "like the default, all available interfaces". That leads straight to the next error.

Fix DENIED Redis is running in protected mode

Why the plain image has no protected mode

Without a config file, the image turns protected mode off. In the lab, CONFIG GET protected-mode returned no, bind was * -::* and requirepass was empty. Redis logged this on startup:

WARNING: Redis does not require authentication and is not protected by network restrictions. Redis will accept connections from any IP address on any network interface.
Enter fullscreen mode Exit fullscreen mode

The Docker Official Image docs explain the choice: "For the ease of accessing Redis from other containers via Docker networking, the "Protected mode" is turned off by default. This means that if you expose the port outside of your host (e.g., via -p on docker run), it will be open without a password to anyone."

Set a password instead of turning protected mode off

With a mounted redis.conf you get the upstream default, protected-mode yes. After the bind * -::* change and no password, every client from another container got the long DENIED reply:

Terminal output showing the full DENIED Redis is running in protected mode message from another container, then NOAUTH Authentication required after adding requirepass, and PONG once REDISCLI_AUTH supplies the password

The message offers four ways out. Three of them switch protected mode off. Option 4, a password for the default user, is the one to pick. Adding requirepass Lab-Pass-4821 to redis.conf and restarting turned DENIED into NOAUTH Authentication required., and the same client with the password got PONG. The Redis security docs state the design plainly: "Redis is designed to be accessed by trusted clients inside trusted environments." A password plus a private Compose network keeps it that way.

Fix NOAUTH and a password that never applied

Pass the password with --requirepass

The first lab file had REDIS_PASSWORD: "ChangeMe123" in environment. Redis started without a password anyway: CONFIG GET requirepass came back empty, and a client from another container ran PING without any auth. The entrypoint script in redis:8.10.3 never reads REDIS_PASSWORD, and it contains no REDIS_ARGS either. A test with REDIS_ARGS: "--requirepass Lab-Pass-4821" also left Redis open, and SET worked without a password. A Stack Overflow answer notes that REDIS_ARGS works with the redis/redis-stack images. The official redis image doesn't support it.

Instead, put the password in .env and let Compose interpolate it into the command:

services:
  redis:
    image: redis:8.10.3
    command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:?set REDIS_PASSWORD in .env}"]
Enter fullscreen mode Exit fullscreen mode

With REDIS_PASSWORD=Lab-Pass-4821 in .env, this produced the errors your app shows when its password is missing or wrong:

Terminal output: redis-cli PING without a password prints NOAUTH Authentication required with exit code 0, a wrong password prints WRONGPASS invalid username-password pair, REDISCLI_AUTH returns PONG, and docker compose ps shows a naive ping healthcheck as healthy while a wrong-password healthcheck is unhealthy

NOAUTH Authentication required. means the client sent no password. WRONGPASS invalid username-password pair or user is disabled. means it sent the wrong one. With -a, redis-cli also prints a warning that a password on the command line may not be safe. Set REDISCLI_AUTH instead, or add --no-auth-warning. Without .env, the :? form stops Compose with required variable REDIS_PASSWORD is missing a value, so Redis never starts open by accident.

Two details from the lab. docker inspect shows the password in the container's Args. And the shell form from a 41-vote answer also worked, with redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}" after /bin/sh -c and the password in environment. The $$ stops Compose from replacing the variable itself. That keeps the value out of Args, but it still sits in the container's environment.

The healthcheck has the same blind spot, as the bottom of the screenshot above shows. A healthcheck of ["CMD", "redis-cli", "ping"] against a password-protected Redis stayed healthy. Its last probe printed NOAUTH Authentication required. and exited 0. So depends_on: condition: service_healthy lets your app start against a Redis it can't log in to.

This check reports the truth:

    environment:
      REDISCLI_AUTH: ${REDIS_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep -q PONG"]
Enter fullscreen mode Exit fullscreen mode

With the right password the service was healthy. With a wrong REDISCLI_AUTH it turned unhealthy, and the probe log showed the WRONGPASS error.

Use an ACL user when your app wants a username

Some apps ask for a Redis username and password. Define the user in an ACL file:

user default off
user app on >App-Pass-7730 ~app:* +@all -@dangerous
Enter fullscreen mode Exit fullscreen mode

Mount it read-only and start Redis with command: ["redis-server", "--aclfile", "/usr/local/etc/redis/users.acl"]. In the lab, redis-cli --user app --pass App-Pass-7730 PING returned PONG, and a URL like redis://app:App-Pass-7730@redis:6379 worked too. A password without the username failed with WRONGPASS, because the default user is off. The ~app:* key pattern did its job: SET other 1 returned NOPERM No permissions to access a key, and FLUSHALL was refused. Putting the same rules on the command line went wrong in the lab. Written as one list item after --user, Redis exited with Spaces not allowed in ACL usernames. The file avoids that.

Fix MISCONF and append-only Permission denied

Why saves to /data fail

The Redis server runs as uid 999 (redis) in this image. When the container starts as root, the entrypoint fixes the ownership of /data itself. The image docs add a limit: "This adjustment is only performed in basic, default scenarios to avoid interfering with custom or user-specific configurations."

The lab hit that limit with a bind mount, ./data:/data, owned by root and holding a .gitkeep file. The entrypoint printed Notice: Unknown file './.gitkeep' found in data dir. Permissions will not be modified. and left the directory alone. Redis started, and reads and writes worked until the first snapshot:

Terminal output: the entrypoint notice about an unknown file in the data dir, BGSAVE failing with Permission denied on the temp RDB file, SET returning the MISCONF error, the appendonlydir Permission denied log line, then chown to 999 fixing writes and rdb_last_bgsave_status ok

The default save rule was 3600 1 300 100 60 10000. After a failed BGSAVE, the log said Failed opening the temp RDB file temp-51.rdb (in server root dir /data) for saving: Permission denied, INFO persistence showed rdb_last_bgsave_status:err, and the next SET returned MISCONF. Reads still worked, as GET a returned 1.

With --appendonly yes the same mount fails at once. The container exited with code 1 after Can't open or create append-only dir appendonlydir: Permission denied. A user: "1000:1000" line on a fresh named volume, which the image creates owned by 999, failed with the same message.

Every start in the lab logged WARNING Memory overcommit must be enabled!, because the host had vm.overcommit_memory set to 0. The warning says it can make a background save fail under low memory, and it names the fix: "To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect." This is a host setting. docker run --sysctl vm.overcommit_memory=1 failed with sysctl 'vm.overcommit_memory=1' is not allowed, and the container read the host value of 0. Set it on the Docker host. A permission error, not this warning, caused every MISCONF in this lab.

Fix ownership, don't switch off the check

The 533-vote answer on the MISCONF question runs config set stop-writes-on-bgsave-error no and adds that you should check why the save failed. The lab shows why that matters. After that command, SET worked again, but the next BGSAVE still ended with rdb_last_bgsave_status:err. Nothing reached the disk.

Fix the directory instead. Any of these worked in the lab:

  • Change the owner to the Redis user with sudo chown -R 999:999 ./data and restart. Writes worked, BGSAVE returned rdb_last_bgsave_status:ok and dump.rdb appeared with owner 999.
  • Run the command from the image docs, docker run --rm -v /your/host/path:/data redis:8.10.3 chown -R redis:redis /data. It set the same root-owned folder to 999:999.
  • Remove the unknown files, so the entrypoint fixes the folder on its own. With .gitkeep gone it changed the owner to 999 and created appendonlydir.
  • If you run Redis with user: "1000:1000", make the volume belong to that user, for example docker run --rm -v <volume>:/data alpine:3.22 chown 1000:1000 /data. After that the AOF service started and aof_last_write_status:ok.

The Grafana and Prometheus guide walks through the same uid mismatch with two other images.

A Redis Compose file that avoids these errors

compose.yaml and .env

This file ran verbatim in the lab:

services:
  redis:
    image: redis:8.10.3
    restart: unless-stopped
    command:
      - redis-server
      - --requirepass
      - ${REDIS_PASSWORD:?set REDIS_PASSWORD in .env}
      - --appendonly
      - "yes"
    environment:
      REDISCLI_AUTH: ${REDIS_PASSWORD}
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep -q PONG"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 5s

  app:
    image: redis:8.10.3
    depends_on:
      redis:
        condition: service_healthy
    environment:
      REDISCLI_AUTH: ${REDIS_PASSWORD}
    command: ["redis-cli", "-h", "redis", "INCR", "app:visits"]

volumes:
  redis-data:
Enter fullscreen mode Exit fullscreen mode
REDIS_PASSWORD=f3Kq9-Lab-Only-2Vx7
Enter fullscreen mode Exit fullscreen mode

Replace app with your real service and give it the host redis and the same password. The named volume belongs to the Redis user from the start, AOF is on, and the healthcheck only passes on a real PONG. Drop the ports block if nothing on the host needs Redis. If your password contains $, read the dollar sign section of the MySQL guide first, because Compose interpolates .env the same way. The Umami guide uses the same service_healthy pattern with PostgreSQL.

Check it

$ docker compose up -d
 Container redisfinal-redis-1  Waiting
 Container redisfinal-redis-1  Healthy
 Container redisfinal-app-1  Started
$ docker compose logs --no-log-prefix app
1
$ docker compose run --rm app
2
$ docker compose exec redis redis-cli INFO persistence | grep -E 'aof_enabled|aof_last_write_status'
aof_enabled:1
aof_last_write_status:ok
$ docker compose restart redis
$ docker compose exec redis redis-cli GET app:visits
2
Enter fullscreen mode Exit fullscreen mode

From the host, redis-cli -h 127.0.0.1 PING without a password got NOAUTH Authentication required., and with REDISCLI_AUTH set it read the counter. The value survived the restart. Paperless-ngx uses a Redis-compatible broker in the same way, as the Paperless-ngx guide shows with Valkey.

FAQ

Connection errors

Why does Redis refuse connections between Docker Compose containers?

Usually the app connects to localhost or 127.0.0.1, which is the app's own container. Use the service name, such as redis. If you mounted your own redis.conf, check its bind line. The stock file has bind 127.0.0.1 -::1, so other containers get Connection refused until you change it.

How do I connect to Redis in Docker from the host machine?

Publish the port, for example ports: ["127.0.0.1:6379:6379"], and connect to 127.0.0.1:6379. Without ports, the host connection is refused. If Redis uses the stock redis.conf with bind 127.0.0.1 -::1, the published port still fails. In the lab, redis-cli on the host got Error: Connection reset by peer.

How do I fix DENIED Redis is running in protected mode in Docker?

Set a password with requirepass or define an ACL user. Protected mode only blocks remote clients when the default user has no password. The plain image starts with protected mode off, so this error usually comes from a mounted redis.conf.

Password and persistence errors

How do I set a Redis password with an environment variable in docker-compose?

The official image doesn't read REDIS_PASSWORD. Put the value in .env and pass it in command, as in ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]. Check with CONFIG GET requirepass or a PING without a password, which should return NOAUTH.

How do I use a Redis username with a password in docker-compose?

Create an ACL file with user default off and a line such as user app on >password ~app:* +@all, mount it, and start Redis with --aclfile. Clients then log in with --user and --pass, or a URL like redis://app:password@redis:6379.

How do I fix MISCONF Redis is configured to save RDB snapshots in Docker?

Read the Redis log for the reason. In Docker it is often Permission denied on /data. Change the data directory's owner to uid 999, or to the user you set with user:, then restart. Setting stop-writes-on-bgsave-error no hides the error but doesn't save anything.

Top comments (0)