← Back to Blog

Redis Connection Refused: Fix ECONNREFUSED Across Every Stack

You push code, restart your app, and there it is: Error: connect ECONNREFUSED 127.0.0.1:6379. Your app cannot talk to Redis. This guide walks you through every possible cause and fix, whether you are running Redis locally, in Docker, in Kubernetes, or on a managed service like ElastiCache.

TL;DR Quick Checklist

  • Is Redis running? Run redis-cli ping. If you get PONG, it is alive.
  • Is it bound to 127.0.0.1? Check the bind directive in redis.conf. Remote clients need 0.0.0.0 or a specific IP.
  • Is protected-mode yes? If you bind to all interfaces without a password, Redis will reject remote connections.
  • Firewall blocking port 6379? Check ufw, iptables, or your cloud security group.
  • Docker? Use the container/service name, not localhost.
  • Kubernetes? Use the full service DNS: redis-master.namespace.svc.cluster.local.

What ECONNREFUSED Actually Means

Before we start fixing things, it helps to understand what the error is really telling you. ECONNREFUSED is a TCP-level rejection. Your client sent a SYN packet to the target host and port, and the operating system on the other end replied with a RST (reset) packet. That is the computer equivalent of knocking on a door and someone shouting "go away."

This is different from a timeout, where nobody answers at all. A refused connection is fast and definitive. It means one of three things: nothing is listening on that port, a firewall is actively rejecting (not dropping) the packet, or the process crashed between the time you configured the host and the time you tried to connect.

With that context, let us walk through the fixes step by step.

Step 1: Is Redis Actually Running?

This sounds obvious, but you would be surprised how often the answer is simply "Redis is not running." Maybe it failed to start after a reboot, maybe a config error prevented it from launching, or maybe someone stopped it and forgot to restart it.

Start here:

# Check if Redis is running as a systemd service
sudo systemctl status redis

# Or if you use redis-server directly
ps aux | grep redis-server

# The quickest test of all
redis-cli ping

If redis-cli ping returns PONG, Redis is running and listening. You can skip to Step 2. If it returns Could not connect to Redis, keep reading.

Check the Redis log for startup errors:

# Default log location on most Linux distros
sudo tail -50 /var/log/redis/redis-server.log

# Or check journalctl
sudo journalctl -u redis -n 50 --no-pager

Common reasons Redis fails to start include a syntax error in redis.conf, another process already using port 6379, insufficient memory (Redis needs enough RAM for its dataset), or incorrect file permissions on the data directory. Fix the underlying issue, then start Redis:

sudo systemctl start redis
sudo systemctl enable redis   # ensure it starts on boot

Step 2: Check the Bind Address

This is the number one cause of "it works locally but not remotely." By default, Redis binds to 127.0.0.1, which means it only accepts connections from the same machine. If your application is on a different server, a different container, or a different pod, it cannot reach Redis.

Open your Redis configuration file:

# Typical locations
/etc/redis/redis.conf
/etc/redis.conf
/usr/local/etc/redis.conf    # macOS with Homebrew

Find the bind directive:

# Default: only localhost
bind 127.0.0.1 ::1

# Accept connections from all interfaces
bind 0.0.0.0

# Or bind to a specific private IP
bind 127.0.0.1 10.0.1.50

If you change the bind address to 0.0.0.0, Redis will listen on every network interface. This is necessary for remote connections, but it also means you must secure Redis with authentication and firewall rules. Never expose an unprotected Redis instance to the public internet.

After changing the bind address, restart Redis:

sudo systemctl restart redis

Verify it is listening on the right address:

ss -tlnp | grep 6379
# You should see 0.0.0.0:6379 or your specific IP

Step 3: Protected Mode

Redis 3.2 introduced protected mode as a safety net. If Redis is bound to all interfaces (0.0.0.0) and no password is set, protected mode automatically rejects connections from non-loopback addresses. You will see this error:

DENIED Redis is running in protected mode because protected mode is enabled
and no password is set for the default user

You have two options. The right option is to set a password:

# In redis.conf
requirepass YourStrongPasswordHere

# Then connect with
redis-cli -a YourStrongPasswordHere ping

The wrong option (but sometimes necessary for local development) is to disable protected mode:

