Nginx listen ipv6 quirks

2015-12-11

Most of 'our' nginx configs have something like:

server {
    listen 80 default_server;
}

maybe if we support more than one site we also have a few copies of:

server {
    listen 80;
}

laying around elsewhere

this all works fine, but it meant nginx is NOT listening on ipv6, and enabling ipv6 support is non-trivial.

The first attempt

first I tried changing every '80' to "[::]:80" which googling told me would support both:

server {
    listen [::]:80;
}

however this doens't work in a multi-site config like I use (having many entries in sites-enabled, each with their own server block):

nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)

A second attempt

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

gives us:

nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)

ahh, since '[::]:80' is for both ipv6 and ipv4, so nginx tries to bind with port 80 on the ipv4 ip twice.

A third attempt

googling tells me that 'ipv6only=on' is the magic sauce:

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;
}

server {
    listen 80;
    listen [::]:80 ipv6only=on;
}

which now gives me:

Restarting nginx: nginx: [emerg] duplicate listen options for [::]:80 in /etc/nginx/sites-enabled/foo:3

A fourth, and final, attempt

ahh so every listen ip:port combo can only be configured once, this makes sense for things like 'default_server', but I was honestly surprised it is for any config options.

This now leaves us with:

TL;DR

in nginx your default server should be:

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;
}

every other (non default) should be:

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

now nginx is happy!