nginx-proxy: label-driven reverse proxying for Docker containers
Automated Nginx Reverse Proxy for Docker
At a glance
- What is it?
- nginx-proxy pairs nginx with docker-gen to regenerate reverse proxy configs as containers start and stop. It is a configuration generator, not a control panel, and that distinction decides who should use it.
- Who is it for?
- Adopt nginx-proxy if your services already live in Docker, you want proxying driven by container metadata, and you are comfortable reading generated nginx configs when something looks wrong. Do not adopt it if you want a browser UI for adding hosts and certificates, or if your upstreams are not containers on a shared Docker network.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem nginx-proxy solves for Docker users
Every container you add to a host needs an nginx server block somewhere. Doing that by hand means editing a config file, reloading nginx, and repeating the process when the container is replaced. nginx-proxy removes that loop by watching the Docker API and writing the proxy configuration itself. The README describes the project as a container running nginx and docker-gen, where docker-gen generates reverse proxy configs and reloads nginx when containers are started and stopped.
The audience is narrow but well defined. You need services running as Docker containers on the same host, DNS already pointing at that host, and a willingness to describe routing through environment variables instead of hand-written config. If your upstreams are bare-metal processes, VMs, or services in a different orchestrator, the container-event model has nothing to react to.
How docker-gen turns container events into nginx server blocks
The repository layout makes the mechanism visible. There is a single nginx.tmpl file at the top level, and docker-gen is the component that renders it. The template is the source of truth for what a generated server block looks like; the Python code in app/ and the Dockerfiles wrap the two processes together.
The data flow is one-directional. A container starts with an environment variable such as VIRTUAL_HOST set. docker-gen sees the container event, queries the Docker API for the running set, renders nginx.tmpl against that set, writes the result, and signals nginx to reload. When the container stops, the same path runs in reverse and the server block disappears.
Two constraints follow from this design. Proxied containers must expose the port to be proxied, either through the EXPOSE directive or the --expose flag. They must also share at least one Docker network with the nginx-proxy container. The README is explicit that if you do not pass --net when creating the proxy container, it attaches only to the default bridge network and cannot reach containers on other networks. That second point is the most common cause of a proxy that starts cleanly and routes nothing.
Installing nginx-proxy and proxying a first container
The README gives a docker run example. It starts the proxy detached, publishes port 80, and mounts the Docker socket read-only at /tmp/docker.sock.
docker run --detach \
--name nginx-proxy \
--publish 80:80 \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy:1.11Then start a container you want proxied, with VIRTUAL_HOST set to the hostname that should route to it.
docker run --detach \
--name your-proxied-app \
--env VIRTUAL_HOST=foo.bar.com \
nginxWith DNS resolving foo.bar.com to the host, a request to http://foo.bar.com is routed to the container carrying that VIRTUAL_HOST value. Note the tag in the first command: 1.11, not latest. The README states that the latest, alpine and dockergen tags point to the latest commit on main and carry no stability promise, and that you should specify the version explicitly.
The repository also ships a docker-compose.yml that wires the same idea together with a whoami test service.
services:
nginx-proxy:
image: nginxproxy/nginx-proxy
container_name: nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
whoami:
image: jwilder/whoami
environment:
- VIRTUAL_HOST=whoami.exampleThat file uses the bare nginxproxy/nginx-proxy image, which is the floating tag the README warns about, so treat the compose file as a starting point rather than a production manifest. One documented restriction worth knowing before you design hostnames: a port number inside VIRTUAL_HOST is not supported. The README points to virtual ports and custom external HTTP/HTTPS ports in the docs instead.
Where nginx-proxy is the wrong tool
The README does not document rollback, and it does not describe what happens to a generated config when docker-gen renders a template that fails. There is no described dry-run mode in the README. If a template edit is wrong, the failure surfaces at reload time and you are debugging generated nginx config rather than source you wrote.
The bigger limitation is scope. The README's own framing is reverse proxy config generation, and the docs section is where anything beyond that lives; nothing in the README describes certificate issuance by the proxy itself. If you expect a dashboard, per-host forms, or certificate management from a browser, this project does not present itself that way.
It is also the wrong choice when your routing is not container-driven. A single static nginx config file, version-controlled and reloaded deliberately, is simpler and has no Docker socket dependency. Mounting the Docker socket read-only still grants the proxy container visibility into the Docker API, which is a real trust boundary decision, not a formality.
nginx-proxy compared with Nginx Proxy Manager
Nginx Proxy Manager is the alternative most searches about this project surface, and the difference is architectural rather than cosmetic. Nginx Proxy Manager is a management interface: you add hosts, certificates and redirects through a UI, and those choices are stored in its own state. nginx-proxy has no such state. Its configuration is derived from container environment variables and the nginx.tmpl template, and it is regenerated from live container state.
That changes the failure mode. With a UI-driven proxy, a host exists because someone created it and it persists until someone deletes it. With nginx-proxy, a host exists because a container with the matching VIRTUAL_HOST is running. Stop that container and the route is gone. For ephemeral or frequently redeployed services this is exactly the behavior you want; for a long-lived host that must stay routable even when its backing container is down, it is not.
Neither approach is strictly better. The trade is between a UI that owns state and a generator that owns none.
Maintenance cost, image tags and licence
The repository is not archived and the last push was on 2026-09-16, so the project is being worked on. Recent releases 1.11.4, 1.11.5 and 1.11.6 all landed in July 2026, which suggests a steady patch cadence rather than long quiet periods. The README also notes that the repository is officially maintained by ZeroSSL.
The upgrade cost is mostly an image tag decision. Three variants exist: a Debian-based image built on nginx:mainline, an Alpine variant with the -alpine suffix, and a -dockergen variant intended for separate-container setups alongside the official nginx image. Pinning a specific version is what the README asks for, and that is also what makes upgrades deliberate rather than accidental.
The project is MIT licensed. That is permissive and places few obligations on how you redistribute or embed it, but the LICENSE file is the authority and nothing here is legal advice. If you redistribute the image, read the licence text and the licences of the bundled nginx and docker-gen components yourself.
Editorial conclusion
Adopt nginx-proxy if your services already live in Docker, you want proxying driven by container metadata, and you are comfortable reading generated nginx configs when something looks wrong. Do not adopt it if you want a browser UI for adding hosts and certificates, or if your upstreams are not containers on a shared Docker network. Before rolling it out, pin an explicit image tag rather than latest, confirm every proxied container shares a network with the proxy, and check whether the repository's docs cover the TLS and virtual-port behavior you need, because the README alone does not.
Frequently asked questions
What is nginx-proxy and how does it relate to nginx?
It is a container that runs nginx together with docker-gen. docker-gen generates reverse proxy configs for nginx and reloads nginx when containers start and stop, so you describe routing with environment variables instead of editing nginx config by hand.
How do I install nginx-proxy with Docker Compose?
The repository ships a docker-compose.yml with an nginx-proxy service that publishes port 80 and mounts /var/run/docker.sock read-only at /tmp/docker.sock, plus a whoami service carrying VIRTUAL_HOST. Note that this file uses the floating nginxproxy/nginx-proxy tag, which the README advises against for production.
How does nginx-proxy decide which container a request goes to?
A container sets the VIRTUAL_HOST environment variable to the hostname it should answer for. With DNS resolving that name to the proxy host, requests for it are routed to the container carrying that value.
Is it safe to mount the Docker socket into nginx-proxy?
The README's example mounts it read-only with :ro, but that still gives the container access to the Docker API. The README does not discuss the trust implications, so treat it as a deliberate decision about what runs on that host.
Which nginx-proxy image tag should I use in production?
The README states that the latest, alpine and dockergen tags point to the latest commit on main and carry no stability promise, and that you should specify the version explicitly. Its own example uses nginxproxy/nginx-proxy:1.11.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nginx-proxy-nginx-proxy)