# Why 127.0.0.1 Doesn’t Reach Local Redis in Docker

20 Oct 2025 • 5 minutes

Junwen Feng  
Engineer

## Scenario Description

At InsForge, our cloud backend is built on OpenResty as a reverse proxy, paired with a Redis Cluster for service discovery. This setup lets us dynamically route developer API requests based on real-time project information.

Recently, we wanted to replicate this setup in a smaller environment for local experiments. To keep things simple, Redis and OpenResty each run in their own Docker Compose project: one serving as the storage backend and the other acting as the reverse proxy.

The project structure looks like this:

```text

```

Below are the example configurations for each service.

**Redis**

```yaml

```

**OpenResty**

```yaml

```

## The Problem: 127.0.0.1 Cannot Connect to Redis

In OpenResty’s Lua code, we attempt to connect to Redis:

```lua

```

But when this code runs, the logs show an unexpected error:

```text

```

This is unexpected. Redis has already been mapped to the host port `6379` using `-p 6379:6379`, which makes it accessible from the host machine. However, when accessed from within another container, the connection fails.

## Understanding Docker Networking

### The Principle of Docker Network Isolation

To understand this behavior, we need to look at how Docker manages container networking. Every container runs in its own virtual network stack, which provides:

- Its own network interfaces
- Its own IP address
- Its own routing table
- Its own port space

When a container starts, Docker automatically creates this virtual network namespace. Within this environment:

- `127.0.0.1` (localhost) refers **only to the container itself**, not the host
- Each container receives a private IP address, typically within the subnet of the `docker0` bridge
- Port mappings such as `-p 6379:6379` apply only to traffic between the host and the container, not between containers

### Network Architecture Diagram

```text

```

### Why Doesn’t 127.0.0.1 Work?

When the OpenResty container attempts to connect to `127.0.0.1:6379`, the connection is handled entirely within its own namespace.

1. The request is scoped to the OpenResty container’s local network stack.
2. Inside that namespace, `127.0.0.1` resolves to the OpenResty container itself.
3. Redis is running in a different container, with a separate namespace.
4. Therefore, no process is listening on port `6379` inside OpenResty, resulting in a “connection refused” error.

Although Redis’s port is mapped to the host, this mapping is not visible inside other containers. Each container is isolated by design, so they cannot reach each other through `127.0.0.1` unless explicitly connected to a shared network.

## Correct Connection Methods

There are several ways to connect containers so that OpenResty can successfully reach Redis. Each approach depends on your environment and use case.

### Option 1: Use the Host Gateway IP

Each Docker network defines a gateway IP, usually the first address in the subnet. For the default bridge network, this is often `172.17.0.1`.

To find the gateway IP inside the container:

```bash

```

Alternatively, you can check `/etc/hosts`:

```bash

```

Once you have the gateway IP, update the Lua connection code as follows:

```lua

```

The gateway IP acts as a bridge between the container and the host. Containers can use this address to access host-exposed ports, such as Redis mapped with `-p 6379:6379`.

### Option 2: Use host.docker.internal (Recommended for Development)

Docker Desktop provides a special DNS name `host.docker.internal`, which resolves to the host IP. This allows containers to communicate with services exposed on the host.

```lua

```

This approach works well for local development and testing because it requires no additional network setup.

Notes:

- Works by default on Docker Desktop for Mac/Windows
- On Linux, it must be added manually by including a host entry when starting the container: `--add-host=host.docker.internal:host-gateway`

This method provides a simple way to reach host-exposed ports without modifying Docker Compose networks. However, it is generally best suited for local development, not production environments.

### Option 3: Use a Shared Docker Network (Best Practice)

For long-term maintainability and service-to-service communication, the recommended approach is to connect containers through a shared Docker network.

Start by creating an external network:

```bash

```

Then configure both Docker Compose projects to use this shared network.

Redis `docker-compose.yml`:

```yaml

```

OpenResty `docker-compose.yml`:

```yaml

```

Once both containers are connected to the same network, Docker’s built-in DNS automatically resolves service names. The OpenResty container can connect to Redis using its service name.

Then in Lua:

```lua

```

Using a shared network keeps configuration clean, avoids host-specific dependencies, and ensures reliable communication across environments. This is considered the best practice for production deployments.

## How Docker Compose Service Name Resolution Works

When multiple services are defined in the same `docker-compose.yml`:

```yaml

```

The OpenResty container can connect using `redis` as the hostname because:

### 1. Docker’s Built-in DNS Server

Each user-defined Docker network includes an internal DNS server, typically located at `127.0.0.11`. This server maintains the mapping between service names and container IP addresses.

You can confirm this by checking the container’s resolver configuration:

```bash

```

### 2. Service Discovery

Docker Compose automatically performs the following for each service:

- Creates a DNS record for each service name
- Resolves the name to the container IP
- Supports round-robin DNS for scaled services

Example:

```bash

```

This means any container in the same Docker network can connect to `Redis` simply by referencing `redis` as the hostname.

### 3. Network Aliases

Docker also supports network aliases, allowing multiple hostnames to resolve to the same container.  
Example configuration:

```yaml

```

In this configuration, the names `redis`, `redis-master`, and `cache-server` all resolve to the same Redis container. This can be helpful when multiple hostnames are required for compatibility or migration.

### 4. Cross-Compose Service Discovery

By default, services defined in different Docker Compose projects cannot communicate with each other because each project creates its own isolated network.  
To enable cross-project communication, you can use one of the following approaches:

- Connect both projects to a shared external network (see Option 3)
- Use the host gateway or port mappings (see Options 1 and 2)

Once both services are attached to the same network, Docker’s internal DNS will resolve all service names within that network, allowing seamless communication between containers across projects.

## Best Practice Recommendations

### For Development

For local development, the simplest and most reliable approach is to connect all containers through a shared Docker network.

```bash

```

Once the network is created, attach each service to it through its respective Compose file. Containers on the same network can communicate directly using service names, without relying on IP addresses or host mappings.

### For Production

In production environments, network design and service discovery should be more structured. Consider the following approaches:

1. Use **Kubernetes** for built-in service discovery and orchestration.
2. Deploy a **Service Mesh** such as Istio or Linkerd to manage advanced routing, observability, and security.
3. Integrate **dedicated discovery tools** like Consul or Etcd for fine-grained control in distributed systems.

These solutions provide better scalability, reliability, and visibility than manual container networking.

### Configuration Management

To make the setup more flexible, externalize the Redis connection configuration instead of hardcoding host and port values.

`config.lua`

```lua

```

In your `docker-compose.yml`, set the environment variables so each service can load its configuration dynamically:

```yaml

```

## Summary

Docker network isolation is powered by Linux namespaces, giving each container its own network stack. Key takeaways:

1. `127.0.0.1` inside a container points to **itself**, not the host
2. Port mapping operates **on the host**, not between containers
3. Reliable inter-container communication methods include:
   - The host gateway or `host.docker.internal` for host-level access.
   - A shared Docker network with service-based DNS resolution (recommended).
4. Docker Compose uses an internal DNS service to automatically resolve service names to container IPs.

Understanding these principles helps prevent connectivity issues and leads to cleaner, more maintainable microservice architectures.
