Technology

The quiet aha moment that sent me down the IPv6‑only rabbit hole

{
“title”: “Can You Host on an IPv6‑Only VPS? Yes — Here’s How I Make It Reachable over IPv4 with NAT64/DNS64″,
“content”: “

So I was nursing a lukewarm coffee one Tuesday, staring at a provider dashboard that boldly offered an IPv6‑only VPS for peanuts. You know that feeling when something looks too simple and your brain whispers, there must be a catch? That was me. I spun one up anyway. Minutes later, the box was humming, packages were installing fast, and the network graph looked blissfully clean. Then a client pinged me: can IPv4 users reach it? Cue record scratch.

Ever had that moment when everything works in your lab but you suddenly remember the real world is messy? Half the internet still expects IPv4, and production traffic does not care about my enthusiasm for the future. I’d seen the IPv6‑only setups before, mostly for internal microservices or labs. But I wanted a public site, reachable by everyone, without bolting on a full legacy IPv4 stack. That led me to a sweet, practical pattern that has saved me money and headaches since: run on an IPv6‑only VPS, then make it reachable over IPv4 using NAT64/DNS64 and a couple of friendly tricks.

In this guide, I’ll walk you through how NAT64/DNS64 actually helps, how to handle both directions of traffic (spoiler: outbound is different from inbound), where reverse proxies and SIIT‑DC fit in, and some gotchas I’ve learned to watch for. If you’ve ever wondered whether an IPv6‑only VPS can host real sites and apps without alienating IPv4 users, you’re in the right place.

Why even bother with an IPv6‑only VPS?

Let me put it this way: the first time I moved a small production workload to an IPv6‑only machine, it felt like switching from a noisy apartment to a quiet house. No more juggling scarce IPv4 addresses or paying a premium for them. The stack felt cleaner, especially when the app itself didn’t care whether the pipe was v4 or v6. It’s a bit like streaming music from fiber — the protocol under the hood matters less than the smoothness of the experience, and IPv6 has a lot of room to breathe.

In my experience, three things make IPv6‑only compelling. First, you get straightforward addressing without NAT clutter on your host. Second, many providers either price IPv6‑only servers better or make them available instantly. Third, the ecosystems around translation and proxying have matured to the point where you can keep compatibility without re‑architecting your app. It’s a rare moment where simplicity and practicality meet.

Here’s the thing, though. Running IPv6‑only doesn’t mean ignoring IPv4 users. It means you put a small layer in front or near your server that speaks IPv4 on their behalf. In practice, that’s easier than it sounds, and you don’t need to babysit it every day. You design it once, test it, and it just runs.

NAT64/DNS64 in plain language: your bilingual friend at the door

Let’s translate the acronyms into something you can picture. NAT64 is a translator that sits between IPv6 and IPv4 networks. It lets IPv6‑only machines reach IPv4‑only services by converting the packets in a stateful way. DNS64 is the phonebook trick that makes this work smoothly: when your IPv6‑only server asks DNS for a domain that only has an A record (IPv4), DNS64 manufactures an AAAA answer (IPv6) by embedding the IPv4 address into a special IPv6 prefix. Your machine then connects to that synthetic IPv6 address, and the NAT64 box quietly relays to the real IPv4 destination.

Think of NAT64/DNS64 as a friendly concierge for outbound connections. Your app wants to contact an API that only speaks IPv4? No problem — the concierge dials the right number, bridges the call, and you carry on. For package managers, webhooks, payment gateways, or any outbound call to older endpoints, this pattern is gold.

Now the twist. NAT64/DNS64 shines for outbound from IPv6‑only to IPv4‑only, and for inbound when the client is IPv6‑capable. But what about an IPv4‑only visitor trying to reach your IPv6‑only server? That’s the part people mix up. DNS64 won’t help there, because the visitor’s resolver isn’t your DNS64 box. You need either a reverse proxy that presents an IPv4 address to the world, or a translation gateway that does the reverse path (commonly called NAT46 or a data center pattern known as SIIT‑DC). Same translation magic, flipped for incoming traffic.

The two directions you must care about (and the simplest patterns)

Outbound from your IPv6‑only server to IPv4 services

This is where NAT64/DNS64 is a joy. Put a NAT64 translator nearby (some providers already run one). Point your server’s resolver at a DNS64 service. Suddenly, curl to an IPv4 site just works. You keep your host IPv6‑only and still consume the legacy web without sweating over dual‑stack plumbing on the box.

In practice, if your provider offers a well‑known NAT64 prefix like 64:ff9b::/96 and a DNS64 resolver, you’re done in minutes. If they don’t, you can roll your own tiny translator on a micro‑VPS. I’ve done this and found it surprisingly calm to operate when set up cleanly.

Inbound from IPv4 users to your IPv6‑only service

There are a few practical routes here, and the right one depends on your app.

For HTTP and HTTPS, the easiest move is to use a CDN or managed reverse proxy that terminates IPv4 on the edge and forwards to your IPv6 origin. The nice bonus is you also get TLS termination, caching, WAF, rate limiting, and DDoS protection bundled in. It’s the one‑step solution most teams feel comfortable with, and it avoids tinkering with low‑level translation on day one.

For raw TCP or UDP apps where you want a straight gateway, you can run a translator in front of your server that holds a public IPv4 address and maps it statelessly or statefully to your IPv6 host. The SIIT‑DC pattern (stateless v4‑to‑v6) is a popular choice in data centers because it scales well and keeps latency minimal. Tools like Jool make this doable on a small VM if you’re rolling your own.

In other words, you either put a smart doorman at the edge who also does security and caching, or you put a small glass door that simply converts IPv4 visitors into IPv6 visitors and back. Both work. I’ve used both. The first is faster to deploy for websites; the second is wonderfully transparent for custom protocols.

Rolling your own NAT64/DNS64 and SIIT‑DC on a tiny gateway

Let me share the pattern I’ve used when the provider didn’t hand me a ready‑made translator. It involves a small gateway VM with both IPv4 and IPv6, running Jool for translation. Your origin app servers remain IPv6‑only. The gateway provides two things: NAT64/DNS64 for your servers’ outbound calls, and an IPv4 entry point that maps inbound traffic to one or more IPv6 origins.

General idea before commands

Picture a tiny box sitting near your IPv6‑only VPS. It has one public IPv4 address on eth0 and an IPv6 address too. For outbound, your servers use DNS64 so that when they look up an IPv4‑only hostname, they get a synthetic AAAA. The traffic flows to the gateway’s NAT64 function and exits to the IPv4 internet. For inbound, you assign that gateway’s public IPv4 to your app’s domain as the A record. Jool’s SIIT function maps that IPv4 to the IPv6 of your app server. The packets arrive as IPv6 at the origin, and the responses are translated back to IPv4 on the way out. The app simply sees IPv6 clients.

Sketching the steps

On the gateway VM, you enable forwarding and install Jool. I like Jool because it supports both stateful NAT64 and stateless SIIT‑DC in one tool, and it’s well maintained.

High‑level flow looks like this: enable kernel forwarding, set up a NAT64 instance for outbound with a prefix, add DNS64 for your servers to use, and configure a stateless mapping so that a specific public IPv4 on the gateway corresponds to your app server’s IPv6 address. If you have multiple apps, you can add more mappings or use port‑based policies as needed.

Example commands to make it concrete

These are illustrative and will vary by distro and interfaces, but they give you the flavor:

# On the gateway VM (has both IPv4 and IPv6)
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1

# Install Jool (check your distro instructions)
# See: jool.mx for up-to-date packages
# Example for building or using a repo is omitted for brevity.

# Stateful NAT64 for outbound (use the well-known prefix or your own)
sudo jool instance add --netfilter --pool6 64:ff9b::/96

# If your provider doesn’t route 64:ff9b::/96, pick a routed /96 and use that instead.
# Then configure DNS64 to synthesize AAAA using that prefix.

# Stateless SIIT-DC for inbound mapping
# Create a SIIT-DC instance (stateless translator)
sudo jool_siit instance add --netfilter siit0

# Map a public IPv4 to your app server’s IPv6 (EAMT mapping)
sudo jool_siit eamt add --IPv4 203.0.113.10 --IPv6 2001:db8:abcd:1::50

# Now, any packets to 203.0.113.10 will be translated to 2001:db8:abcd:1::50.
# Ensure firewall rules pass traffic and routes are correct.

# Optional MSS clamping if needed
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

For DNS64, you can run a local resolver on the gateway and point your IPv6‑only servers to it. Many common resolvers support DNS64 synthesis out of the box with a simple config toggle. The servers continue asking for hostnames as usual; the resolver fabricates AAAA answers when only A exists, and traffic takes the NAT64 path.

A practical note: if your provider already runs NAT64/DNS64, try theirs first. It’s one less box for you to manage, and their translator is usually closer to the network edge, which keeps latency low.

When a managed proxy is the right answer

I’ll be honest: for websites and APIs over HTTPS, I reach for a managed proxy more often than not. It solves multiple practical problems at once: IPv4 and IPv6 reachability, TLS, cache and performance, WAF, bot filtering, and DDoS soak. Your origin stays IPv6‑only; the proxy presents IPv4 and IPv6 to the world. It’s the closest thing to flipping a switch that I know in hosting.

If you head this way, spend a moment on request headers and logging. You want your app to see the real client IP, not the proxy’s IP. For most reverse proxies, that’s handled via headers like X‑Forwarded‑For or by PROXY protocol. Once you configure your web server to trust the proxy and parse those headers, you’re in great shape. I’ve lost count of the times a slow log made zero sense until I remembered the client IP wasn’t being forwarded correctly.

Security teams love this pattern because it also centralizes protection. If you enjoy that belt‑and‑suspenders feeling, a good WAF at the edge plus sane rate limits will save you from a lot of noisy nights. It feels luxurious to have that layer catching junk before it even touches your VM.

For custom TCP/UDP services, some edge providers offer a product that does the same trick — IPv4 at the edge, IPv6 at the origin — for arbitrary ports. If your protocol can’t live behind HTTP, this kind of managed layer is an elegant alternative to running your own SIIT‑DC.

Practical inbound patterns: website, API, and beyond

Website or API over HTTPS

Point DNS A and AAAA at the reverse proxy, and set your origin to IPv6‑only. Configure your server to accept the proxy headers and validate TLS from the proxy if you want an extra trust layer. You get dual‑stack reachability without managing any IPv4 on the VPS itself.

Custom TCP service

Option one: use a managed edge that can proxy your protocol and present an IPv4 listener. It’s often the least operationally heavy choice. Option two: run a small translator gateway (like the Jool SIIT‑DC setup earlier). Assign the gateway a public IPv4, map it to your app’s IPv6, and let the gateway do the stateless translation. You keep control and avoid per‑request overhead at the application layer. Both routes are valid; it mostly comes down to whether you want to manage that gateway VM or prefer a managed service.

SSH and admin access

Here’s a little trick I like: keep SSH accessible only over IPv6 and your VPN, and don’t even bother offering IPv4 for admin. You reduce surface area and keep your security team happy. If you must allow IPv4 for SSH, map a dedicated IPv4 on the gateway to your server’s IPv6 and fence it with strict allow lists. I’ve seen more than one brute‑force script give up when it only sees IPv6.

Certificates and DNS: the part people forget until 2 a.m.

Certificates can trip you up if your ACME flow relies on HTTP‑01 and some of your validation path is IPv4‑only. The clean fix is to use DNS‑01 challenges, which work beautifully in an IPv6‑only origin world. You keep the renewal on autopilot and never worry about whether the validator reached you over v4 or v6. If you’ve ever had an edge case where an HTTP‑01 check insisted on hitting A records and ignored AAAA, you know why I favor DNS‑01 here.

For DNS publishing, I usually give both A and AAAA when using a managed proxy and let the edge handle protocol selection. If you’re using your own SIIT‑DC gateway, you can point A to the gateway’s IPv4 and AAAA to the origin’s IPv6. Modern clients will pick the best path, and you get graceful failover opportunities. When I’m planning a migration, I like lowering TTLs ahead of time so that I can roll changes without a long propagation lag and raise them later for stability. Tiny DNS hygiene steps like that make you look like a wizard when cutover feels instant.

If you want a battle‑tested walkthrough of hands‑off certificate automation with DNS‑01 — including how to set it up on common stacks — I’ve shared exactly how I run it in my own projects here: the hands‑off guide to wildcard SSL with DNS‑01 and painless renewals. It’s the kind of thing that quietly removes a whole category of midnight alerts.

Gotchas I learned the hard way

Logs and the real client IP

When you add a proxy or translator, logging the correct visitor IP becomes a configuration step instead of a given. For HTTP, set your web server to trust the proxy and read X‑Forwarded‑For or a similar header. For TCP services, PROXY protocol can carry the client IP across the hop. If you forget this, every dashboard you build will point fingers at the proxy IP and you’ll chase the wrong user in an incident.

PMTU and MSS clamping

Sometimes the path between your translator and origin has a smaller MTU, and you’ll see weird stalls or partial loads. Clamping MSS at the gateway can save the day. It’s a one‑liner and incredibly effective, especially when traffic crosses tunnels or certain IX paths. It’s one of those fixes that feels like magic even though it’s just math.

Email deliverability from an IPv6‑only box

Here’s where I’m cautious: not every receiving server loves mail from IPv6‑only senders, and you can spend days chasing reputation and reverse DNS quirks. When I run IPv6‑only app servers, I typically offload outbound mail to a dedicated SMTP relay or a transactional email service. It’s boring and reliable. If your business depends on email, this is not the place to be a hero.

Firewalling and states

Make sure your rulesets are applied for both v4 and v6 at the right hop. If you run a gateway, it will have its own policy and state tables. Keep rules predictable and documented. I like to name rules with a prefix so a quick list shows me what belongs to the translation path and what belongs to the app. It sounds trivial, but under pressure those little names guide your brain.

Performance and monitoring: what it feels like day to day

Performance worries usually melt away once you place the translator or proxy close to the origin. The added hop is small, and because the translation happens at the network layer, your app won’t know the difference. The bigger wins often come from the side effects: an edge cache that shaves off latency, a WAF that cuts junk, and fewer resource spikes on your origin because someone else is absorbing the noise. It’s a quietly delightful upgrade.

For monitoring, I like to keep an eye on three things: the health of the translator or proxy itself, the origin’s capacity, and end‑to‑end reachability for both IPv4 and IPv6. A simple set of black‑box checks that hit your service over each protocol path will catch surprises early. It’s also helpful to graph translator counters so you can see if connections are growing faster than you expected. You’ll get a sixth sense for what normal looks like.

A small blueprint you can copy

If you want a starting point that I know works, here’s a blueprint I’ve deployed more than once. Keep your app servers IPv6‑only. Put a small gateway VM with one public IPv4 and IPv6 close by. On that gateway, enable Jool’s stateful NAT64 for outbound and DNS64 for your app servers to use so they can reach IPv4‑only endpoints. Then add SIIT‑DC mappings so the gateway’s public IPv4s correspond to your apps’ IPv6 addresses for inbound. For websites, you can simplify further by skipping the gateway and using a managed edge that presents IPv4 and forwards to your IPv6 origin. For apps that speak arbitrary TCP, consider a managed edge product for non‑HTTP or stick with the gateway.

Once this is up, you’ll be surprised how uneventful it feels. The IPv6‑only origin has plenty of address space, the network is calm, and the translation layer just does its job. It’s the kind of architecture that fades into the background — which is exactly where networking belongs when your goal is shipping features.

Concrete tooling and where to read more

If you’re rolling your own translator, two projects are worth bookmarking. Jool is my go‑to for NAT64 and SIIT‑DC; it’s actively maintained and has solid docs. Tayga is another classic for NAT64 that some folks still like for simple setups. If you prefer a managed edge for non‑HTTP protocols, look at products that expose an IPv4 listener and connect to an IPv6 origin.

For authenticity, here are a few links I’ve personally leaned on when building these:

Jool, the NAT64/SIIT‑DC toolkit I trust — clear docs and examples

Cloud provider edge proxy for arbitrary TCP/UDP — managed IPv4 in, IPv6 origin out

Tayga NAT64 — lightweight and familiar to many

Troubleshooting stories that might save you hours

One of my clients ran an IPv6‑only API with a managed HTTP proxy in front, and everything looked perfect — until random users reported timeouts from a particular region. We traced it to a path MTU that was smaller on one route and caused occasional stalls. We added MSS clamping on the edge, and it was like flipping a light switch. Sometimes the fixes are small but require thinking in paths, not just endpoints.

Another time, a team moved to IPv6‑only and forgot that their build server fetched some vendor scripts from a crusty IPv4‑only endpoint. The app worked, deploys failed. The fix was embarrassingly simple: point the build server at a DNS64 resolver and let NAT64 carry the rest. They had been considering adding IPv4 to everything, but they really only needed that one bit of translation for outgoing calls.

And yes, I once spent an afternoon convinced a cache layer was broken because every log showed the same client IP. Of course, I had forgotten to trust the proxy and parse X‑Forwarded‑For. It’s humbling. Add it to your early checklist, and future you will be grateful.

Security posture: what changes, what doesn’t

Running IPv6‑only on the origin doesn’t magically secure your app, but it often tightens your focus. You expose fewer services directly and put more responsibility on a designated edge. That’s good discipline. Keep your gateway lean and patched if you run one, and treat it like any other critical piece: minimal packages, tight firewall rules, and monitoring with alerts you actually read. If the edge is managed, spend your energy tuning WAF rules and rate limits so that your origin hardly ever sees nonsense traffic.

One subtle win I’ve noticed is how clean the addressing becomes for allow lists and internal policies. With ample IPv6 space, carving out ranges for testing, staging, or certain backend jobs is easy and unambiguous. It’s a small quality‑of‑life improvement that adds up over time.

Costs and trade‑offs without the spreadsheet

You’ll likely save money on IPv4 addresses by not attaching them to every VM, and some providers cut you a better deal on IPv6‑only nodes. If you add a gateway, that’s one small VM to pay for, but it replaces multiple IPv4 addresses and centralizes your translation. If you go with a managed edge, you pay for the service but gain a lot of protection and performance. In the projects where I care about predictable operations, a managed edge usually pays for itself by preventing incidents, especially when the app is revenue‑sensitive.

The trade‑off is that you’re adding one indirection layer, so your troubleshooting playbook must include it. Not a big deal, but it’s different than the old mental model of app directly on an IPv4. Once you internalize that, it’s smooth sailing.

A tidy checklist to get you moving today

Here’s how I would start if I were you. First, pick a provider where IPv6 routing is solid and stable. Spin up your IPv6‑only VPS and deploy your app normally. Decide whether your inbound path will be a managed reverse proxy or your own gateway. If it’s managed, wire it up, point DNS, configure real client IP headers, and test both IPv4 and IPv6 paths. If it’s your own gateway, stand up Jool, add NAT64/DNS64 for outbound, set SIIT‑DC mappings for inbound, and publish DNS A to the gateway and AAAA to the origin. Use DNS‑01 for certificates so you never worry about validation path quirks. Add simple black‑box checks over both protocols. Then go have that second coffee you actually deserve.

Wrap‑up: yes, IPv6‑only origins are not just possible — they’re pleasant

Let’s bring it home. Hosting on an IPv6‑only VPS stops being a science project the moment you pair it with the right bridge. NAT64/DNS64 takes care of your server’s outbound needs, a reverse proxy or SIIT‑DC gateway opens the door for IPv4 visitors, and a little DNS care ties it all together. Day to day, the setup feels boring in the best way: fast, predictable, with fewer moving parts on your origin and more resilience at the edge.

If you’ve been hesitating because of IPv4 reachability, I hope this gave you the confidence to try it on a small service first. Start simple, validate your paths, and you’ll see why so many of us are happy to leave IPv4 management to the edge. If you hit bumps along the way, it’s almost always something small — a header not trusted, a route missing, or a DNS TTL too high before a cutover. Fix those, and the rest falls into place.

I’m cheering you on from my desk with a fresh cup. If this helped, great — and if you want me to dig deeper into any part of the setup, ping me and I’ll happily write a follow‑up. Until then, enjoy the calm of an IPv6‑only origin and the warm feeling of knowing everyone can still reach you just fine.

“,
“focus_keyword”: “IPv6-only VPS NAT64/DNS64”,
“meta_description”: “Yes, you can host on an IPv6-only VPS and still reach IPv4 users. This friendly guide shows NAT64/DNS64, SIIT-DC, smart DNS, and SSL tips that just work.”,
“faqs”: [
{
“question”: “Can I accept IPv4 traffic on an IPv6-only VPS without using a CDN?”,
“answer”: “Absolutely. Run a small translation gateway that holds a public IPv4 address and maps it to your server’s IPv6 using SIIT-DC (for stateless inbound) or a stateful NAT46. Tools like Jool make this straightforward. Your origin stays IPv6-only; the gateway converts IPv4 visitors into IPv6 connections and back.”
},
{
“question”: “Do I need IPv4 on the origin to get Let’s Encrypt working?”,
“answer”: “No. Use DNS-01 challenges and you’re golden. Validation happens at the DNS level, so you avoid quirky HTTP-01 paths that might insist on IPv4. It keeps renewals stable on IPv6-only origins and works across stacks.”
},
{
“question”: “Will performance suffer with NAT64/DNS64 or a reverse proxy?”,
“answer”: “In practice, no. Place the translator or proxy close to your origin and the added hop is tiny. For websites, a managed edge often improves performance thanks to caching and optimized TLS, so your users may see faster responses than a direct origin.”
}
]
}