# In redis.conf
protected-mode no

I want to be very clear: disabling protected mode on a Redis instance that faces the internet is asking for trouble. Attackers actively scan for open Redis instances and use them to write SSH keys, deploy cryptominers, or exfiltrate data. Always use authentication in any environment beyond your local laptop.

Step 4: Firewall Rules

Your Redis config might be perfect, but if a firewall is blocking port 6379, connections will still fail. The error might be ECONNREFUSED if the firewall actively rejects, or a timeout if it silently drops.

UFW (Ubuntu/Debian)

# Check current rules
sudo ufw status

# Allow Redis from a specific IP
sudo ufw allow from 10.0.1.0/24 to any port 6379

# Or from anywhere (not recommended for production)
sudo ufw allow 6379

iptables

# Check existing rules
sudo iptables -L -n | grep 6379

# Allow from a specific subnet
sudo iptables -A INPUT -p tcp -s 10.0.1.0/24 --dport 6379 -j ACCEPT

Cloud Security Groups (AWS, GCP, Azure)

If Redis is running on an EC2 instance, the security group attached to that instance must allow inbound TCP on port 6379 from the source IP or security group of your application. This is one of the most commonly missed steps when migrating from local development to cloud infrastructure. Check the security group in the AWS console or with the CLI:

aws ec2 describe-security-groups --group-ids sg-xxxxxxxx \
  --query "SecurityGroups[0].IpPermissions"

Step 5: Docker Networking

Docker adds an entire layer of networking complexity. The most common mistake is using localhost or 127.0.0.1 as the Redis host from inside a container. Inside a Docker container, localhost refers to the container itself, not the host machine and not other containers.

Docker Compose

When using Docker Compose, each service gets its own hostname matching the service name. If your docker-compose.yml looks like this:

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  app:
    build: .
    environment:
      REDIS_HOST: redis    # use the service name, NOT localhost
      REDIS_PORT: 6379
    depends_on:
      - redis

Your application should connect to redis:6379, not localhost:6379. Docker Compose creates a default network where services can reach each other by name.

Standalone Docker Containers

If you are running containers individually (not with Compose), they need to be on the same Docker network:

# Create a network
docker network create my-app-network

# Run Redis on that network
docker run -d --name redis --network my-app-network redis:7-alpine

# Run your app on the same network
docker run -d --name app --network my-app-network \
  -e REDIS_HOST=redis my-app

Without a shared network, containers are isolated and cannot communicate. You can also use the host network mode (--network host) as a quick workaround during development, but it defeats the purpose of container isolation and is not recommended for production.

Docker Desktop (Mac/Windows)

On Docker Desktop, if you want to connect from your host machine to Redis running in a container, use localhost:6379 (assuming you mapped the port with -p 6379:6379). But if you want to connect from one container to another, use the container name on a shared network as described above.

Step 6: Kubernetes Service Discovery

Kubernetes has its own DNS system for service discovery, and getting the hostname wrong is a very common cause of connection refused errors in K8s deployments.

If you deployed Redis with a Kubernetes Service, the DNS name follows this pattern:

<service-name>.<namespace>.svc.cluster.local

For example, if you have a Redis Service called redis-master in the production namespace:

# Full DNS name
redis-master.production.svc.cluster.local

# Short form (works within the same namespace)
redis-master

Common Kubernetes Redis issues and their fixes:

  • Wrong namespace: If your app and Redis are in different namespaces, you must use the full DNS name including the namespace.
  • Service type mismatch: A ClusterIP service is only reachable from within the cluster. If you need external access, use NodePort or LoadBalancer.
  • Pod not ready: If the Redis pod is crashing or not passing its readiness probe, the Service will not route traffic to it. Check kubectl get pods and kubectl describe pod.
  • NetworkPolicy blocking traffic: If you have NetworkPolicies in your cluster, they might be blocking ingress to the Redis pod on port 6379.

Verify the Service is working:

# Check the service exists and has endpoints
kubectl get svc redis-master -n production
kubectl get endpoints redis-master -n production

# Test connectivity from another pod
kubectl run debug --rm -it --image=redis:7-alpine -- redis-cli -h redis-master.production.svc.cluster.local ping

