Introduction
IPv6 on Raspberry Pi isn’t magic, but it does take more work than people expect. You don’t just slap an IPv6 address on a service and walk away. There’s static addressing, reverse proxy setup, DNS records, and if you’re lucky, a router that doesn’t throw a tantrum. Most of the time, it feels like your Pi is perfectly fine talking to the world until you realize nobody’s talking back because DNS is busted or the reverse proxy is clueless.
I’ll walk you through getting your Raspberry Pi online with IPv6, setting up a reverse proxy using Nginx, and making sure your DNS records actually point to the right address. We’ll deal with static IPv6, AAAA records, and a few things your ISP probably didn’t tell you.
Key Takeaways
- IPv6 gives your Raspberry Pi public access without NAT but needs careful firewall and DNS setup.
- Static IPv6 setup avoids service downtime due to address changes.
- Nginx handles multiple services under one IPv6 address using reverse proxy rules.
- AAAA records are critical for IPv6 reachability and must point to the correct address.
- Use Dynamic DNS if your public IPv6 prefix isn’t static.
Choosing the Right Raspberry Pi and OS
Picking a model that won’t choke on packets
Not all Raspberry Pi models are equal. If you’re planning to run multiple services, forward traffic, and handle IPv6 lookups without latency, go for at least a Raspberry Pi 4. The 3B+ is decent if you’re patient. Anything older or with only 512MB RAM might crash once you throw a reverse proxy into the mix. Trust me, I’ve tried.
Operating systems that actually speak IPv6
Raspberry Pi OS (formerly Raspbian) handles IPv6 pretty well out of the box, especially the 64-bit version. Ubuntu Server works too, though it uses netplan instead of dhcpcd. Arch Linux ARM and DietPi are lighter and snappier, but you’ll need to configure more by hand.
Enable IPv6 at the system level
Most Pi OSes already have IPv6 enabled, but double-check it didn’t get disabled by accident:
cat /proc/sys/net/ipv6/conf/all/disable_ipv6
If it returns 1, you’ve got a problem. Set it to 0 by editing /etc/sysctl.conf or creating a new file in /etc/sysctl.d/ with this:
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
Then reload with:
sudo sysctl -p
Assigning a Static IPv6 Address
Why dynamic IPv6 isn’t enough
Your Raspberry Pi probably got an IPv6 address through SLAAC (Stateless Address Autoconfiguration) or maybe DHCPv6. That’s fine for browsing Reddit, but not great for hosting anything. If your Pi’s address changes every time the router sneezes, your DNS records are toast and your reverse proxy breaks. Static is the way to go.
How to assign a static IPv6 address on Raspberry Pi OS
On Raspberry Pi OS, you’ll want to edit /etc/dhcpcd.conf. Add a block like this:
interface eth0
ipv6only
static ip6_address=2001:db8:abcd:1234::100/64
static routers=2001:db8:abcd:1234::1
static domain_name_servers=2001:4860:4860::8888 2001:4860:4860::8844
Make sure your IPv6 address is inside your prefix range. You’ll probably get that from your router or ISP. If you’re not sure, check with:
ip -6 route
Ubuntu or other netplan-based OS setups
For distros using netplan, go to /etc/netplan/01-netcfg.yaml or whatever your config file is called:
network:
version: 2
ethernets:
eth0:
dhcp6: no
addresses:
- 2001:db8:abcd:1234::100/64
gateway6: 2001:db8:abcd:1234::1
nameservers:
addresses: [2001:4860:4860::8888, 2001:4860:4860::8844]
Then apply changes:
sudo netplan apply
Verify it’s working
Check your address and routes:
ip -6 addr show dev eth0
ip -6 route
You want to see your assigned address, your gateway, and ideally, no tentative or dadfailed errors.
Testing IPv6 Connectivity
Checking if the thing’s even alive
All right, your Raspberry Pi has a static IPv6 address. Great. But does it actually work? Let’s not assume. A misconfigured gateway or missing DNS server will ruin your day. You need to test it.
Basic connectivity checks
Use ping6 (or just ping with -6) to test reachability:
ping6 google.com
If that fails, try pinging an IPv6 address directly:
ping6 2001:4860:4860::8888
Still failing? That means the issue is probably routing or DNS, not the service.
Testing outbound and inbound traffic
Try reaching a public IPv6 test site:
curl -6 https://icanhazip.com
It should return your public IPv6 address. If it doesn’t, you’re either not getting a routed IPv6 prefix or the firewall is blocking it.
To test if your Raspberry Pi is reachable from the outside:
- Use a phone or laptop on a different network (like mobile data).
- Run
ping6 your-pi-ipv6-addressor access it via browser if you have a web service running.
Check open ports
Use nmap to see what services are available over IPv6:
nmap -6 -p 22,80,443 your-pi-ipv6-address
If you see filtered ports, your firewall or router may be interfering.
Useful diagnostics tools
ip -6 route show: Confirms your routing table.dig AAAA google.com: Tests DNS resolution over IPv6.netstat -tulnp | grep LISTEN: Shows listening services, so you know if they’re bound to IPv6.

