Self-hosted service
evertramos/nginx-proxy-automation avatar
evertramos/nginx-proxy-automation

nginx-proxy-automation: a shell-driven nginx reverse proxy with Let's Encrypt certificates

Automated docker nginx proxy integrated with letsencrypt.

2,867 stars640 forksShellMIT

At a glance

What is it?
evertramos/nginx-proxy-automation wires nginx, docker-gen and the acme-companion into a compose stack that reads VIRTUAL_HOST labels and issues certificates on its own. It suits single-host Docker deployments that want a reverse proxy without a control panel.
Who is it for?
Adopt it if you run one or a few Docker hosts and want nginx plus Let's Encrypt driven by container labels rather than a web UI. Skip it if you need a dashboard, multi-host clustering, or you cannot accept a git clone with --recurse-submodules.
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 129 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem nginx-proxy-automation addresses

Running a reverse proxy by hand means editing server blocks every time a service moves, then remembering to renew certificates before they expire. nginx-proxy-automation packages that work into a Docker Compose stack. You start a container, give it a VIRTUAL_HOST environment variable, and the proxy picks it up. Certificates come from Let's Encrypt through the acme-companion image.

The target reader is someone running Docker on a single host who wants nginx as the front door but does not want to hand-write vhost files. The repository is Shell, MIT licensed, and its topics list certificate, docker, docker-compose, letsencrypt, nginx and nginx-proxy. There is no control panel here. Configuration lives in a .env file and in container labels, which is a deliberate trade-off: less to click, more to type.

How the docker-gen, nginx and acme-companion containers fit together

The stack has three cooperating services in docker-compose.yml. The first is nginx-proxy-automation-web, built from nginx:${NGINX_IMAGE_VERSION:-stable-alpine}, which publishes ports 80 and 443 and carries the label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy set to "true". That label is what tells the certificate companion which container is the proxy.

The second is nginx-proxy-automation-gen, running nginxproxy/docker-gen. Its command watches the Docker socket, renders /etc/docker-gen/templates/nginx.tmpl into /etc/nginx/conf.d/default.conf, and sends a SIGHUP to the web container with -notify-sighup. The polling interval is given as -wait 5s:30s, so changes are picked up on a short cycle and a longer one.

The third service is the Let's Encrypt companion, named acme-companion in .env.sample. It reads the same labels and writes certificates into the shared certs directory, which the web container mounts read-only at /etc/nginx/certs. All three share conf.d, vhost.d, html, certs and htpasswd volumes under ${NGINX_FILES_PATH:-./data}. The data flow is one direction: labels in, nginx configuration and certificates out.

Installing it and serving a first domain

The README is explicit that the clone must include submodules, because the project uses evertramos/basescript as a git submodule. Skipping that flag leaves the bin/ scripts without their dependency.

bash
git clone --recurse-submodules https://github.com/evertramos/nginx-proxy-automation.git proxy

Then run the bootstrap script from the bin directory, passing your email address so Let's Encrypt can register the account. The README example uses --yes and --skip-docker-image-check.

bash
cd proxy/bin && ./fresh-start.sh --yes --skip-docker-image-check -e your_email@domain

To confirm the proxy works, start a throwaway web container on the proxy network with a VIRTUAL_HOST value. Use a domain whose DNS already resolves to this host, or the certificate step will fail.

bash
docker run -dit -e VIRTUAL_HOST=your.domain.com --network=proxy --name test-web httpd:alpine

The repository also ships a helper, so the equivalent check is a single call with the domain as an argument.

bash
./test.sh your.domain.com

What you should see is the test page served over HTTPS, with a certificate issued for the domain you passed. If the domain does not resolve to the host, or ports 80 and 443 are already taken, this is where it breaks.

Where the label-driven model gets in the way

Everything hinges on the proxy network. The .env.sample comments state that the network name is used to forward requests to the correct containers and that every container must join it, otherwise the proxy breaks. A service started on the default bridge network is invisible to docker-gen no matter how correct its VIRTUAL_HOST label is.

The second constraint is binding. The compose file publishes ports on ${IPv4:-0.0.0.0}, and the sample file notes that 0.0.0.0 works but recommends updating the variable to the real external interface. IPv6 is present as a commented block, so dual-stack setups need edits rather than a flag.