Step 7: ElastiCache and Managed Redis

Managed Redis services like AWS ElastiCache, Azure Cache for Redis, and Google Cloud Memorystore add their own layer of networking restrictions. The connection refused error on these services almost always comes down to network access.

AWS ElastiCache

ElastiCache clusters are deployed inside a VPC and are not publicly accessible by default. Your application must be in the same VPC (or a peered VPC) to reach the cluster. Key things to check:

  • Security group: The ElastiCache security group must allow inbound TCP on port 6379 from the security group of your application (EC2 instance, ECS task, Lambda, or EKS pod).
  • Subnet group: Ensure the ElastiCache subnet group includes subnets in the same availability zones as your application.
  • VPC peering: If your app is in a different VPC, you need VPC peering with proper route table entries.
  • Encryption in transit: If TLS is enabled, you must connect using rediss:// (note the double s) and your client must support TLS.
# ElastiCache connection string format
redis://your-cluster.xxxxx.ng.0001.use1.cache.amazonaws.com:6379

# With TLS enabled
rediss://your-cluster.xxxxx.ng.0001.use1.cache.amazonaws.com:6379

Important Note on ElastiCache

You cannot connect to ElastiCache from your local machine unless you set up a VPN, SSH tunnel, or bastion host. This trips up a lot of developers who can connect fine in staging but not from their laptop. Use an SSH tunnel as a workaround during development:

# SSH tunnel through a bastion host
ssh -L 6379:your-cluster.xxxxx.cache.amazonaws.com:6379 \
  ec2-user@bastion-host

Step 8: Application Connection Configuration

Sometimes Redis is perfectly fine and the issue is in how your application is configured. Here are the correct connection patterns for popular stacks.

Node.js (ioredis)

const Redis = require('ioredis');

const redis = new Redis({
  host: process.env.REDIS_HOST || '127.0.0.1',
  port: parseInt(process.env.REDIS_PORT) || 6379,
  password: process.env.REDIS_PASSWORD || undefined,
  retryStrategy(times) {
    const delay = Math.min(times * 50, 2000);
    return delay;
  },
  maxRetriesPerRequest: 3,
});

redis.on('error', (err) => console.error('Redis error:', err.message));
redis.on('connect', () => console.log('Redis connected'));

PHP / Laravel

# .env file
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

# For Docker Compose, use the service name
REDIS_HOST=redis

# For Kubernetes
REDIS_HOST=redis-master.production.svc.cluster.local

In config/database.php, Laravel reads these values automatically. If you are using phpredis (the C extension) instead of predis, make sure the extension is installed:

# Check if phpredis is installed
php -m | grep redis

# Install it on Ubuntu/Debian
sudo apt install php-redis
sudo systemctl restart php-fpm

Python (redis-py)

import redis
import os

r = redis.Redis(
    host=os.getenv('REDIS_HOST', 'localhost'),
    port=int(os.getenv('REDIS_PORT', 6379)),
    password=os.getenv('REDIS_PASSWORD', None),
    decode_responses=True,
    socket_connect_timeout=5,
    retry_on_timeout=True,
)

try:
    r.ping()
    print("Connected to Redis")
except redis.ConnectionError as e:
    print(f"Failed to connect: {e}")

Pro Tip: Redis 7+ requires ACL-based authentication by default, not just requirepass. If you upgraded to Redis 7 and your connections started failing, check your auth setup with ACL WHOAMI and ACL LIST. The default user still works with requirepass, but custom users need proper ACL rules.

Redis Deployment Types Compared

Different deployment models have different connection patterns and common failure modes. Here is a quick reference:

Deployment Type Connection String Common Issues
Standalone redis://host:6379 Bind address, protected mode, firewall
Sentinel redis+sentinel://sentinel1:26379/mymaster Wrong master name, sentinel not reachable
Cluster redis://node1:6379,node2:6379,node3:6379 NAT/port mapping breaks cluster bus, client must support cluster mode
ElastiCache rediss://xxx.cache.amazonaws.com:6379 Security groups, VPC access, TLS required
K8s StatefulSet redis-master.ns.svc.cluster.local:6379 Wrong namespace, NetworkPolicy, pod not ready

