https://github.com/bitcoin/bitcoin/blob/d72758c3f6c34cd835f5de3efc230964f3b1ea56/src/torcontrol.cpp#L539

If a user is running Tor proxy outside localhost (perhaps on other host inside LAN or in containerised environment) , the automatic creation of onion services ends up creating an onion service listening to hardcored 127.0.0.1 address on the Tor proxy's host instead of the private address of the bitcoind – effectively running an empty onion service listening the Tor proxy's localhost.
if (LookupHost(externalIPAddress, resolved, false)) {
                        LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString());
                        AddLocal(resolved, LOCAL_UPNP);
                    }
                } else {
                    LogPrintf("UPnP: GetExternalIPAddress failed.\n");
                }
            }
        }
        std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
        do {
            r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
            if (r != UPNPCOMMAND_SUCCESS) {
                LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
            } else {
                LogPrintf("UPnP Port Mapping successful.\n");
            }
        } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20)));
        r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
        LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
        freeUPNPDevlist(devlist); devlist = nullptr;
        FreeUPNPUrls(&urls);
    } else {
        LogPrintf("No valid UPnP IGDs found\n");
        freeUPNPDevlist(devlist); devlist = nullptr;
        if (r != 0)
            FreeUPNPUrls(&urls);
    }
}
void StartMapPort()