Docker
Go
GRPC
Networking
Troubleshooting

Cannot connect to Go GRPC server running in local Docker container

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When a Go gRPC server works locally but not from a Docker container, the root cause is usually networking, not gRPC itself. The server may be listening on the wrong interface, the container port may not be published, or the client may be dialing the wrong hostname. The fastest way to debug this is to separate three layers: server bind address, Docker port mapping, and client target address.

Make Sure The Server Listens On All Interfaces

Inside a container, listening on localhost only exposes the service inside that container. The host machine cannot reach it through Docker port mapping unless the process listens on 0.0.0.0.

go
1package main
2
3import (
4    "log"
5    "net"
6
7    "google.golang.org/grpc"
8)
9
10func main() {
11    lis, err := net.Listen("tcp", ":50051")
12    if err != nil {
13        log.Fatal(err)
14    }
15
16    server := grpc.NewServer()
17    log.Println("gRPC server listening on :50051")
18    log.Fatal(server.Serve(lis))
19}

Using :50051 means “listen on all interfaces on port 50051,” which is what you usually want in a container.

Publish The Container Port Correctly

The next layer is Docker port mapping. If the server listens on port 50051 in the container, publish that port to the host.

bash
docker run --rm -p 50051:50051 my-grpc-image

Or in Docker Compose:

yaml
1services:
2  grpc-server:
3    build: .
4    ports:
5      - "50051:50051"

Without this mapping, the container may be healthy but unreachable from the host.

Dial The Right Address From The Client

If the client is running on the host machine, it should usually dial the published host port, not the container’s internal DNS name.

go
1package main
2
3import (
4    "context"
5    "log"
6    "time"
7
8    "google.golang.org/grpc"
9    "google.golang.org/grpc/credentials/insecure"
10)
11
12func main() {
13    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
14    defer cancel()
15
16    conn, err := grpc.DialContext(
17        ctx,
18        "localhost:50051",
19        grpc.WithTransportCredentials(insecure.NewCredentials()),
20        grpc.WithBlock(),
21    )
22    if err != nil {
23        log.Fatal(err)
24    }
25    defer conn.Close()
26
27    log.Println("connected")
28}

If the client runs in another container on the same Docker network, localhost is wrong. In that case, dial the service name or container hostname instead.

Host Client Versus Container Client Matters

This distinction causes a lot of confusion:

  • host client to container server: usually use localhost:published_port,
  • container client to sibling container server: usually use service name and internal port,
  • container client to host service: often use a host-specific route such as host.docker.internal where supported.

If you do not identify where the client is running, you can spend a long time debugging the wrong address.

Check Basic Connectivity Before Blaming gRPC

Before assuming protobuf or TLS problems, confirm the service is reachable at the TCP level.

bash
docker ps

Check that the published port is present. Then inspect logs:

bash
docker logs <container_id>

If the server never started listening, gRPC configuration is irrelevant because the container is not actually serving yet.

TLS And Plaintext Must Match Too

If the server expects TLS and the client uses insecure credentials, the connection will fail for a different reason. Likewise, if the server is plaintext and the client expects TLS, the dial attempt will not succeed.

So after networking is correct, make sure both sides agree on:

  • plaintext versus TLS,
  • port number,
  • hostname or target address.

Common Pitfalls

  • Binding the Go server to localhost inside the container instead of all interfaces.
  • Forgetting to publish the container port with -p or Compose ports.
  • Dialing localhost from one container when the target is a different container.
  • Debugging protobuf or gRPC application logic before checking basic TCP reachability.
  • Mixing TLS and insecure client settings across server and client.

Summary

  • In Docker, the server should usually listen on :50051 or 0.0.0.0:50051.
  • Publish the container port to the host if the client runs outside the container network.
  • Use localhost only when the client truly runs on the host and the port is published.
  • Use service names for container-to-container calls on the same Docker network.
  • Separate network debugging from gRPC protocol debugging to find the real failure quickly.

Course illustration
Course illustration

All Rights Reserved.