Timeout vs Connection Refused: Know the Difference

These two errors look similar but have completely different root causes, and troubleshooting them requires different approaches.

Connection refused (ECONNREFUSED) means the target host received your connection attempt and actively said no. The response is immediate (milliseconds). Causes: Redis is not running, wrong port, bind address excludes you, or a firewall is rejecting (not dropping) the packet.

Connection timed out (ETIMEDOUT) means your connection attempt got no response at all. You wait for the timeout period (usually 5 to 30 seconds) before the error appears. Causes: wrong IP address, packets being dropped by a firewall or security group, routing issues, or the host is completely unreachable.

If you are getting timeouts instead of refused errors, focus on network path issues: security groups, NACLs, route tables, VPC peering, and DNS resolution. If you are getting refused errors, focus on the Redis process itself: is it running, what is it bound to, and is protected mode blocking you.

Six Common Mistakes That Cause Connection Refused

  1. Using localhost inside a Docker container. Inside a container, localhost is the container itself. Use the service name or container name on a shared network.
  2. Forgetting to restart Redis after config changes. Editing redis.conf does not take effect until you restart the service. Run sudo systemctl restart redis.
  3. Security group allows the wrong source. You added a rule for your IP, but your app runs on a different EC2 instance with a different security group. Allow the app's security group, not your personal IP.
  4. Redis is running on a non-default port. Maybe someone changed it to 6380 or 6381 for multi-instance setups. Check with ss -tlnp | grep redis.
  5. The Redis container exited silently. Run docker ps -a to check if the container is actually running, not just created. Check logs with docker logs redis.
  6. IPv6 vs IPv4 mismatch. Redis might be bound to ::1 (IPv6 loopback) while your client is connecting to 127.0.0.1 (IPv4). Or vice versa. Check the bind directive covers both, or use bind 0.0.0.0 :: to cover all interfaces on both protocols.

Check Your Redis Exposure

An open Redis port on the internet is a critical security risk. Use SecureBin Exposure Checker to verify your infrastructure is not leaking services to the public.

Run a Free Exposure Check

Frequently Asked Questions

What is the difference between Redis connection refused and connection timed out?

Connection refused (ECONNREFUSED) means the target machine actively rejected the connection, usually because Redis is not running on that port or the bind address excludes your client IP. Connection timed out means packets are being dropped silently, typically by a firewall or security group rule. Refused is fast and definitive. Timed out is slow and usually a network layer problem.

Is it safe to bind Redis to 0.0.0.0?

Binding to 0.0.0.0 makes Redis listen on all network interfaces, which is necessary for remote connections. It is safe as long as you also set a strong password with requirepass (or ACL in Redis 7+), enable protected mode, and restrict access at the firewall or security group level. Never expose Redis to the public internet without authentication and network restrictions.

Why does my Redis connection work locally but fail from another container or pod?

This almost always comes down to one of three things: Redis is bound to 127.0.0.1 instead of 0.0.0.0, you are using localhost as the hostname instead of the container or service name, or the containers are on different Docker networks. In Kubernetes, make sure you are using the full service DNS name like redis-master.namespace.svc.cluster.local.

Wrapping Up

Redis connection refused errors are frustrating, but they are also one of the most predictable problems in infrastructure. The cause is almost always one of these: Redis is not running, it is bound to the wrong address, protected mode is blocking you, a firewall is in the way, or you are using the wrong hostname for your networking context (Docker, Kubernetes, managed service).

Work through the steps in order. Start with redis-cli ping on the Redis host itself, then expand outward: check the bind address, check authentication, check firewalls, and finally check application-level configuration. Ninety percent of the time, you will find the answer in the first three steps.

If you are dealing with Redis memory issues instead of connection issues, check out our guide on fixing Redis maxmemory reached errors. And if your Docker builds are getting slow, our Docker build cache invalidation guide might save you some time too.

Secure Your Infrastructure

Connection errors are annoying. Security breaches are expensive. Run a free exposure check to make sure your Redis, databases, and services are not accidentally public.

Scan Your Infrastructure Free
UK
Written by Usman Khan
DevOps Engineer | MSc Cybersecurity | CEH | AWS Solutions Architect

Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.