Inside a container behind a corporate VPN, DNS resolves, the login page loads, a health endpoint answers, and then an HTTPS request hangs until it times out. Rebuilding the image and changing proxy variables changes nothing, because the failing part is not in the image. The client authenticates over one transport and moves bulk data over another, and only the second one is broken.

Two transports, one confusing symptom

A TLS-based corporate client usually authenticates over an ordinary TLS connection, then negotiates a second UDP-based data channel for the session. When that second leg works, throughput and latency improve. When it is filtered or cannot fit through the path, everything before bulk transfer succeeds and the transfer itself stops, which is exactly the pattern above.

A container does not change that negotiation; it changes the path the outer packets take. The host translates the container flow, so the UDP datagrams leave from a different source port than the client local socket, and they return through a translation entry that expires on a much shorter timer than an established TCP flow. If the tunnel works on the host and fails in the container, compare those two flows before blaming the application. That comparison settles more than any image rebuild.

Decide by experiment, not by intuition

The cheapest experiment disables the UDP data channel and repeats the request. Use the command-line client rather than the desktop wrapper, so the flag is actually applied: openconnect vpn.example.net --user=you --interface=tun0 --no-dtls. If the request that hung now completes, the DTLS leg was the problem and the rest was noise.

Confirm the result in both directions from the same container, one bulk request with the flag and one without. A behaviour change that follows the flag is evidence; a page that eventually loads is not.

If the flag fixes it, decide why

Three conditions produce the same symptom, and they call for different responses. The path may filter UDP entirely, which makes the flag the correct permanent answer for that network. The path may allow UDP but drop large datagrams, which points at MTU rather than filtering. Or the path may be lossy enough that the UDP leg performs worse than TCP, which is a capacity decision rather than a reachability one.

Distinguish them before you document a workaround. Filtering survives every packet size, MTU problems appear only above a threshold, and loss shows up as retransmission and variable delay rather than a clean hang. Each test narrows the cause without changing the client configuration.

Check packet size before you keep the workaround

If size is the cause, the tunnel interface accepts packets the path cannot carry, and the problem returns on any transport as soon as payloads grow. Compare the interfaces: ip link show tun0 on the host, then docker network inspect bridge to read the MTU recorded in the network options. A container bridge still at 1500 bytes under a tunnel that carries less is the usual finding.

Probe upward from a known-good size with a do-not-fragment test inside the tunnel, then lower the container network MTU or clamp TCP MSS on the tunnel. Either correction often makes the UDP path usable again and removes the need for the flag, which is the better outcome when filtering is not the cause.

Decide what you are willing to trade

With the data channel disabled, the tunnel carries its payload inside a TCP connection. On a clean path that is merely slower; on a lossy one, a single lost segment delays everything behind it and the outer retransmission competes with the inner one. That trade suits interactive development and small API calls, and fits poorly with bulk transfers or long sessions over mobile links.

Whichever option you choose, record which transport the tunnel ended up using and why. A container that silently depends on a flag someone added months ago is the next outage, and the person debugging it will not know whether the flag was a workaround for filtering or a leftover from an MTU experiment.

ተዛማጅ ጽሑፎች