9.1 KiB
VPN transit via VM 213 and Vultr
Date: 2026-09-27 to 2026-09-28
Goal
Provide access from Russian residential/mobile networks to services restricted by Russian network filtering, while retaining the existing foreign egress on Vultr.
Verified network facts
- Test host
3060:46.39.251.163, connection from Khimki / Iskratelecom. - Transit VM
213:5.172.178.213, public egress observed as5.172.178.65; hosted in NUBES data centre. - Vultr addresses: primary
95.179.252.111; secondary104.238.177.67. 3060 -> 213: ICMP approximately 3 ms, 0% loss.213 -> Vultr: ICMP approximately 34 ms, 0% loss; HTTPS response returned in about 0.07-0.11 s.- Direct
213 -> Vultrtest file transfer: 10 MiB in 1.59 s, about 6.27 MiB/s / 50.2 Mbit/s. - Direct
3060 -> Vultrtest file transfer timed out / was throttled. - Direct
213 -> OVH proof endpoint: 10 MiB in 1.18 s, about 8.5 MiB/s. - Direct access from
213to YouTube and Telegram failed withHTTP=000and timeout/SSL errors, while OVH and Google returned HTTP 200. Therefore a foreign egress remains required for those services.
Persistent changes on VM 213
- Created backup:
/etc/nginx/sites-available/check.kube5s.ru.bak_vpn
- Modified:
/etc/nginx/sites-available/check.kube5s.ru
- Added an Nginx
/wsreverse-proxy location with:- upstream
https://95.179.252.111:443 - SNI
vipien.kube5s.ru - upstream Host header
vipien.kube5s.ru - WebSocket upgrade headers
- 3600-second proxy timeouts
- upstream
- Ran
nginx -tsuccessfully and reloaded Nginx. - Existing unrelated Nginx warnings about duplicate
contracts.kube5s.ruserver names remained.
Persistent/previously existing changes on Vultr
The following configuration was read or used during validation:
/etc/nginx/conf.d/vipien.conf: TLS/WebSocket endpoint forvipien.kube5s.ru./etc/v2ray-agent/xray/conf/08_VLESS_ws_inbound.json: VLESS WebSocket inbound on127.0.0.1:10086, path/ws./etc/systemd/system/hysteria-server.service: Hysteria service was stopped and disabled; it was not changed in this work.- Xray service was confirmed active.
- Nginx service was confirmed active.
- Cloudflared tunnel configuration was inspected earlier, but it is not used by the final working route.
- A temporary 10 MiB test file was created on Vultr and removed after testing.
Temporary files on test VM 3060
The following temporary client files were created under /tmp/xray-test/ for validation and are not repository files:
client-cf.jsonclient-213.jsonclient-directip.json- temporary log/test artifacts where applicable
The files contained test Xray client configurations. They were used only to verify the route from 3060; no permanent system service was installed there.
Final tested route
client in Russia -> 5.172.178.213:443 -> Nginx WebSocket proxy -> 95.179.252.111:443 -> Xray -> Internet
Final test from 3060 through the route:
- observed outbound IP:
95.179.252.111 - 10 MiB OVH download: 1.76-1.91 s
- measured speed: approximately 5.5-6.0 MiB/s
Final client parameters
- Address:
5.172.178.213 - Port:
443 - UUID: existing UUID used by the Vultr Xray inbound
- TLS SNI:
check.kube5s.ru - WebSocket path:
/ws - WebSocket Host:
vipien.kube5s.ru
The final direct-IP test used Xray 26.3.27. The client-side allowInsecure option was not used because this Xray version reports that the option was removed.
Secondary Vultr IP
Before removal, the Nginx upstream on VM 213 was switched from 104.238.177.67 to 95.179.252.111. A post-switch end-to-end test succeeded, with outbound IP 95.179.252.111 and approximately 6.0 MiB/s.
No Vultr IP deletion was performed in this work. The secondary address was only confirmed as no longer referenced by the transit configuration.
Scope audit
- No repository source/configuration files were edited before this record.
git statuswas clean before this documentation file was created.- This documentation file is the only workspace file created by the current documentation action.
- Server-side files were changed on VM 213 and earlier on Vultr; temporary test files were also created on VM 3060.
- No commit was created for this record.
Important limitations
The measurements prove the route worked at test time. They do not guarantee permanent availability: NUBES, Vultr, upstream providers, or network filtering policy can change independently.
Later the same day: optimisation attempt and its outcome
Automation created
A reusable, idempotent tool was created outside this repository:
/home/naeel/nubes/HowTo/vpn-transit/vpn-setup.sh check | apply | verify | passthrough | verify-passthrough | client-config | rollback
/home/naeel/nubes/HowTo/vpn-transit/client-config.json generated client config (chmod 600, contains UUID)
/home/naeel/nubes/HowTo/vpn-transit/README.md description, measurements, rollback
/home/naeel/nubes/HowTo/howto-vpn-transit-213-vultr-2026-09-28.md full report
Every change is preceded by a timestamped backup and followed by a config test (nginx -t, xray run -test) with automatic rollback on failure.
Changes applied
| Host | File | Change | Backup |
|---|---|---|---|
| 213 | /etc/nginx/sites-available/check.kube5s.ru |
proxy_buffering off; added inside location /ws, marked # vpn-transit: proxy_buffering off |
check.kube5s.ru.bak.1790601681 |
| Vultr | /etc/v2ray-agent/xray/conf/00_log.json |
loglevel: debug → warning (log had grown to 76 MB), service restarted |
00_log.json.bak.1790601723 |
| 213 | /usr/local/sbin/vpn-transit-dnat.sh, /etc/systemd/system/vpn-transit-dnat.service |
DNAT 213:8443 → 95.179.252.111:443 plus FORWARD rules, enabled at boot |
none (rules tagged vpn-transit) |
Measurements after the changes
- Outbound IP:
95.179.252.111 - Throughput:
5.6–7.3 MiB/s(10 MiB in 1.4–1.9 s) - Per-connection latency:
0.23–0.37 s - WebSocket upgrade success rate on 213:
14569 / 14573(99.97%), oneupstream timed outerror
Hypothesis that was disproved: mux
verify compared the tunnel with and without "mux": {"enabled": true, "concurrency": 8}:
| Mode | 10 MiB download | Connection behaviour |
|---|---|---|
| without mux | 7.32 MiB/s in 1.43 s | stable |
| with mux | 0 B/s, failed | after 4 requests connections hang for 15 s |
Conclusion: mux is harmful in the VLESS + WebSocket behind nginx combination. It is excluded from the client config. The test remains in the script for re-checking on future Xray versions.
Optimisation that could not be delivered: removing the second TLS layer
The intended speed fix was to drop one TLS handshake (client → 213, then 213 → Vultr) by forwarding TCP straight through to Vultr.
ngx_stream_module.sois absent on 213, so nginx cannot do SNI-based passthrough without installinglibnginx-mod-stream.- Kernel-level DNAT on port 8443 was installed instead, but does not work: from outside, port 8443 returns
Connection refusedand the DNAT counter on 213 stays at 0 packets — traffic never reaches the machine. - Cause: the provider firewall in front of 213 exposes only ports 80 and 443. Measured from
3060:3001, 8080, 8443, 8766, 8767, 8888, 18080, 40229are closed. - Therefore the second TLS layer can only be removed after the provider opens an additional port. The rules are already installed and would start working immediately once that happens.
Errors made during this work
- Recommended
muxbefore measuring it. The recommendation was given as the main fix and was later disproved by measurement. Correct order: measure first, recommend after. - Changed server configuration before measuring the benefit.
proxy_buffering offhas no effect on a WebSocket connection after the101 Switching Protocolsupgrade, andloglevelaffects only log size. Neither change improves speed, so from the user's point of view nothing changed. - Changed the client config to port 8443 before verifying the port was reachable from outside. The config was regenerated back to port 443 immediately.
Net result for the user
Nothing changed for the client: address 5.172.178.213, port 443, SNI check.kube5s.ru, path /ws and the UUID are unchanged, and the previously used link still works. No client-side reconfiguration is required.
The only actionable finding is client-side: the Xray log on Vultr contained 331 connect: connection refused to 127.0.0.1:45987, i.e. the client requested a loopback address, plus Telegram advertises AAAA records while the tunnel is IPv4-only. The generated client-config.json addresses both (remote DNS, queryStrategy: UseIPv4), but the device itself was not modified.
Separately: 10170 reset by peer entries to 157.240.0.13 (Meta infrastructure) are blocking by those sites, unrelated to the transit.
Scope audit (this action)
- Repository files changed: this document only.
git statusalso showed unrelated pre-existing changes (DEV_STAND/FullPipe/shturval.tfdeletion,TMP/*files) that were not touched or committed. - Server-side files changed: as listed in the table above.
- Temporary test files on 3060:
/tmp/xray-test/*(no permanent service installed).