Good framing for putting nginx on the same compose network as the apps. Two ops habits that save hours: resolve upstreams by service name (not published host ports), and make sure nginx reloads or recreates when backend tasks get new IPs — a long-lived nginx worker can keep stale resolved addresses after a recreate unless you use variables with resolver or restart the proxy on deploy.
Expose only 80/443 on the nginx service; leave app ports internal. For TLS termination at nginx I mount the certs read-only and keep a healthcheck that curls the public name after reload, not just the container status. When something 502s after a redeploy I check docker network connectivity from the nginx container to the service name first, then the proxy_pass URI and trailing-slash rules — most “Docker broke my proxy” tickets are still upstream name or path joining, not the image itself.