Your client is set to UDP, the Nginx server has a location block pointing at the VPN port, and nothing arrives. That is expected: an HTTP location cannot carry a UDP datagram, however the front end is labelled. Nginx needs a stream listener for this, and once you move the configuration there, a second set of details decides whether a session survives.

The proxy layer comes before the address

Nginx's stream module proxies TCP and UDP without parsing HTTP. Its HTTP module expects a request line, headers, and a response, none of which a native OpenVPN datagram contains. Choosing the layer is therefore the first decision, and it is independent of whether the listener is IPv4 or IPv6.

A configuration that appears to work with TCP OpenVPN says nothing about the UDP case. The transport is part of the protocol here, not a detail of the deployment.

The listener and the backend

nginx -V 2>&1 | tr ' ' '\n' | grep stream

stream {
    server {
        listen [::]:1194 udp reuseport;
        proxy_pass [2001:db8::20]:1194;
    }
}

Confirm the build includes the stream module before editing anything; a distribution package may split it into a separate module, and a missing module produces a configuration error rather than a tunnel. Put the block in the main configuration context, not inside http, and replace the documentation address with the real backend.

Both routes and both firewalls have to allow the datagrams: the path to the listener and the path from the proxy to the backend. An IPv6 listener also does not create an IPv4 path, so if some clients still use IPv4, that listener is a separate entry with its own testing. Check the backend's own firewall from the proxy's address, not from your laptop.

Session timeouts are not request timeouts

The stream proxy keeps state for a UDP session and discards it according to its timeout and response settings. A short timeout copied from a DNS relay is a poor fit for a VPN that expects to exchange datagrams for hours, and the failure appears as a connection that dies quietly rather than one that refuses to start.

Read the deployment's effective proxy_timeout and any response-count setting, then compare them with the keepalive behaviour the VPN actually uses. Two numbers written in different files by different people is the normal state of a working system. Two numbers that were never compared is the normal state of an intermittent one. Write both into the service record so the comparison happens once, in daylight.

What the relay changes, and what it does not

The stream proxy does not terminate OpenVPN's TLS control channel or convert it into HTTPS. The backend remains the VPN service, and the certificate relationship stays between the client and that service. Preserving the intended backend identity is still the client's and server's job.

Ordinary proxying also changes the source address the backend observes. Logs will show the proxy, and any IP-based access rule on the VPN server will evaluate that address rather than the client's. Transparent source preservation is a separate routing and privilege design, not something that follows from adding udp to a listener. Plan for it before a policy that depends on the client address is written.

Verify from the client, not from the port

A local check that the UDP port is open proves the listener exists. It does not prove that a client can authenticate through it or keep exchanging traffic. Complete a real connection from an approved client, then confirm the session stays up long enough to matter.

When someone reports that Nginx cannot do UDP because a simple relay tool worked instead, the two facts are unrelated. The tool proved that the backend answers; it said nothing about what the front end supports. Both can be true, and only one of them is a configuration for this deployment.

འབྲེལ་བའི་རྩོམ་ཡིག