# How to Configure Nginx as a Reverse Proxy for Docker Containers: A Complete Guide

> Learn to configure Nginx as a reverse proxy for Docker containers. This guide shows how to route traffic and terminate TLS using container names with proxy_pass directives.

- Repository: [Michael Royal/Self-Hosting-Guide](https://github.com/mikeroyal/Self-Hosting-Guide)
- Tags: how-to-guide
- Published: 2026-06-17

---

**Nginx can terminate TLS and route traffic to Docker containers by running on a shared Docker bridge network, using container names as upstream addresses in `proxy_pass` directives.**

The `mikeroyal/Self-Hosting-Guide` repository documents several methods to configure Nginx as a reverse proxy for Docker containers, ranging from manual configuration to automated solutions like `nginx-proxy` and Nginx Proxy Manager. This guide walks you through the architectural patterns and production-ready implementations found in the repository.

## Architecture Overview

A typical reverse proxy setup routes traffic through a clean separation of concerns:

```

Client → Internet → Nginx (host) → Docker bridge network → Container(s)

```

**Nginx runs either on the host or in its own container**, listening on ports 80 and 443. It forwards requests based on the `Host` header or request path to the appropriate backend container. **Docker containers expose only their internal ports**, avoiding port collisions on the host and keeping the network surface minimal. **TLS termination occurs at the Nginx layer**, allowing backend containers to run unencrypted HTTP internally, which simplifies image builds and certificate management.

## Setting Up the Docker Network

Before configuring Nginx, create an isolated bridge network that allows the proxy to communicate with application containers using DNS names.

```bash

# Create an isolated network for the proxy and the app containers

docker network create proxy-tier

```

This network enables Nginx to reach containers by their container names (e.g., `webapp:8080`) rather than IP addresses, which is essential for dynamic container environments.

## Configuring Nginx as a Reverse Proxy

### Manual Configuration

For static setups, mount a custom Nginx configuration file into an Nginx container. Create a virtual host file at [`nginx/conf.d/app.example.com.conf`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/nginx/conf.d/app.example.com.conf):

```nginx
server {
    listen 80;
    server_name app.example.com;

    # Optional TLS configuration

    # listen 443 ssl;

    # ssl_certificate     /etc/nginx/certs/app.example.com.crt;

    # ssl_certificate_key /etc/nginx/certs/app.example.com.key;

    location / {
        proxy_pass http://webapp:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

```

The `proxy_pass` directive uses the container name `webapp` and internal port `8080`, relying on Docker's embedded DNS resolver on the `proxy-tier` network.

### Automatic Configuration with docker-gen

For dynamic environments where containers start and stop frequently, the `nginx-proxy` project (referenced in the repository's [`README.md`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/README.md) under the **Nginx Proxy** section) uses `docker-gen` to automatically regenerate configuration files.

Add a `docker-gen` service to your [`docker-compose.yml`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/docker-compose.yml):

```yaml
  nginx-gen:
    image: nginxproxy/docker-gen
    container_name: nginx-gen
    restart: unless-stopped
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - /var/run/docker.sock:/tmp/docker.sock:ro
    command: -watch -notify-sighup nginx /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
    depends_on:
      - nginx
    networks:
      - proxy-tier

```

When application containers set the `VIRTUAL_HOST` environment variable, `docker-gen` detects the change, renders a new Nginx configuration from the template, and sends a `SIGHUP` signal to reload Nginx without dropping connections.

## Production-Ready Docker Compose Configuration

Below is a complete [`docker-compose.yml`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/docker-compose.yml) that implements the manual configuration pattern described in the `mikeroyal/Self-Hosting-Guide` repository:

```yaml
version: "3.9"

services:
  nginx:
    image: nginx:stable-alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/certs:/etc/nginx/certs:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy-tier

  webapp:
    image: nginx:alpine
    container_name: webapp
    restart: unless-stopped
    expose:
      - "8080"
    environment:
      VIRTUAL_HOST: app.example.com
    networks:
      - proxy-tier

networks:
  proxy-tier:
    external: true

```

This configuration mounts the `docker.sock` file for optional integration with automation tools, exposes only port 8080 internally (not mapped to the host), and connects both services to the external `proxy-tier` network.

## Alternative: Nginx Proxy Manager

According to the [`README.md`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/README.md) section **Nginx Proxy Manager (NPM)**, the repository also recommends a UI-driven alternative for those who prefer graphical management. Nginx Proxy Manager runs as a Docker container and provides a web interface for configuring reverse proxies, managing SSL certificates, and setting up redirections, eliminating the need to manually edit Nginx configuration files.

## Summary

- **Create a dedicated Docker network** (`proxy-tier`) to enable DNS-based service discovery between Nginx and your containers.
- **Expose only internal ports** on application containers to avoid host port conflicts and reduce the attack surface.
- **Use `proxy_pass` with container names** (e.g., `http://webapp:8080`) to route traffic, leveraging Docker's internal DNS.
- **Terminate TLS at the Nginx layer** to simplify certificate management and keep backend traffic unencrypted.
- **Automate configuration** with `docker-gen` or use Nginx Proxy Manager for a UI-based approach as documented in the `mikeroyal/Self-Hosting-Guide` repository.

## Frequently Asked Questions

### How does Nginx resolve container names as upstream addresses?

When Nginx and application containers share the same Docker bridge network (such as `proxy-tier`), Docker's embedded DNS server automatically resolves container names to their current IP addresses. Nginx uses standard DNS resolution for the hostname in the `proxy_pass` directive, allowing containers to be restarted or recreated with different IPs without reconfiguring Nginx.

### Should Nginx run on the host or inside a container?

Both approaches work, but running Nginx in a container is generally preferred for consistency and ease of deployment. When containerized, Nginx can still bind to host ports 80 and 443 via port mappings, and it communicates with backend containers over the Docker network. This approach matches the containerized patterns documented in the `mikeroyal/Self-Hosting-Guide` repository.

### What is the difference between `expose` and `ports` in Docker Compose?

The `expose` directive in a [`docker-compose.yml`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/docker-compose.yml) file makes a port available to other containers on the same network without publishing it to the host, while `ports` creates a binding between the host's network interface and the container. For reverse proxy setups, backend containers should use `expose` for their service ports, while the Nginx container uses `ports` to accept public traffic on 80 and 443.

### How do I handle SSL certificates when using Nginx as a reverse proxy?

Mount SSL certificates into the Nginx container at a path like `/etc/nginx/certs/` and configure the `ssl_certificate` and `ssl_certificate_key` directives in your server block. Nginx terminates the TLS connection and forwards unencrypted HTTP to the backend containers. Tools like `nginx-proxy` or Nginx Proxy Manager can automatically provision and renew Let's Encrypt certificates based on the `VIRTUAL_HOST` environment variable.