Third, the certificate flow assumes reachability. Let's Encrypt has to validate the domain over HTTP or HTTPS from the public internet, so this is the wrong tool for a proxy sitting behind another proxy, for internal-only hostnames, or for hosts where you cannot open port 80. The README does not document a DNS-01 path, and it does not document rollback if fresh-start.sh fails partway through.

nginx-proxy-automation against jwilder/nginx-proxy and Nginx Proxy Manager

The closest relative is jwilder/nginx-proxy, which this project builds on: the nginx.tmpl template and the com.github.jrcs.letsencrypt_nginx_proxy_companion label both come from that lineage. The difference is packaging. jwilder/nginx-proxy gives you the proxy container and expects you to add a companion for certificates and to manage the compose file yourself. Here the compose file, the .env.sample, the fresh-start.sh bootstrap and the test.sh helper are all in the repository, and basescript is pulled in as a submodule to support the scripts.

Nginx Proxy Manager takes the opposite route. It ships a web interface where you add proxy hosts and request certificates by clicking, and it stores state in its own database. nginx-proxy-automation has no UI and no database: the source of truth is the labels on your containers and the files under the data directory. If you want to see and edit routes in a browser, this is not that project. If you want routes to appear automatically when a container starts, the label model does that without an extra step.

Maintenance, upgrades and the MIT licence

The last push to the default branch was on 2026-05-24, and the repository is not archived. The most recent release is v2.0, published on 2025-04-19; before that the tags jump back to v0.6 in September 2021, so the release cadence is uneven and you should read the repository rather than assume a schedule.

Upgrade cost sits in three places. The image versions are pinned through environment variables such as NGINX_IMAGE_VERSION, DOCKER_GEN_IMAGE_VERSION and NGINX_PROXY_COMPANION_IMAGE_VERSION, so bumping them is an edit to .env rather than to the compose file. The nginx.tmpl template is a file in the repository, which means a new upstream template has to be merged rather than pulled automatically. And the basescript submodule has to be updated with the usual submodule commands, since a plain git pull will not move it.

The licence is MIT, which permits commercial use and modification provided the copyright notice and permission notice are kept. That is a summary of the identifier given in the repository, not legal advice; check the LICENSE file for the exact terms.

Editorial conclusion

Adopt it if you run one or a few Docker hosts and want nginx plus Let's Encrypt driven by container labels rather than a web UI. Skip it if you need a dashboard, multi-host clustering, or you cannot accept a git clone with --recurse-submodules. Before committing, verify that your DNS points at the host, that ports 80 and 443 are free, and that IPv4 in .env.sample matches the interface you want to bind.

Frequently asked questions

Can nginx-proxy-automation run nginx as a reverse proxy for Docker containers?

Yes. The compose stack runs nginx as the front end, and docker-gen renders nginx.tmpl into default.conf based on the labels of containers on the proxy network. A container with VIRTUAL_HOST set is routed without editing nginx configuration by hand.

What are the disadvantages of using nginx-proxy-automation?

Every routed container must join the proxy network or the proxy breaks, according to the .env.sample comments. There is no web interface, IPv6 requires uncommenting blocks in docker-compose.yml, and the README does not document rollback for a failed fresh-start.sh run.

How do I install nginx-proxy-automation?

Clone the repository with --recurse-submodules because basescript is a submodule, then run ./fresh-start.sh from the bin folder with --yes, --skip-docker-image-check and your email address. The README gives the exact commands for both steps.

Does nginx-proxy-automation obtain Let's Encrypt certificates automatically?

The stack includes a Let's Encrypt companion service, named acme-companion in .env.sample, and the web container carries the com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy label that marks it as the proxy. Certificates are written to the shared certs directory, which nginx mounts read-only.

Which network must containers join to be proxied by nginx-proxy-automation?

The proxy network. The .env.sample comments state that the network name is used to forward requests to the correct containers and that containers must be added to it, otherwise the proxy breaks. The README test command uses --network=proxy.

Official sources

  1. evertramos/nginx-proxy-automation on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/evertramos-nginx-proxy-automation.svg)](https://hysenlabs.com/projects/evertramos-nginx-proxy-automation)