All-in-one VK-call tunnel + Hysteria2 for MikroTik ROS v7.24+ containers (x86_64 / arm64 / armv7).
177
All-in-one VK-call tunnel for MikroTik RouterOS containers (x86_64 / arm64 / armv7).
Tunnels traffic through a VK video call's TURN relay — disguised as call media (SRTP) — and terminates it on a Hysteria2 backend bundled in the same container (sing-box). Pairs with the WireTurn Android client. From inside Russia the transport looks like an ordinary VK call; the exit is your VPS.
Built from source. Config-file driven: drop a *.json in the mount and the container brings up a Turnable server, a sing-box Hysteria2 backend, and auto-emits the client URLs.
| Layer | Size |
|---|---|
| Compressed (Docker Hub) | ~35 MB |
| Unpacked (on device) | ~75 MB |
Multi-stage build — Turnable + sing-box binaries on Alpine, no Go toolchain in the final image. Multi-arch: amd64 / arm64 / armv7.
Vanilla Turnable is only a transport — it relays to a backend you host and route to separately, which on RouterOS means cross-container routing, UDP return-path asymmetry, and a second service to babysit. This image folds the whole chain into one container.
| Area | Bare upstream | This image |
|---|---|---|
| Backend | You run + route to s-ui/3x-ui | sing-box Hysteria2 built in, route to 127.0.0.1 |
| proto | Docs default dtls | Forced to srtp — VK relay only forwards call media; dtls never completes |
| Keys | Manual, drift on restart | Persistent, client pub_key always matches server priv_key |
| UUIDs / password / cert | Hand-written | Auto-generated and persisted |
| TLS pin | none | Emits both pinSHA256 and pinnedPeerCertSha256 (WireTurn's Xray needs hex) |
| Call presence | Keep a client connected | Periodic anchor joins every 30 min, leaves cleanly |
| Crash handling | none | Turnable, sing-box, anchor self-restart in-container |
/interface/veth add name=veth-tb address=172.16.182.2/24 gateway=172.16.182.1
/ip/address add address=172.16.182.1/24 interface=veth-tb
/ip/firewall/nat/add chain=srcnat src-address=172.16.182.0/24 action=masquerade comment="turnable out"
/ip/firewall/nat/add chain=dstnat protocol=udp dst-port=56000 action=dst-nat to-addresses=172.16.182.2 to-ports=56000 comment="turnable listen"
Only 56000/udp is forwarded in. The Hy2 backend listens on 127.0.0.1 inside the container and is never exposed.
/container/mounts/add name=tb_config src=/usb1/turnable-confs dst=/etc/turnable
Holds your *.json config, plus auto-created .keys/ (ML-KEM keys + Hy2 cert), clients/ (the generated URLs), and singbox.log. Without this mount the keys regenerate on every restart and old client links break.
/container/add
remote-image=ulfrasark/turnable-hy2:latest
interface=veth-tb
mounts=tb_config
root-dir=/usb1/turnable
dns=8.8.8.8
start-on-boot=yes
restart-policy=on-failure
restart-interval=60s
logging=yes
RouterOS caches images by name:tag. When updating, use a NEW tag AND a NEW root-dir, or the old layer is reused.
/container/start [find interface=veth-tb]
/log print where topics~"container"
On startup the container prints the client links. Hand out two per user, both from the mount's clients/ dir:
turnable://... (transport via VK)
hysteria2://... (proxy)
| File | Purpose |
|---|---|
| server.json | Turnable server config (an example below, renamed) |
| hysteria2.json | Built-in Hy2 backend fields |
| config.shared-one-cred.example.json | One credential, everyone shares it |
| config.5users-own-creds.example.json | 5 users, own creds each, revocable individually |
| config.vless.example.json | VLESS-over-TCP backend variant |
{
"listen_port": 2443,
"password": "PUT_PASSWORD_OR_LEAVE",
"sni": "max.ru",
"obfs_password": "",
"up_mbps": 0,
"down_mbps": 0
}
listen_port must equal the Turnable route port. Leave password as PUT_* to auto-generate. up/down_mbps 0 = no brutal (a single VK allocation is already capped to ~2 Mbit flat, so shaping is usually pointless).
{
"servers": {
"vk": {
"type": "relay",
"provider": "prov",
"platform_id": "vk.com",
"call_id": "PUT_VK_CALL_ID",
"public_ip": "PUT_YOUR_VPS_IP",
"listen_addr": "0.0.0.0:56000",
"proto": "srtp",
"pub_key": "",
"priv_key": ""
}
},
"providers": {
"prov": {
"type": "raw",
"routes": [
{ "id": "shared", "address": "127.0.0.1", "port": 2443, "socket": "udp", "transport": "none" }
],
"users": [
{ "uuid": "PUT_SHARED_UUID_OR_LEAVE", "allowed_routes": ["shared"], "type": "relay", "peers": 1 }
]
}
}
}
Fill call_id (from a VK call link) and public_ip. Leave keys / UUID / password empty — they auto-generate and persist. The route port must match hysteria2.json.
| Port | Protocol | Required |
|---|---|---|
| 56000 | UDP | Always (Turnable listener; VK relay connects here) |
| Peers | Throughput | Note |
|---|---|---|
| 1 | ~2 Mbit flat | One allocation, VK policer, no sawtooth. Quiet. |
| 2 | ~3 Mbit | Floats; 2 participants in the call |
| 4 | ~5 Mbit | Floats more; 4 participants — higher antifraud signal |
Keep peers=1 for quiet long-term use; raise only for short bursts. Each peer is another "participant" VK's antifraud can see.
phone Xray (Hy2, pinned) -> Turnable client -> VK TURN relay (SRTP) -> Turnable server :56000 -> route 127.0.0.1:2443 -> sing-box Hysteria2 -> internet
The client briefly joins the VK call to get TURN credentials, then leaves the room while the allocation stays live — traffic rides the allocation, not a visible participant, which keeps antifraud quiet.
| Key | Default | Purpose |
|---|---|---|
| TURNABLE_ANCHOR_MODE | periodic | periodic = brief visits, off = none |
| TURNABLE_ANCHOR_INTERVAL | 1800 | seconds between visits |
| TURNABLE_ANCHOR_HOLD | 20 | seconds held per visit |
Transport: TheAirBlow/Turnable
Proxy core: SagerNet/sing-box
Client: WireTurn
This image packages and wires them together for RouterOS; all protocol credit is theirs.
Source and Dockerfile: coming soon
Content type
Image
Digest
sha256:675ac99eb…
Size
24.6 MB
Last updated
18 days ago
docker pull ulfrasark/turnable-hy2