How to Configure Nginx Reverse Proxy for Node.js in Docker Compose: Connecting Containers Properly


4 views

When running multiple services in Docker Compose, a common pain point is getting services to communicate with each other. The configuration you shared attempts to proxy requests from Nginx to a Node.js server, but there's a critical networking issue.

In your nginx.conf, you're using proxy_pass http://localhost:8000 which won't work because:

  • localhost refers to the Nginx container's own loopback interface
  • The Node.js server runs in a separate container with its own network namespace

Docker Compose automatically creates a dedicated network for your services where:

  • Each service can be reached by its service name
  • DNS resolution is automatically configured
  • Containers can communicate over this private network

Here's the corrected version of your setup:

version: '3.8'
services:
    server:
        build:
            context: ../../
            dockerfile: ./packages/website/Dockerfile
        command: node /cutting/index.js
        environment:
            PORT: 8000
            NODE_ENV: production
        restart: always
        expose:
            - "8000"  # Explicitly expose the port to other containers
    
    nginx:
        build:
            context: ./
            dockerfile: ./nginx/Dockerfile
        ports:
            - "80:80"  # Changed from 8000:8000 to standard HTTP port
        depends_on:
            - server
        restart: always

The key change is in the proxy_pass directive:

server {
    # ... other config remains the same ...
    
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        
        # Changed from localhost to service name
        proxy_pass http://server:8000;
    }
}

When implementing this solution:

  • Use depends_on carefully - it only waits for container startup, not application readiness
  • Consider adding health checks to your services
  • For production, use proper restart policies and logging
  • You might want to add a custom network with specific settings

For more complex scenarios, you might want this setup:

version: '3.8'
services:
    server:
        # ... previous config ...
        healthcheck:
            test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
            interval: 30s
            timeout: 10s
            retries: 3
    
    nginx:
        # ... previous config ...
        depends_on:
            server:
                condition: service_healthy

When running Nginx and Node.js in separate Docker containers, you can't use localhost:8000 in your Nginx configuration because each container has its own network namespace. Instead, you need to use Docker's internal DNS resolution.

version: '3'
services:
    node_app:
        build:
            context: ../../
            dockerfile: ./packages/website/Dockerfile
        command: node /cutting/index.js
        environment:
            PORT: 8000
            NODE_ENV: production
        restart: always
        expose:
            - "8000"
    nginx:
        build:
            context: ./
            dockerfile: ./nginx/Dockerfile
        depends_on:
            - node_app
        ports:
            - "80:80"
        restart: always

The key change is in the proxy_pass directive:

location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://node_app:8000;
}
  • Use the service name (node_app) instead of localhost
  • Added expose to make the Node.js port available to other containers
  • Changed Nginx's external port mapping to standard HTTP port 80
  • Removed the tail -f /dev/null command as it's not needed

To test if containers can communicate:

docker-compose exec nginx ping node_app

For better performance, consider adding these to your Nginx config:

proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;