Setting up Nginx Proxy Manager (npm) on docker
1cd
2mkdir docker-compose/npm; cd npm
3mkdir data
4mkdir letsencrypt
Create the following file as docker-compose.yaml
1services:
2 app:
3 image: 'docker.io/jc21/nginx-proxy-manager:latest'
4 container_name: npm
5 restart: unless-stopped
6 ports:
7 # HTTP port
8 - 80:80
9 # HTTPS Port:
10 - 443:443
11 # Admin UI
12 - 10.0.0.1:81:81
13 environment:
14 DB_SQLITE_FILE: "/data/npm.sqlite"
15 volumes:
16 - ./data:/data
17 - ./letsencrypt:/etc/letsencrypt
18 networks:
19 - seafile_seafile-npm
20
21networks:
22 seafile_seafile-npm:
23 external: true
Then docker-compose up -d
to run the container.
Post install
When logging in for the first time, use credentials
Email: [email protected]
Password: changeme
You’ll need to change the credentials immediately after first login.
Comment: If a reverse proxied service shows a 502 bad gateway error, it is almost always because npm container can not access the ip_addr/name:port of the service being reverse proxied.
Notes
- Binding the admin UI at port
81
to the Wireguard interface10.0.0.1
only. This means that the admin interface is only accessible from a wireguard client. Currently, this is my home Linux Desktop machine. There is no need to expose this UI to the world. - The
seafile_seafile-npm
network needs some explanation. Basically, Nginx Proxy Manager can only access Seafile if- npm and seafile are on the same network: This means, either put all containers that need to be reverse proxied on the same network OR create a dedicated network for each container to share with npm.
- The former method essentially breaks network isolation across containers (all containers can see each other if they are on the same network. If one container gets compromised, potentially other containers get compromised too. The way around this is to use something like TRAFFICJAM (see References) which uses iptables to ensure that only container<>npm connections are allowed.).
- The latter approach leads to the creation of at least one network per container-that-needs-to-be-proxied. That’s .. icky, but the approach taken here, because it is simple. Will revise it if there are many containers and this becomes unwieldy or starts showing performance issues. So we create a network in Seafile’s docker-compose named seafile-npm (more generally, appname-npm) and add npm to this network as shown above. We start the Seafile docker container first so that npm can find the
external: true
seafile_seafile-npm network. If we start npm docker first, it’ll complain that the seafile_seafile-npm network isn’t available. Conversely, if we shut down seafile docker container before npm is shut down (very likely scenario), note that the seafile_seafile-npm network will not be shut down because npm is “using it”. Then, when npm is shut down, it will still not shut down the seafile_seafile-npm network because it is an external network. So to bring down this network we’d need to dodocker-compose down
first in seafile, then in npm, then again in seafile. /shrug. Note further that with this approach, there is no need to expose ports in other containers to the host i.e. no port mapping needs to be defined. NPM can look up other containers using their definedcontainer_name
and connect to the ports inside the container.
- npm has access to the hosts
127.0.0.1
so that it can proxy a request to127.0.0.1:port_num
.-
One way to do this is by selecting network_mode to
host
i.e. the npm container runs on the hosts network stack. Then, npm will have access to127.0.0.1:port_num
. However, it means that no port-bindings e.g.80:80
are possible, since that makes no sense. In that case, the npm admin UI at port 81 gets exposed to ALL host interfaces and the only thing that would prevent the world from accessing the admin interface would be a firewall rule (either on the host or through the VPS e.g. Hetzner firewall). I haven’t yet figured out how npm’s port 81 could be bound only to the Wireguard interface, when npm docker is run with host networking. -
The other way to do this would be to use
extra_hosts: - "host.docker.internal:host-gateway"
in the
docker-compose.yaml
file, which presumably gives the npm container access tolocalhost
on the host i.e.host.docker.internal:port_num
would be127.0.0.1:port_num
on the host. But I never got this to work. It seems that npm would not resolve host.docker.internal to localhost.- An alternative to the
host-gateway
would be to manually set the ip address of thedocker0
interface on the host, typically172.17.0.1
, in place ofhost-gateway
in the above. I tried that, but it didn’t work either.
- An alternative to the
-
- npm and seafile are on the same network: This means, either put all containers that need to be reverse proxied on the same network OR create a dedicated network for each container to share with npm.
References
- https://github.com/kaysond/trafficjam <– A firewall that prevents containers from talking to each other when they are on the same docker network
- https://nginxproxymanager.com/setup/#running-the-app <– Official setup guide