Fully automated Nginx reverse proxy for Docker and Swarm.
No more writing configuration files. nginx-proxy watches for container changes and automatically generates reverse proxy configurations. It supports SSL via Let's Encrypt, Basic Auth, and custom Nginx directives—all configured through environment variables.
- Zero-config Nginx: Automatically discovers and routes traffic to containers and services
- SSL Automation: Automatic SSL certificates via Let's Encrypt (ACME).
- Flexible Routing: Host-based, path-based, and multiple domains per container.
- Advanced Features: Sticky sessions, basic auth, redirects, and custom Nginx directives.
- Swarm Ready: Compatible with Docker Swarm services.
nginx-proxy is built on the belief that infrastructure should be invisible. Each service keeps its configuration.
Note When migrating to v3, old volumes should be removed.
docker network create frontend
docker run -d --restart always \
--name nginx-proxy \
--network frontend \
-p 80:80 \
-p 443:443 \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v nginx_data:/etc/nginx \
-v nginx_logs:/var/log/nginx \
mesudip/nginx-proxy:v3Start a container on the same network and set the VIRTUAL_HOST environment variable.
docker run -d \
--name wordpress \
--network frontend \
-e VIRTUAL_HOST="blog.example.com" \
wordpressdocker run -d \
--name registry \
--network frontend \
-e VIRTUAL_HOST="htttps://registry.example.com/v2" \
-e PROXY_BASIC_AUTH="registry.example.com -> user1:pass1,user2:pass2" \
-e "client_max_body_size=2g" \
registry:2/etc/nginx/conf.d– rendered configs (nginx-proxy.conf) plus any custom files you mount./etc/nginx/ssl– certificate material, split into/certsand/private(override withSSL_DIR,SSL_CERTS_DIR,SSL_KEY_DIR)./etc/nginx/challenges– ACME challenge payloads, configurable viaCHALLENGE_DIRandWELLKNOWN_PATH./var/log/nginx– access/error logs.
Control the default behavior of nginx-proxy:
| Variable | Default | Description |
|---|---|---|
CLIENT_MAX_BODY_SIZE |
1m |
Default max body size for uploads. |
NGINX_WORKER_PROCESSES |
auto |
Number of Nginx worker processes. |
NGINX_WORKER_CONNECTIONS |
65535 |
Max connections per worker. |
CERT_RENEW_THRESHOLD_DAYS |
30 |
By default certificates are renewed when they have <=30 days remaining. |
ENABLE_IPV6 |
false |
Enable IPv6 support on nginx. |
DOCKER_SWARM |
ignore |
Controls Docker Swarm discovery. Supported values are ignore, exclude, enable, prefer-local, and strict; see Docker Swarm Support. |
SWARM_DOCKER_HOST |
- | URL of the Swarm manager socket (e.g., tcp://manager:2375). |
CERTAPI_URL |
- | External Certificate API URL. |
CERTAPI_BATCH_DOMAINS |
true |
When using CERTAPI_URL, request safe domain batching (batch_domains=true) to avoid recursive domain-order errors. |
CHALLENGE_DIR |
/etc/nginx/challenges/ |
Base directory for acme challenge store, when requesting certificates with acme. .well-known/acme-challenge folder lives inside this. |
CLOUDFLARE_API_KEY_KEY* |
- | Cloudflare api keys to issue DNS certificates. |
BACKEND_START_GRACE_SECONDS |
10 |
Delay registering containers without a Docker healthcheck so crashing backends dont' result reload |
To expose a container, set the VIRTUAL_HOST* environment variable. The container must be on the same Docker network as nginx-proxy.
VIRTUAL_HOST=[<scheme>://] <domain> [<path>] [-> [:<port>] <path>] [; <nginx_directives>]
| VIRTUAL_HOST | Description |
|---|---|
example.com |
Proxies to exposed container port. |
example.com -> :8080 |
Proxies to port 8080. |
https://example.com |
HTTPS proxy to container exposed http port |
https://example.com/api |
Only /api path is passed on to container. /api prefix is not removed |
https://example.com/api -> /v1 |
Re-maps path /api to /v1 on container exposed port |
https://example.com/api -> :8080/v1 |
Re-maps path /api to /v1 on container port 8080 |
https://example.com/api -> https://_:8080/v1 |
Re-maps path /api to /v1 on port 8080. Https is used to connect to container |
Note: Directives separated by ; are injected into the Nginx location block.
Example: VIRTUAL_HOST=site.com; proxy_read_timeout 900;
WebSocket support requires explicit configuration if the protocol is not detected automatically.
- Explicit:
VIRTUAL_HOST=wss://ws.example.com -> :8080/websocket - Auto-Upgrade:
VIRTUAL_HOST=https+wss://example.com(Supports both HTTP and WSS on te host).
A single container can serve multiple domains path mappings. All that matters is that env variable starts with VIRTUAL_HOST e.g. VIRTUAL_HOST1, VIRTUAL_HOST2, etc.
Example:
docker run -d --network frontend \
-e "VIRTUAL_HOST_API=https://api.example.com -> :3000" \
-e "VIRTUAL_HOST_ADMIN=https://admin.example.com -> :4000" \
my-appProxy to external hosts, (not in Docker) using STATIC_VIRTUAL_HOST. The container must be running for the site to be live.
Format: STATIC_VIRTUAL_HOST=domain.com->http://192.168.0.1:8080.
Note Be aware that if domain as target, nginx will crash if DNS resolution fails.
Warning : Automatic exposed port detection will not work when swarm support is enabled. You must explicitly set port on the VIRTUAL_HOST.
Docker Swarm discovery is controlled by the DOCKER_SWARM environment variable on the nginx-proxy container.
DOCKER_SWARM value |
Local containers | Swarm services | Use case |
|---|---|---|---|
ignore |
Included | Not discovered | Default Docker-only behavior. Swarm task containers are treated like standalone containers if they are visible on the local Docker socket. |
exclude |
Included | Not discovered | Docker-only discovery while explicitly ignoring containers that belong to Swarm services. |
enable |
Included | Included | Mixed mode. Use this when nginx-proxy should route both standalone containers and Swarm services. |
prefer-local |
Included | Included | Mixed Swarm mode that prefers healthy local task containers and keeps the service VIP as a fallback. |
strict |
Excluded | Included | Swarm-only mode. Use this when nginx-proxy should route only Swarm services. |
ignore is the default and does not require the Docker node to be in Swarm mode. In this mode, nginx-proxy only reads the normal Docker container API. If a Swarm task container is visible on the local Docker socket, it can be registered as if it were a regular container.
exclude still uses only the local Docker container API, but skips containers that have Swarm service labels. This is useful when the same Docker host runs standalone containers and Swarm services, but this proxy instance should only manage standalone containers.
enable reads both local containers and Swarm services. Standalone containers are discovered from the local Docker socket. Swarm services are discovered from the Swarm manager API, and task containers are skipped so each service is registered once.
prefer-local reads both local containers and Swarm services, but local Swarm task containers are also discovered from the local Docker socket. When a route has local containers and the Swarm service VIP, nginx sends normal traffic to the local containers and marks the service VIP as a backup upstream server. If no local container is available, the service VIP is used normally. Existing container healthcheck and BACKEND_START_GRACE_SECONDS behavior still applies before local containers are registered.
strict reads only Swarm services. Local standalone containers are ignored, and Swarm task containers are also ignored. This is the mode to use when this proxy instance is dedicated to Swarm routing.
For enable, prefer-local, and strict, the Swarm API client must be connected to a manager node because Docker only allows managers to list services. If nginx-proxy is running on a worker node, set SWARM_DOCKER_HOST to a reachable manager Docker API endpoint:
-e DOCKER_SWARM=enable \
-e SWARM_DOCKER_HOST=tcp://manager:2375If SWARM_DOCKER_HOST is not set, the local Docker socket is used for both local containers and Swarm services. When SWARM_DOCKER_HOST is set, nginx-proxy uses the local Docker socket for standalone containers and the remote manager socket for Swarm services. If the local Docker socket cannot be reached but SWARM_DOCKER_HOST is set, nginx-proxy switches to strict mode and uses only the remote Swarm manager.
Redirect traffic from one domain to another.
-e 'VIRTUAL_HOST=https://example.uk' \
-e 'PROXY_FULL_REDIRECT=example.com,www.example.uk -> example.uk'Enable session affinity (sticky sessions) for load balancing.
Set NGINX_STICKY_SESSION on the backend container.
trueorip_hash– enableip_hashbalancing.false– disable stickiness (round-robin).- Any other string – injected verbatim (e.g.,
hash $cookie_sessionid consistent).
nginx-proxy automatically requests and renews Let's Encrypt certificates.
- Expose ports 80 and 443 on
nginx-proxy. - Map
/etc/nginx/ssland/etc/nginx/challenges. - Set
VIRTUAL_HOSTto start withhttps://in your containers.
To issue wildcard certificates (e.g., *.example.com) or avoid exposing port 80, you can use DNS challenges via Cloudflare.
- Set the
CLOUDFLARE_API_KEY*environment variables onnginx-proxy.nginx-proxysupports multiple api-keys and will discover available domains. - Set
VIRTUAL_HOSTto a wildcard domain (e.g.,*.example.com) or just use it for regular domains to use DNS validation.
Example:
docker run -d \
-e CLOUDFLARE_API_KEY_KEY1="your-cloudflare-api-token" \
...
mesudip/nginx-proxyMount your own certificates:
- Directory:
/etc/nginx/ssl/certs - Naming:
example.com.crtandexample.com.key
Wildcard support: *.example.com.crt will match sub.example.com.
Use the getssl command inside the container:
docker exec nginx-proxy getssl example.comProtect your services with HTTP Basic Auth.
Set PROXY_BASIC_AUTH environment variable.
- Per-Host:
example.com -> user:password - Per-Path:
example.com/admin -> user:password
Note: Passwords should not be base64 encoded; the proxy handles hashing.
Requests with an unknown Host header return 503 Service Unavailable.
Control this with the DEFAULT_HOST variable on the nginx-proxy container:
true(default): Returns 503.false: Passes default requests to the first configured server.
To route unknown traffic to a specific container:
-e "VIRTUAL_HOST=fallback.example.com" \
-e "PROXY_DEFAULT_SERVER=true"docker exec nginx-proxy verify www.example.com ## check if request routes back to this server.
docker exec nginx-proxy getssl www.example.com example.com www2.example.com # issue certificate
docker exec nginx-proxy reload # rescan Docker state and reload nginx config
We are constantly improving nginx-proxy to make it the most robust and versatile zero-config reverse proxy solution. Here’s what’s coming next:
- High Availability & Multi-Node Swarm Deployment: Comprehensive guides and templates for deploying
nginx-proxyin a multi-node Swarm environment for maximum uptime. - 100% Test Coverage: Ensuring rock-solid stability and reliability for every release.
- Remote Swarm Cluster Support: Monitor and route traffic for Swarm clusters running on remote nodes seamlessly.
- Pangolin Support: Integration with Pangolin as an alternative reverse-proxy engine.