0% прочитано

Why localhost Does Not Work Between Docker Containers

localhost:9001 may open MinIO from the browser, but localhost inside the Laravel container points back to the application container itself. I explain why and how containers reach each other through the Docker network.

8 жовтня 2026 р. 8 хв читанняLaradock

While connecting MinIO locally, I ran into a very common Docker networking confusion.

The MinIO web console worked from the browser at localhost:9001. It seemed reasonable that Laravel could also reach MinIO through localhost.

But inside the application container, that address points somewhere completely different.

Every container has its own localhost.

Once I separated those network contexts, the model became simple: use an exposed port from the host machine and the service name for container-to-container communication.

localhost does not mean "this computer in general". It means the current network environment from which the connection is made.

Why localhost:9001 Works in the Browser

Host request through an exposed port
Browser   |   | http://localhost:9001   vComputer running Docker   |   | published port 9001   vMinIO container

The browser runs on the machine where Docker is running.

For the browser, localhost therefore means that host machine.

Docker exposes a container port to the host, so a request to localhost:9001 reaches the MinIO web console.

In this case, localhost is correct because the request originates outside the containers.

Inside a Container, localhost Means That Container Itself

localhost does not jump to another container
Laravel container      |      | http://localhost:9000      vLaravel container itself  MinIO container      X The request neverreached MinIO.

A container has its own network namespace.

If PHP inside a container opens http://localhost:9000, it looks for port 9000 inside that same container.

MinIO runs in another container, so the request never reaches it.

The problem is neither Laravel nor MinIO. The address simply points to the wrong network context.

127.0.0.1 Has the Same Meaning

Replacing localhost with 127.0.0.1 does not fix this problem.

Both addresses point back to the current network namespace.

Inside a container, that still means the same container rather than another Docker service.

Containers Reach Each Other by Service Name

Container-to-container request
Laravel container      |      | http://minio:9000      vDocker network      |      | service name: minio      vMinIO container

If both services are attached to the same Docker network, they can reach each other using service names.

For MinIO, the internal address in my case became:

http://minio:9000

Docker resolves the service name minio to the corresponding container.

I do not need to know its current internal IP address.

I Do Not Configure Services by Container IP

Fragile address
http://172.20.0.7:9000
Stable address through the service name
http://minio:9000

A container internal IP may change when the container is recreated.

The service name is a much more stable boundary.

I therefore configure the application with minio rather than a hardcoded 172.x.x.x address.

The Same Service May Have Different Addresses Depending on the Caller

The address depends on where the request originates
From the browser: http://localhost:9001 for the web console  From the Laravel container: http://minio:9000 for the S3-compatible API

At first this can look contradictory: MinIO appears to have two different addresses.

In reality, the service is the same. Only the path to it differs.

The host machine enters through an exposed Docker port, while another container uses the internal Docker network.

The addresses therefore do not conflict. They belong to different network contexts.

Container Ports and Host Ports Are Not Necessarily the Same

The host port may differ from the container port
Host localhost:19001        |        vDocker port mapping        |        vcontainer:9001

Docker may expose an internal container port under a different port number on the host.

For example, the host may use 19001 while the container still listens on 9001.

Container-to-container communication normally uses the service internal port rather than the host-facing port.

I Test Connectivity from the Application Container

Check MinIO from the container
curl -i \  http://minio:9000/minio/health/live

If Laravel needs to reach MinIO from the PHP container, I test exactly that route.

A working web console proves only that the path from the host to an exposed port is available.

It says nothing about communication between two containers.

A health check from the application environment is therefore a much more useful verification.

The Service Name Can Be Verified Separately

Check service-name resolution
getent hosts minio

If minio cannot be resolved, the problem may exist before HTTP is involved.

The containers may not share a network, or the service may use another name.

I therefore separate the checks: first confirm name resolution, then confirm that the required port responds.

The Laravel Endpoint Must Match the Application Network Context

Wrong for Laravel running inside a container
AWS_ENDPOINT=http://localhost:9000
MinIO address inside the Docker network
AWS_ENDPOINT=http://minio:9000AWS_USE_PATH_STYLE_ENDPOINT=true

If Laravel runs inside Docker, its S3 endpoint must be reachable from that environment.

For local MinIO, I therefore use the service name minio.

This is not a special Laravel requirement. It follows from where PHP is actually running.

Works in the Browser Does Not Mean Works from the Container

This is the main practical mistake I now check first.

The browser, PHP container, queue worker, another service, and MinIO itself may all run in different network contexts.

An address that works in one place is not guaranteed to work in another.

Before changing Laravel configuration or debugging the S3 driver, I check where the request actually originates.

The Mental Model That Keeps This Clear

Three rules that are usually enough
If the request starts on the host: localhost-> the host machine  If the request starts in a container: localhost-> that container  If a container needsanother container: service name

What I Check When One Container Cannot Reach Another Service

My short checklist:

  • Where does the request actually originate?
  • What does localhost mean in that environment?
  • Is the target container running?
  • Do both containers share a Docker network?
  • What is the target service name?
  • Can that name be resolved inside the container?
  • Which internal port does the service listen on?
  • Am I using a host-facing port where an internal port is required?
  • Does the service respond when checked directly from the container?

localhost Always Depends on Where You Are

The localhost problem becomes much simpler once localhost is no longer treated as a universal address for the whole machine.

For the browser, localhost means the host machine.

For Laravel inside a container, localhost means the Laravel container.

For Laravel to reach MinIO in another container, it needs the MinIO service name on the shared Docker network.

I now answer "which address should I use?" by first asking "where does this request originate?".

Before changing ports or configuration, I first determine whose localhost I am actually using.