Start your 3-day free trial
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.


VPS IPv6 outbound works for a self-hosted proxy when the server has a working global IPv6 address and default route, and the proxy is told which address family to prefer when it resolves a destination. Confirm the allocation first, then configure the system, the firewall and sing-box in that order, and keep IPv4 as a tested fallback. This procedure is based on the official sources listed below; a complete end-to-end test has not been performed.
Key Takeaways
- The provider's allocation decides what you can configure, so read it before editing anything.
- ICMPv6 carries Neighbor Discovery; blocking all of it can break the IPv6 path.
- In sing-box 1.12 and later, the IPv6 preference lives in
domain_resolver, not the deprecateddomain_strategy.- This guide uses one stable IPv6 address; it does not cover rotating addresses to avoid bans or rate limits.
A proxy server opens a new connection to each destination on the client's behalf. When the client sends a domain name, the proxy resolves it and picks an IPv4 or IPv6 address; when the client sends a literal IP, the family is already decided. In sing-box, dial fields control that choice and can also bind outgoing connections to a specific local IPv6 address.[1]
So the server needs two things: an IPv6 path that works from the operating system, and a proxy setting that uses it. The overview of running your own network services covers the wider maintenance duties. If the address families themselves are new to you, start with how IPv4 and IPv6 addressing differ, because the checks below assume you can read an IPv6 prefix.
This is a server-side change. A client device sending IPv6 outside its tunnel is a different problem, covered in the explanation of IPv6 leaks.
Providers usually hand out one of a few common shapes, and each one changes what "add an address" means. With an on-link prefix, the upstream router finds your address through Neighbor Discovery: it sends a Neighbor Solicitation, and only an address actually configured on your interface answers.[2] With a routed prefix, the provider forwards the whole prefix to one address you already hold.
| Allocation | What the router expects | What to configure | Main risk |
|---|---|---|---|
| Single /128 | One address answering ND | That address and the stated gateway | Typo in address or gateway |
| On-link /64 | Addresses answering ND on the link | One chosen address from the prefix | Using an address you never configured |
| Routed /64 | Prefix forwarded to a link address | Link address plus prefix use | Missing the separate link address |
| SLAAC via RA | Router Advertisements on the link | Accept RA; avoid static duplicates | RA ignored when forwarding is on |
Read the provider's control panel or documentation for the exact address, prefix length and gateway; some providers use a link-local gateway such as fe80::1. The rest of this guide uses one stable address. Cycling many addresses per connection to get around platform blocks or limits is out of scope here, and it can break the provider's or the destination's terms.
Log in over IPv4 first, so an IPv6 mistake cannot lock you out. Record the current state:
ip -6 addr show scope global
ip -6 route show default
sysctl net.ipv6.conf.all.forwarding
sysctl net.ipv6.conf.eth0.accept_ra
curl -6 -sS -o /dev/null -w '%{http_code}\n' https://example.com
Replace eth0 with your interface. A global address plus a default route and a successful curl -6 mean the system path already works, so you can skip to Step 3. A global address without a default route, or no global address, means Step 2 applies.
The accept_ra value matters on proxy servers that also forward traffic, such as a VPN endpoint. With accept_ra set to 1, the kernel accepts Router Advertisements only while forwarding is disabled; 2 accepts them even when forwarding is enabled.[3] If your provider relies on RA and you enable forwarding, the default route can disappear silently.
On Ubuntu servers that use Netplan, add a separate file instead of editing the provider's generated one, and keep a copy of every existing file under /etc/netplan/. The example sets one address and a Netplan IPv6 default route, using the documentation prefix 2001:db8::/32; replace the address, prefix length, gateway and interface with your provider's values.
network:
version: 2
ethernets:
eth0:
addresses:
- "2001:db8:1234:5678::10/64"
routes:
- to: "::/0"
via: "fe80::1"
on-link: true
Netplan's addresses adds static addresses alongside any from DHCP or RA, routes needs at least to, and a default route can be defined only once per IP family and routing table.[4] on-link: true tells the system the gateway is directly connected, which matters when the gateway sits outside your prefix. If the provider uses RA instead, set accept-ra: true and leave the static route out, so you do not end up with two competing defaults.[4]
Apply with a safety timer:
sudo netplan try
netplan try applies the configuration and rolls it back unless you confirm within 120 seconds by default; after a timeout or cancel, check that the old state actually returned.[5] Run the Step 1 commands again before confirming.
Neighbor Discovery runs over ICMPv6, so a firewall that drops all ICMPv6 can make the address unreachable even when the configuration is correct.[2] Allow the ICMPv6 types your firewall tool marks as required for IPv6, rather than writing a blanket drop.
Then check exposure. A proxy listening on [::] accepts IPv6 connections, so the inbound rules you wrote for IPv4 need IPv6 equivalents. Compare ss -lntup with the firewall's IPv6 rule list, and confirm that management ports and any local-only listener stay closed to the internet on both families.
From sing-box 1.12.0, the domain_strategy dial field is deprecated and scheduled for removal in 1.14.0; the replacement is a domain_resolver object that names a DNS server and a strategy.[1][6] The strategy values are prefer_ipv4, prefer_ipv6, ipv4_only and ipv6_only.[7]
The following fragment shows only the parts that matter: a local DNS server, which resolves through the host system, a direct outbound that prefers IPv6, and a test inbound bound to the loopback address.[8] Merge it into your existing configuration rather than replacing your real inbound.
{
"dns": {
"servers": [{ "type": "local", "tag": "local" }]
},
"inbounds": [
{ "type": "mixed", "tag": "test-in", "listen": "127.0.0.1", "listen_port": 2080 }
],
"outbounds": [
{
"type": "direct",
"tag": "direct-out",
"inet6_bind_address": "2001:db8:1234:5678::10",
"domain_resolver": { "server": "local", "strategy": "prefer_ipv6" }
}
],
"route": { "final": "direct-out" }
}
inet6_bind_address pins IPv6 connections to the address you configured in Step 2; leave it out if you have only one global address.[1] Run sing-box check -c /etc/sing-box/config.json and restart the service only when the check passes.
| Strategy | Destination has A and AAAA | IPv4-only destination | Use it when |
|---|---|---|---|
prefer_ipv4 | IPv4 used | IPv4 used | IPv6 is new and untested |
prefer_ipv6 | IPv6 used | Falls back to IPv4 | You want IPv6 where available |
ipv6_only | IPv6 used | Fails | Testing the IPv6 path alone |
ipv4_only | IPv4 used | IPv4 used | Rolling back quickly |
Test through the loopback inbound on the server itself. --socks5-hostname sends the domain name to the proxy, so sing-box performs the resolution and applies the strategy.
curl -sS --socks5-hostname 127.0.0.1:2080 -o /dev/null -w '%{remote_ip}\n' https://example.com
Use these as expected observations and record your own results.
| Check method | Expected observation | If it fails |
|---|---|---|
| Dual-stack site through the test inbound | Connection succeeds over IPv6 | Recheck Step 1 and the bind address |
IPv4-only site with prefer_ipv6 | Connection falls back to IPv4 | Make sure you did not set ipv6_only |
| Client device through your real inbound | IP check shows the server's IPv6 for an IPv6 path | Compare client DNS and proxy rules |
| Firewall scan of public ports on IPv6 | Only intended ports answer | Add the missing IPv6 rules |
From a client connected to your proxy, check which address the outside world sees. An IPv6 result confirms that web requests take the new path; an IPv4 result can still be correct when the site or the client's own resolution chose IPv4. Remove the test inbound when you finish.
Keep the IPv4 session open while reverting. Delete or move the Netplan file you added, run sudo netplan apply, and confirm ip -6 route show default matches your Step 1 record. In sing-box, switch the strategy to prefer_ipv4 or ipv4_only, remove inet6_bind_address, run the configuration check and restart.
If the server becomes unreachable, use the provider's web console to remove the added file. Do not disable IPv6 kernel-wide as a first response; the ip-sysctl documentation notes that writing the all value of disable_ipv6 changes every interface at once.[3]
IPv6 egress changes the route your server takes, not who you are. Destinations still see one stable address tied to your VPS. If the goal is avoiding the operator's own responsibilities, a hosted service may fit better than a server you maintain.
It is also the wrong tool when the destination has no IPv6, when the provider's IPv6 path is unstable, or when you need per-user addresses. In those cases keep prefer_ipv4, and revisit after the provider fixes the path. Home routers that receive a delegated prefix need a different procedure, described in the OpenWrt prefix delegation guide.
netplan try, keeping IPv4 access open.domain_resolver with prefer_ipv6 in sing-box 1.12 or later, then test both IPv6 and IPv4 fallback.No. One working global address and a default route are enough for a proxy to reach IPv6 destinations. A /64 only gives you more addresses to choose from.
Usually the default route is missing or points to the wrong gateway. Compare ip -6 route show default with the provider's stated gateway, and check that the firewall is not dropping ICMPv6.
Not on 1.12 or later. It is deprecated there and scheduled for removal in 1.14.0, so move the preference into a domain_resolver object with a strategy value.
It should not. With prefer_ipv6, a destination without an AAAA record is still reached over IPv4; only ipv6_only makes those connections fail.
With accept_ra at 1, the kernel ignores Router Advertisements once forwarding is on. Set accept_ra to 2 on that interface or configure a static default route instead.
This guide does not cover that. Rotating addresses to get around blocks or limits can violate provider and site terms, and the procedure here keeps one stable address.
No. It changes which address family carries traffic. Destinations still see an address registered to your VPS, so your privacy depends on the same operator and logging choices as before.
Disclaimer: This article is for general informational purposes only and does not constitute legal, technical, or other professional advice. We make no guarantees regarding the accuracy, completeness, or timeliness of the content. Follow your hosting provider's acceptable use policy and the terms of the services you connect to.
Sources:
Sources checked 14 October 2026.
Related articles:
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.





