DEV Community

jagadeesh k s
jagadeesh k s

Posted on

Exposing services behing CGNAT | My home-Lab experience with Jio-Fiber

Escaping CGNAT: Exposing Self-Hosted Services over IPv6 (No Public IPv4 Required)

I’ve just begun my self-hosting journey.

I repurposed an old laptop and installed Ubuntu Server on it. On top of
that, I spun up several services using Docker:

  • Nextcloud – Personal cloud storage
  • Vaultwarden – Lightweight password manager
  • Pi-hole – DNS-level ad blocking
  • Jellyfin – Media streaming server

I installed Ubuntu Server, configured Docker, and had everything running
in a single day. It was smooth, fun, and surprisingly satisfying.

Then came the final step: exposing my services to the public internet.

That’s when things got complicated.


The Problem: CGNAT

My ISP (Jio Fiber) uses CGNAT (Carrier-Grade NAT).

That means:

  • My router does not get a public IPv4 address.
  • Traditional port forwarding is impossible.
  • Devices on the public internet cannot directly reach my server.

In simple terms: I had no public “return address”.


Options I Considered (and Why I Avoided Them)

I explored a few alternatives, but each came with trade-offs:

  • Buy a Public IPv4 from ISP
    → Extra cost and additional hassle

  • Use a VPN (Tailscale, WireGuard, etc.)
    → Friends and family outside my network would need client setup

  • Use a VPS or reverse proxy (e.g., Cloudflare Tunnel)
    → Introduces dependency and potential privacy concerns

I wanted something simpler and more native.

That’s when I rediscovered IPv6.


Why IPv6 Solves This

CGNAT exists because IPv4 addresses are exhausted.

IPv6 does not have this limitation.

With IPv6:

  • Every device can have a globally routable address
  • NAT is not required
  • Port forwarding is unnecessary
  • Direct end-to-end connectivity is restored

Tactical advantages I observed:

  • ✅ No port forwarding required
  • ✅ Reduced exposure to IPv4 bot noise
  • ✅ Typically stable addressing (or use dynv6 if dynamic)

Here’s exactly what I did.


Step 1: Verify IPv6 Connectivity

What this does

Checks whether your ISP provides IPv6.

ip -6 addr show

Look for a global IPv6 address (starts with 2xxx: or 3xxx:).
Ignore fe80:: — those are link-local addresses.

ping6 -c 4 google.com

Validation

If ping6 succeeds → IPv6 works.

If not → Contact your ISP and ask them to enable IPv6.


Step 2: Enable IPv6 in Netplan

What this does

Configures Ubuntu Server to accept IPv6 router advertisements and
DHCPv6.

Edit your Netplan configuration:

sudo nano /etc/netplan/00-installer-config.yaml

Example configuration:

    network:
      version: 2
      ethernets:
        eno1:  # Replace with your interface name
          addresses:
            - 192.168.29.100/24
          routes:
            - to: default
              via: 192.168.29.1
          nameservers:
            addresses: [8.8.8.8, 8.8.4.4]
          dhcp6: true
          accept-ra: true
Enter fullscreen mode Exit fullscreen mode

Apply changes:

sudo netplan apply

Validation

curl -6 ifconfig.co

If this returns an IPv6 address, you’re good to go.


Step 3: Configure Firewall Rules (Critical)

With IPv6, your server is globally reachable.

That makes firewall configuration extremely important.

Using UFW:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

That’s it. I only exposed HTTP and HTTPS.

⚠️ Do not expose unnecessary ports.
⚠️ I did NOT expose Pi-hole publicly.

Everything runs behind HTTP/HTTPS via reverse proxy.


Step 4: Configure Nginx to Listen on IPv6

Inside your server block:

    server {
        listen 80;
        listen [::]:80;

        server_name example.com;

        location / {
            proxy_pass http://172.18.0.2:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
Enter fullscreen mode Exit fullscreen mode

For HTTPS:

    server {
        listen 443 ssl;
        listen [::]:443 ssl;

        server_name example.com;

        ssl_certificate /etc/ssl/certs/fullchain.pem;
        ssl_certificate_key /etc/ssl/private/privkey.pem;

        location / {
            proxy_pass http://172.18.0.2:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
Enter fullscreen mode Exit fullscreen mode

The important part:

listen [::]:80;
listen [::]:443 ssl;

That ensures Nginx accepts IPv6 connections directly.


Step 5: Forwarding to Docker Containers

My applications run inside Docker.

Docker uses private IPv4 subnets internally (e.g., 172.18.0.0/16).

To find the container IP:

docker inspect <container_name> | grep IPAddress

Nginx (host) proxies traffic to that internal Docker IP.

Everything up to this point happens on Ubuntu Server.

Next: DNS and router configuration.


Step 6: DNS Configuration (AAAA Record)

To make the service reachable:

Add an AAAA record pointing to your IPv6 address.

Example:

mydomain.dynv6.net → AAAA → 2001:db8:abcd::1234

I used dynv6 because it’s simple and works well for dynamic IPv6
addresses.

Now clients accessing the domain will connect via IPv6.


Step 7: Jio Router IPv6 Firewall Rules

Before opening access:

  • Use strong firewall rules
  • Install Fail2ban
  • Keep services updated
  • Use HTTPS only
  • Consider rate limiting

Login to your router admin page:

http://192.168.29.1

Go to:

Security → Firewall → IPv6 Firewall Rules

Add rules only for the ports you want (80 and 443).

⚠️ The more ports you expose, the larger your attack surface.


Final Result

Once configured:

  • Your services are accessible from any IPv6-enabled network
  • No public IPv4 needed
  • No CGNAT workaround
  • No VPS
  • No port forwarding

Just clean, native IPv6 connectivity.


Closing Thoughts

Self-hosting taught me:

  • Networking fundamentals matter
  • IPv6 is underused but powerful
  • CGNAT is frustrating — but not unbeatable

If you’re stuck behind CGNAT, try IPv6 before paying for a VPS.

If you’ve faced similar pain points, drop them in the comments — it
might help someone else.

Good day! 🚀

Top comments (0)