Reverse Proxy with Nginx over IPv6
Why you need a reverse proxy in the first place
If you’re running more than one service on your Raspberry Pi – like a media server, dashboard, or even your own wiki; you can’t just open up 10 different ports. That’s messy and invites trouble. A reverse proxy like Nginx lets you keep everything on ports 80 and 443, then route traffic by hostname or path. Cleaner, safer, and lets you use SSL certificates without pulling your hair out.
Install Nginx
On Raspberry Pi OS or Ubuntu:
sudo apt update
sudo apt install nginx
Enable and start it:
sudo systemctl enable nginx
sudo systemctl start nginx
Make sure it listens on IPv6
By default, Nginx might only be listening on IPv4. To add IPv6, open your site config in /etc/nginx/sites-available/default (or your custom config) and make sure it includes this:
listen [::]:80 default_server;
listen [::]:443 ssl default_server;
This tells Nginx to listen on all IPv6 interfaces for HTTP and HTTPS. Then reload it:
sudo systemctl reload nginx
Routing traffic to local services
Here’s a basic reverse proxy block for forwarding traffic to something running on port 8123 (like Home Assistant):
server {
listen [::]:443 ssl;
server_name yourdomain.example.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.example.com/privkey.pem;
location / {
proxy_pass http://[::1]:8123;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
You can add as many of these as you want for different services. Just change the domain and the port.
Check your config before restarting
Always run this to make sure your config won’t break Nginx:
sudo nginx -t
If it says everything is okay, reload it again.
sudo systemctl reload nginx
DNS Configuration with AAAA Records

If nobody can find your Pi, does it even exist?
You can have the slickest IPv6 setup in town, but if DNS doesn’t point to it, nobody’s getting through. AAAA records are what tell the internet, “Hey, this domain lives at this IPv6 address.” It’s like giving out your number but forgetting to tell people which area code to use.
What’s a AAAA record?
Simple: it maps a domain name to an IPv6 address, just like an A record maps to an IPv4 one. The format’s the same, but the address looks scarier.
Example:
ipv6.mypi.example.com. IN AAAA 2001:db8:abcd:1234::100
Where to add them
Depends on your DNS provider. Here are some basics:
- Cloudflare: Go to DNS > Add Record > Type: AAAA > Name:
@oripv6> Content: your IPv6 address. - DuckDNS: Add your static IPv6 address to the domain settings manually or with a script.
- Namecheap: Same as above… just use the Advanced DNS panel.
Verify it’s working
Use dig or nslookup:
dig AAAA ipv6.mypi.example.com
You should see the IPv6 address in the answer section. If it doesn’t show, DNS hasn’t propagated or you made a typo (it happens).
DNS propagation times
Even though IPv6 records tend to propagate faster, give it a few minutes. Also check TTL (time to live) on your records – lower values like 300 seconds are good while testing.
Troubleshooting
- Typo in address: One wrong hex group and you’re toast.
- Firewall blocking requests: Even if DNS works, your Pi needs to actually respond to traffic.
- Record conflict: Make sure there’s no CNAME pointing somewhere else.
Firewall and Port Forwarding for IPv6
IPv6 skips NAT, but you’re not totally off the hook
Here’s where things get weird. Unlike IPv4, IPv6 doesn’t usually need NAT. Every device can have a public address. Sounds great, right? Until you realize your Pi is now exposed to the entire internet, and if your firewall’s asleep on the job, someone else might start poking around.
Why port forwarding is different with IPv6
Because there’s no NAT, you don’t technically “forward” ports in the same way. If your Pi has a public IPv6 address, it just needs the router to route properly and your firewall to allow traffic. That’s it. No DNAT, no PAT, just basic routing and filtering.
Using UFW to allow traffic
UFW (Uncomplicated Firewall) works with IPv6 out of the box – if you turn it on for v6:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Make sure UFW is configured to manage IPv6:
Open /etc/ufw/ufw.conf and set:
IPV6=yes
Then reload:
sudo ufw reload
Check what’s open
To verify what’s allowed:
sudo ufw status
You should see both 80 and 443 allowed on “Anywhere (v6)”.
Router configuration for IPv6
Some routers support IPv6 firewall rules separately. If yours is one of them:
- Log in to your router’s admin panel.
- Look for “IPv6 Firewall” or “IPv6 Filtering”.
- Allow inbound connections to the Pi’s IPv6 address on ports 80 and 443.
Watch for ISP-level filtering
Some ISPs block incoming IPv6 traffic unless you ask them nicely (or pay extra). Test this by trying to access your Pi from an external IPv6 device. If it fails and everything looks good on your end, call your ISP. They love those conversations.
Using Dynamic DNS with IPv6
What if your IPv6 changes every Tuesday at 3 a.m.?
Not everyone gets a static IPv6 prefix from their ISP. Some rotate them randomly, some change them on every reconnect, and some just like watching you suffer. If your Raspberry Pi’s public IPv6 address changes, your AAAA records become useless. This is where Dynamic DNS (DDNS) saves the day.
What Dynamic DNS actually does
It updates your DNS records automatically when your IP address changes. Most DDNS providers support IPv4 by default, but not all support IPv6, so check first.
Good IPv6-compatible DDNS providers
- DuckDNS (free, supports IPv6, works great with scripts)
- No-IP (supports IPv6 but might require manual updates)
- Dynu (supports IPv6 with their API)
- Cloudflare (use API scripts to update AAAA records directly)
Set up DuckDNS with IPv6
- Create an account and subdomain at duckdns.org.
- Use a script like this in a cron job:
curl "https://www.duckdns.org/update?domains=yourdomain&token=yourtoken&ipv6=your_ipv6_address"
Replace your_ipv6_address with the current one using a command like:
ip -6 addr show eth0 | grep 'global' | awk '{print $2}' | cut -d/ -f1
Put it in a shell script, make it executable, and run it hourly:
crontab -e
Add:
0 * * * * /home/pi/update_duckdns_ipv6.sh >/dev/null 2>&1
Cloudflare DDNS via API for IPv6
You can also use the Cloudflare API to update your AAAA record. Scripts like cloudflare-ddns on GitHub can automate this.
Test it works
Run the script manually and use dig to see if your AAAA record updates correctly:
dig AAAA yourdomain.duckdns.org
If the result changes when your Pi’s IPv6 does, you’re golden.
Securing Services with SSL
Plain HTTP is for people who enjoy living dangerously
If you’ve set up a reverse proxy and exposed it to the internet – even over IPv6 – you’re gonna want SSL. Without it, everything’s flying around unencrypted, and that’s just asking for some script kid to sniff your credentials. Encrypt your traffic. It’s free. It’s easy. There’s no excuse not to.
Use Let’s Encrypt with Certbot
Let’s Encrypt gives you free SSL certificates. Certbot handles the mess of generating, renewing, and installing them. Nginx works smoothly with it, especially when DNS is set up right.
Install Certbot:
sudo apt install certbot python3-certbot-nginx
Then run:
sudo certbot --nginx -d yourdomain.example.com
This does three things:
- Gets a cert
- Configures Nginx for HTTPS
- Sets up auto-renewal
Force HTTPS
Make sure HTTP traffic gets redirected to HTTPS. Add this server block to your config:
server {
listen [::]:80;
server_name yourdomain.example.com;
return 301 https://$host$request_uri;
}
That way, even if someone forgets the “s” in HTTPS, they’re still protected.
Automatic certificate renewal
Certbot sets up a renewal timer by default, but double-check:
sudo systemctl list-timers | grep certbot
You should see something that runs twice a day.
Handling multiple services
Each domain or subdomain gets its own certificate. You can re-run Certbot for each:
sudo certbot --nginx -d service1.example.com -d service2.example.com
Certbot will create a certificate that covers both.
If using a custom DDNS domain
Some free DDNS providers don’t support Let’s Encrypt’s HTTP challenge. In that case, use the DNS challenge, which requires API access to your DNS provider. Cloudflare is great for this.
Monitoring and Maintaining IPv6 Services
It worked yesterday — famous last words
Once your Raspberry Pi is running services over IPv6 with a reverse proxy and proper DNS, you might be tempted to call it a day. Don’t. IPv6 behaves differently from IPv4 in ways that can sneak up on you. Keep an eye on things unless you enjoy surprise downtime.
Watch network activity
Use iftop or vnstat to monitor live traffic. These show what’s using bandwidth, and whether anything suspicious is coming in over IPv6:
sudo apt install iftop
sudo iftop -i eth0
For long-term stats:
sudo apt install vnstat
vnstat -u -i eth0
vnstat -i eth0
Check services with systemctl
Make sure the services behind your reverse proxy are actually running:
sudo systemctl status nginx
sudo systemctl status home-assistant
sudo systemctl status pihole-FTL
Look out for expiring SSL certs
Let’s Encrypt certificates last 90 days. Certbot should renew them automatically, but run a dry-run renewal monthly just to be safe:
sudo certbot renew --dry-run
DNS verification tools
DNS misconfiguration breaks everything. Use dig or host:
dig AAAA yourdomain.com
host yourdomain.com
Make sure it resolves to your IPv6 and not some expired address from last month.
Automate config backups
Services like Nginx, Pi-hole, and DNS setups change over time. Back up /etc/nginx, /etc/pihole, and /etc/systemd/system with something like rsync or a cron job.
Check firewall rules regularly
UFW can lose its mind after updates. Double-check allowed ports:
sudo ufw status verbose
If your IPv6 rules vanish, reapply them manually or use a script.
Log alerts when things go south
Set up log monitoring tools or push notifications for service failures. A simple systemd timer and script combo can email you when Nginx fails to start.
FAQ
Q: Why does my Raspberry Pi have multiple IPv6 addresses?
A: That’s normal. IPv6 assigns multiple addresses including link-local, temporary, and stable private ones. Only use your global unicast address in DNS records.
Q: Can I use IPv6 without a reverse proxy?
A: Sure, but you’ll have to open individual ports and configure SSL manually for each service. A reverse proxy makes management easier.
Q: Do I still need Dynamic DNS with IPv6?
A: Yes, if your ISP changes your IPv6 prefix regularly. DDNS updates your DNS records when your public IP changes, keeping your services reachable.
Q: Why aren’t my AAAA records resolving?
A: Check DNS propagation, record spelling, and if your domain provider supports IPv6. Use dig or nslookup to confirm the record exists.
Q: Is my ISP blocking inbound IPv6?
A: Some do. Test externally or ask support. You may need to enable IPv6 port access on your router or request a public prefix.

