This easy to follow tutorial describes how to host a website on the Hetzner Cloud with Debian or Ubuntu Linux using an IPv6 address. It includes examples for both the nginx and apache2 webserver. It also explains how to create DNS records and how to generate a SSL certificate with letsencrypt.
Please contact us if anything is not clearly described, does not work, seems incorrect or if you require support.
Temporarily Assigning Multiple Individual IPv6 Addresses for Testing
Temporarily Making the Server Reachable for All IPs in the Subnet
Permanently Assigning Individual Single IPv6 Addresses (Reboot Safe)
Setting up IPv6 DNS AAAA Records in the Hetzner DNS Management WebUI
Generating a Letsencrypt Certificate for an IPv6 / AAAA Domain
Configuring the Nginx Webserver for Listening on IPv6 Addresses
Configuring the Apache2 Webserver for Listening on IPv6 Addresses
Before starting with IPv6 you should check if your Internet Service Provider (ISP) supports IPv6. You can do so by simply accessing ipv6.google.com. This is the google search page, but via IPv6 instead of IPv4. If this works for you, you can continue reading the article. If it does not, complain to your ISP to setup IPv6 or start using a VPN like mullvad.net, which will allow you to access IPv6.
At the time of this writing (April 2024), about half the internet supports IPv6, with France and Germany having the best coverage at about 75%, the US only has about 45%.
For hosting public websites, these aren't exactly shining numbers, but for hosting websites behind Content Delivery Networks (CDN) like cloudflare, this is irrelevant, as cloudflare supports IPv6 and can access your backend webserver at its IPv6 address.
At Blunix, we also like to exclusively assign IPv6 addresses to servers that are not meant to be publicly accessible, but only via an internal company VPN. For these company-internally used servers we use a (cost-free) IPv6 address for administrative purposes, with all WebUIs and other services only being accessible over the company internal VPN.
Create a new server and make sure IPv6 is enabled. You can select to only use an IPv6 address for your server to save a bit of money. For this blog post, we selected to not assign an IPv4 address to the server. Pretty much all modern computers and mobile devices understand IPv6 by now, so you don't really need an IPv4 address anymore.
After creating the cloud or physical server, the Hetzner Cloud WebUI shows you the IPv6 subnet that was assigned to the server. At the time of this writing, each server is assigned a /64 subnet. Fun fact: a /64 IPv6 subnet contains 18.45 quintillion, or 18.446.744.073.709.551.616, or 18.45 * 10^18 usable IPv6 addresses. This should be enough to host a couple of websites. Refer to the ripe net documentation on understanding IP addressing and CIDR charts for additional information.
If your subnet is 2a01:4f8:1c1c:dd6::/64
, then you can connect to the Hetzner Cloud server via SSH by adding a 1 at the end:
To quickly add a new IPv6 address to your server, use the following command:
To check if this is working, run the following command from your workstation:
You can choose any address between the minimum and maximum value in the assigned subnet. Remember that IPv6 addresses are written hexadecimal values: "0-9" and "a-f".
Lowest possible address: 2a01:4f8:1c1c:dd6:0000:0000:0000:0000
Largest possible address: 2a01:4f8:1c1c:dd6:ffff:ffff:ffff:ffff
More examples:
If you only use ip address add
, the configuration will be lost after the next reboot. Refer to the next section on how to make those configurations reboot safe.
You can also make the server listen to every IP address in the whole /64 subnet like this:
The IPv6 address that your server was accessible for after its creation is configured in /etc/network/interfaces.d/50-cloud-init
:
Copy the relevant section and create a new file /etc/network/interfaces.d/99-custom
. Add a configuration block like this for each IPv6 address you want the server to be reachable at. Note that if you configure a whole subnet, the server will not be reachable for all IPs in the subnet - the block above will only make the server reachable at 2a01:4f8:1c1c:dd6::1
, not at every IP in 2a01:4f8:1c1c:dd6::1/64
! Refer to this serverfault.com thread for additional information.
Remember that the normalized version of 2a01:4f8:1c1c:dd6::1
is 2A01:04F8:1C1C:0DD6:0000:0000:0000:0001
, and that specifying a /128 subnet in IPv6 is like specifying a /32 subnet in IPv4 - it refers to a single IP address.
To apply changes to configuration files below /etc/network/interfaces.d/
execute the following command. This will restart your servers network and the server will be unreachable for about two to three seconds. In this time your SSH session will hang, which is normal.
To create a reboot safe configuration for making the server reachable at all IPs in the subnet, simply add the route commands at the bottom:
For each IPv6 address you want to use for hosting a website, or any other service for that matter, configure one AAAA type DNS records. In Hetzners DNS Management WebUI, this can be done like this:
To verify that you setup the DNS record correctly you can simply ping the new domain. Make sure to use ping6
instead of ping
:
You can also setup /etc/hosts records for IPv6 addresses using the exact same way you would setup IPv4 records:
There is nothing special to generating Letsencrypt certificates for IPv6 domains. In the following example we automatically agree to the terms of service (--agree-tos
), spin up a standalone webserver (--standalone
) and refuse to provide an email address, which only means that we will not be notified by email two weeks before the domain expires (--register-unsafely-without-email
).
The following example nginx vhost is configured to serve SSL for all of the 18.45 quintillion IPv6 addresses. Keep in mind that we have to create AAAA DNS Records for each IP, as well as configure them in /etc/network/interfaces.d/
as described above for the IP to work.
The following SSL configuration is only an example - to create a proper configuration, take a look at the mozilla SSL configuration generator for webservers. The following commands let you determine your nginx and openssl version:
Here is the nginx vhost configuration for hosting a website using an IPv6 address on Hetzner Cloud. Notice the two listen directives, one for localhost (127.0.0.1) and one for one or all possible IPv6 addresses:
To configure Nginx to only listen on a specific IPv6 address simply state the address in brackets:
When using public IP addresses, there is no good usecase for this. Normally, you should only use one public IP per website. However when using a public as well as an internal (private) IP, this can make sense.
In order for nginx to make a vhost available on multiple IP addresses, you can simply specify multiple listen directives to configure one Nginx Vhost to be available on multiple IPv6 addresses:
Nginx does not provide an option to listen on a whole subnet, IPv4 or IPv6. You can configure Nginx to listen on all incoming IPv6 traffic like this:
And then simply use your firewall to allow only the addresses you wish to let through.
The following example apache2 vhost is configured to serve SSL for all of the 18.45 quintillion IPv6 addresses. Keep in mind that we have to create AAAA DNS Records for each IP, as well as configure them in /etc/network/interfaces.d/
as described above for the IP to work.
The following SSL configuration is only an example - to create a proper configuration, take a look at the mozilla SSL configuration generator for webservers. The following commands let you determine your apache2 and openssl version:
First enable the apache2 ssl and headers modules:
Here is the apache2 vhost configuration for hosting a website using an IPv6 address on Hetzner Cloud. Notice the two listen directives, one for localhost (127.0.0.1) and one for one or all possible IPv6 addresses:
To configure Apache2 to only listen on a specific IPv6 address simply state the address in brackets:
While you can specify multiple IPv4 or IPv6 addresses in /etc/apache2/ports.conf
like so:
But you are still limited to one single IP in each VirtualHost configuration:
Which you can set to listen on all possible IPs:
From there you have to use the ServerName
directive to sort the incoming requests:
Apache2 does not provide an option to listen on a whole subnet, neither IPv4 nor IPv6. You can configure Apache2 to listen on all incoming IPv6 traffic like this:
And then simply use your firewall to allow only the addresses you wish to let through.
Your website should now be configured and reachable over its IPv6 address(es). Time to check if it is working correctly.
Simply enter the URL in your browser:
You can also use curl
to check if the website is online:
Are you looking for
Linux Emergency Support,
Linux Consulting for Projects,
Linux Managed Hosting,
Qubes OS Consulting and Support or
Linux Trainings and Workshops?