A public Enshrouded dedicated server in Docker, tunneled to hide the home IP, with a split-horizon DNS setup so LAN players connect directly instead of round-tripping through the internet. It works great. Then a container that has been perfectly healthy the entire time turns out to have never had its port open to the host, and only one of the two ways in ever noticed.
Enshrouded's dedicated server runs happily under Docker on a plain Linux container. No Windows requirement,
no special hardware. The community sknnr/enshrouded-dedicated-server image handles the Wine
layer needed to run enshrouded_server.exe on Linux, listening on a single UDP port, 15637 by
default.
To expose it publicly without leaking a home IP, an frpc sidecar container runs alongside it on
its own Docker bridge network, tunneling out to a relay server. Both containers share that private bridge, so
frpc reaches the game process over Docker's own internal DNS at
enshrouded:15637 rather than dialing 127.0.0.1. Worth knowing. That is exactly what
let the bug below hide as long as it did.
Public traffic routing through a tunnel is fine. There is no reason a player on the same LAN as the server should take that detour. A local DNS override on the router resolves the server's public hostname straight to its real LAN IP for anyone inside the network, while everyone outside still routes through the tunnel. Same hostname, two different paths depending on where the client is.
This is a real, worthwhile optimization with a sharp edge, covered below. The LAN path and the tunnel path are genuinely different network routes. One working is no proof the other does.
docker inspect on the game container showed a bare {} for its published ports. The
compose file put it on a private bridge network with the frpc sidecar and never published its
port to the host. A host-level port scan confirmed nothing was listening there.
Here is the catch. frpc could still reach the game container fine, because they share the same
private Docker network and talk over Docker's internal DNS. The public tunnel path kept working the entire
time. Only the LAN-direct path was broken, and that one needs the port published to the host's own network
stack. A player connecting from outside the LAN would never have noticed.
That is also why it looks like a sudden break with nothing changed. A client with a cached DNS answer pointing at the public tunnel address keeps working until that cache expires. Then a fresh lookup resolves to the LAN IP via the split-horizon override and hits a port that was never open. From the player's side that looks exactly like a random outage.
Publish the port explicitly in the compose file. Being reachable over the tunnel's internal Docker network is a completely different claim from being reachable on the host's network stack:
services:
enshrouded:
# ...
ports:
- "15637:15637/udp"
networks:
- gamenet
Recreate the container with docker compose up -d. Then confirm a real listener exists at the
host level, not inside the container's own namespace. Those are not the same check:
# from inside the LXC or host itself. Checks the host's own network stack
ss -ulnp | grep 15637
# from inside the container. Checks whether the process is listening at all,
# useful on minimal images with no ss or netstat installed
docker exec enshrouded cat /proc/net/udp
Enshrouded's server browser lists a discoverable dedicated server automatically, at the top, with no manual "add server" step. Type the server's name into the browser's own search box, though, and you reliably get zero results. That is expected behaviour, not a broken tunnel or DNS entry. The server is already visible without searching. The search field just does not match against it. Connecting straight from the list works fine.
Enshrouded has no official mod support. This server runs the game under Wine inside Docker rather than
natively, and most mod loaders require setting a WINEDLLOVERRIDES environment variable on the
actual Wine process invocation. That is unconfirmed against this image's entrypoint. Other people running the
same base image have hit the identical wall with no documented fix. Verify it against a disposable test
instance, with a fresh backup on hand, before you try it on a server real players are connected to.