How do I install NGINX-Proxy-Manager on Portainer?
I want to install NGINX-Proxy-Manager on my Linode using Portainer in order to manage reverse proxies and SSL certificates, how do I do this?
✓ Best Answer
This is a continuation of a previous Community Questions post regarding the setup and use of a Portainer/NGINX-Proxy-Manager stack. Although there will be some recap, this post assumes Portainer is already set up, and this guide primarily focuses on the setup and use of NGINX-Proxy-Manager.
For more information about how to deploy Portainer, be sure to refer back to that post:
In subsequent commands, $NETWORK_NAME will be the Portainer network previously created during the Portainer setup.
1. Deploy NGINX-Proxy-Manager through Portainer:
While logged into Portainer, navigate to your Stacks page and select Add stack to create a new deployment for NGINX-Proxy-Manager (NPM). The full NGINX-Proxy-Manager Docker compose .yml file is located here, but the basic configuration shown below has some changes made to it:
version: '3' services: app: image: 'jc21/nginx-proxy-manager:latest' restart: always ports: - '80:80' - '81:81' - '443:443' volumes: - nginx-proxy-manager-data:/data - letsencrypt-certificates:/etc/letsencrypt networks: - $NETWORK_NAME networks: $NETWORK_NAME: external: true volumes: nginx-proxy-manager-data: letsencrypt-certificates:
If you already understand Docker Compose syntax, feel free to skip ahead, but in short this deployment stack does the following:
- Launch a container of the latest image of NGINX-Proxy-Manager,
- That container will always start automatically unless the stack is stopped
- Ports 80 & 443 will be opened for standard HTTP/HTTPS traffic, and port 81 will be opened for the NPM management UI
- Volumes will be created and mounted to store the app configuration and SSL certificates
- The container will be hosted on the Portainer/NGINX-Proxy-Manager $NETWORK created earlier
Environment variables can be defined and created beneath the Web Editor box, but this specific deployment doesn’t include any passwords or information that would benefit from using that feature.
Once you click deploy, Portainer will interpret the compose file to pull the NGINX-Proxy-Manager image defined above and will alert you to the deployment’s success or provide error messages upon its failure.
2. Create Domain Entry and Login to NGINX-Proxy-Manager:
Since the primary benefit of using NGINX-Proxy-Manager is to easily create proxies and SSL certs, you should now create new A Records for Portainer and NGINX-Proxy-Manager.
If you use a service like CloudFlare that can proxy/obfuscate your records’ IP addresses, you will need to temporarily disable the obfuscation for the next steps to work. Similarly, if you had previously configured UFW or Linode’s Cloud Firewall, be sure to allow connections on port 81 (and 9000 should already be open from configuring Portainer).
Navigate to your NGINX-Proxy-Manager web UI located at http://your.domain.tld:81 (or http://$IP_ADDRESS:81) and login using the default credentials
- User: email@example.com
- Password: changeme
You will then be prompted to change the admin username and make a new, more secure password for that new user.
3. Create Reverse Proxies and SSL Certificates:
Now, the magic begins. Navigate from the Dashboard to the Proxy Hosts page and start by adding a new proxy host for either NGINX-Proxy-Manager since that’s where the work is currently being performed.
NGINX-Proxy-Manager allows you to simultaneously create the Proxy and the SSL certificate, so use the following information for the Details tab:
- Domain Names: $sub.domain.tld - This is whatever A Record DNS name you created for NGINX-Proxy-Manager
- Scheme: Leave as HTTP
- Forward Hostname/IP: Since Portainer and NGINX-Proxy-Manager are on the same Docker network, you can use the container name displayed in Portainer. For me, this is
- Forward Port: 81 (Web UI Port for NGINX-Proxy-Manager to which you forward requests made to your domain name)
- Options: Cache Assets, Block Common Exploits, and Websocket Support can be enabled
Use the following information for the SSL tab:
- SSL Certificate: Request a new SSL Certificate
- Options: Select Force SSL, HTTP/2 Support, HSTS Enabled, and HSTS Subdomains for added security
- Enter your SOA email address so Let’s Encrypt alerts you when your SSL certificate is about to expire. NGINX-Proxy-Manager automatically attempts to renew your certificates, but it's still nice to know if your certificate is about to expire in-case the renewal fails.
- Agree to the Let’s Encrypt ToS and click Save
After you save, Let's Encrypt will do its thing and you will see a brand new Proxy Host that will navigate you back to your NGINX-Proxy-Manager login menu via HTTPS. You can now repeat those steps for Portainer, substituting:
- A different, unique Domain Name/A Record for Portainer
- The Portainer container name (in this instance portainer) for the Forward Hostname/IP
- Forward Port: 9000
Connecting to your Apps & Moving Forward:
Now that the SSL certificates have been created, your A Record IP Address can resume being proxied/obfuscated if you use CloudFlare or another similar service. Additionally, I personally leave ports 81 and 9000 unblocked by my Firewall in case I need to access the admin pages of Portainer and/or NGINX-Proxy-Manager by IP (if your SSL certificate or domain name expires, for example). You can however make your rules as strict as you’d like as long as ports 80/443 are exposed.
Alternatively, you can use the actual containers’ Docker IPs when designating the Forward Hostname/IP, but this can create issues in the future if you ever need to reboot your Linode or a container crashes. Those Docker IPs are likely to change which will then break the routing for the SSL certificate and (usually) return a 500 error. This is why we created the $NETWORK_NAME for Portainer.
Any other deployment that you create on Portainer can similarly use this NGINX-Proxy-Manager deployment template for easy SSL/Proxy creation.