Your IPv6 VPN client reaches the workload, the workload answers into the tunnel, and the answer never arrives. The instinct is to blame the firewall or the tunnel, and neither is usually at fault. An address handed out by OpenVPN is an inner network that exists only inside the tunnel; Google Cloud has no idea that fd42:90::/64 is supposed to live behind one particular VM until you tell it.

Two separate systems have to learn that fact, and only one of them is OpenVPN. The VPC needs a route before it can send anything back, and the VM must be permitted to forward what arrives. Get either half wrong and the symptom looks identical.

The pool is an inner network, not a subnet

OpenVPN can hand out IPv6 addresses from a range you choose, and that range is a fiction the two tunnel endpoints agree on. It is not a Google Cloud subnet, it does not appear in the VPC subnet list, and the VM's network interface does not acquire those addresses. A workload that replies to a client therefore sends a packet to an address the fabric considers unroutable.

Keep the pool out of every range the VPC already uses. Keep it out of the ranges behind your other tunnels as well. Overlap turns a clean routing failure into an intermittent one that only shows up under load.

Unique local addressing such as fd42:90::/64 is a reasonable choice for this inner network. The prefix also serves as a reminder that the design is private by construction. Nothing about a ULA pool makes it a legal source on the public internet.

Give the VPC a destination and a next hop

The reply needs a VPC route whose destination is the pool and whose next hop is the VPN VM. Current static-route documentation covers IPv6 destination ranges and lists which next-hop types are eligible, so verify the supported form for your topology instead of copying an older IPv4 example.

A next-hop address has to be one that is actually assigned to the instance's cloud interface. The VM's inner tunnel address is not that address. Using it anyway produces a route that installs cleanly and never matches a packet. Read the interface list before you write the route.

Route precedence matters as much as the entry itself. Subnet routes are more specific than the custom routes you add, so a pool carved out of an existing subnet range will lose to the subnet route no matter how the custom route is written. Choose a distinct pool, then confirm the route shows the instance as next hop and the correct destination prefix:

gcloud compute routes create vpn6-return \
  --network=vpn-vpc \
  --destination-range=fd42:90::/64 \
  --next-hop-instance=openvpn-vm \
  --next-hop-instance-zone=europe-west1-b

Forwarding has to be enabled twice

A route delivers the packet to the instance. It does not make the instance willing to forward something that is not addressed to it, and that permission exists at two layers.

The cloud side needs the instance forwarding capability enabled, which is the setting that lets a VM receive traffic for addresses it does not own and pass it on. The guest side needs its own IPv6 forwarding, because the kernel applies a separate decision after the packet arrives. Both have to be on before a single reply crosses the tunnel.

sysctl -w net.ipv6.conf.all.forwarding=1

Firewall rules then have to admit the inner source range in both directions. Google Cloud evaluates the decapsulated packet, so a rule that only covers the VPC subnet drops the pool traffic after the tunnel has already succeeded. Add the pool as a source range on the ingress rule, and check that no custom egress policy blocks the reply. The implied allow rule covers the default case, a hand-written egress rule does not.

Tell OpenVPN who owns what

OpenVPN forwards to a client when it knows that client owns the destination. Client addresses from the pool are handled by the server directive that defines the pool, but a subnet sitting behind a client is a different arrangement and needs an explicit ownership entry for that client.

Confusing the two produces a tunnel where pings to the server work and nothing behind it does. Decide which case you are building, then write the matching entry rather than adding a second push line and hoping.

Keep internet egress out of this design

Private access and internet access are separate plans. A ULA pool can reach VPC workloads over the tunnel once routing and forwarding agree, and that same pool has no path to the public internet through the VPC, because the cloud gateway will not accept an arbitrary tunnel source as its own.

If clients also need internet egress, plan a supported addressing and routing design for it, keep it independent of the private pool, and test the two paths separately. One working path tells you nothing about the other, and a design that conflates them fails in a way that looks like a broken tunnel.

Artaigilean co-cheangailte