I'm trying to put a Grimmory container behind Nginx Proxy Manager while keeping its application port off the Docker host and public network interfaces. Previously, publishing port 6060 made the service bind to 0.0.0.0, which exposed it through the server's public IP.
Nginx Proxy Manager is attached to an external Docker bridge network named proxy, with ports 80 and 443 published on the host. Grimmory is attached to both proxy and an internal grimmory_stack network, while MariaDB is attached only to grimmory_stack. Grimmory listens on container port 6060, but I have removed the host port mapping and tried using expose: 6060 instead.
The containers appear to be connected to the proxy network. However, configuring Nginx Proxy Manager with the Grimmory container IP, the container name, localhost:6060, and other hostname variations results in connection failures or 504 errors. What is the correct Docker and Nginx Proxy Manager configuration, and how can I verify whether Grimmory is listening on the right interface inside the container?
3 Answers
Make sure both Compose projects really reference the same external network. The network should be created once, for example with docker network create proxy, and both files should declare it as external: true. Then recreate the containers so their network attachments are updated.
In Nginx Proxy Manager, use the Docker-resolvable name and port, not the host address. You can confirm the name resolution from the proxy container with getent hosts grimmory. If name resolution works but curl times out or refuses the connection, check Grimmory’s bind address and logs. A 504 only indicates that Nginx Proxy Manager received the request but could not get a timely response from the upstream; it does not necessarily mean the upstream hostname is correct.
Your network layout is basically correct. A container does not need a published port to receive traffic from another container. Since Nginx Proxy Manager and Grimmory share the proxy network, create the proxy host with Forward Hostname/IP set to grimmory and Forward Port set to 6060. Do not use localhost, because that means the Nginx Proxy Manager container itself. Also do not rely on the container IP, since it can change; Docker’s internal DNS resolves the service or container name.
The Grimmory service can keep expose: 6060, although expose is only documentation here. The important part is that there is no ports: mapping such as 6060:6060. Only Nginx Proxy Manager needs host mappings for 80 and 443. Keeping MariaDB exclusively on grimmory_stack is also the right isolation approach.
Test the connection from the same network before changing more Nginx settings. For example, run a temporary curl container on proxy, or enter the Nginx Proxy Manager container and try something like curl http://grimmory:6060/api/v1/healthcheck. If that succeeds, Docker networking is working and the Nginx Proxy Manager target should be grimmory:6060. If it fails, inspect Grimmory itself.
Inside the Grimmory container, check the listening socket with ss -lntp or netstat -lntp. The application must listen on 0.0.0.0:6060 or the container’s interface address, not only 127.0.0.1:6060. Listening only on localhost would make it reachable by its own healthcheck but not by Nginx Proxy Manager, which commonly produces a 504.
The healthcheck using localhost only proves that Grimmory can reach itself. It does not prove that another container can connect. A curl from the proxy container is the more useful test here.

That makes sense. I was treating expose as if it created the connection, but the shared network and Docker DNS are what actually matter. I’ll test the service name from inside the Nginx Proxy Manager container.