Nginx configuration

Although it's possible to use Centrifugo without any reverse proxy before it, it's still a good idea to keep Centrifugo behind mature reverse proxy to deal with edge cases when handling HTTP/Websocket connections from the wild. Also you probably want some sort of load balancing eventually between Centrifugo nodes so that proxy can be such a balancer too.

In this section we will look at Nginx configuration to deploy Centrifugo.

Minimal Nginx version – 1.3.13 because it was the first version that can proxy Websocket connections.

There are 2 ways: running Centrifugo server as separate service on its own domain or embed it to a location of your web site (for example to /centrifugo).

separate domain for Centrifugo

upstream centrifugo {
    # Enumerate all upstream servers here
    #sticky;
    ip_hash;
    server 127.0.0.1:8000;
    #server 127.0.0.1:8001;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

#server {
#    listen 80;
#    server_name centrifugo.example.com;
#    rewrite ^(.*) https://$server_name$1 permanent;
#}

server {

    server_name centrifugo.example.com;

    listen 80;

    #listen 443;
    #ssl on;
    #ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    #ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
    #ssl_certificate /etc/nginx/ssl/wildcard.example.com.crt;
    #ssl_certificate_key /etc/nginx/ssl/wildcard.example.com.key;
    #ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1000;
    gzip_proxied any;

    # Only retry if there was a communication error, not a timeout
    # on the Tornado server (to avoid propagating "queries of death"
    # to all frontends)
    proxy_next_upstream error;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header Host $http_host;

    location /connection {
        proxy_pass http://centrifugo;
        proxy_buffering off;
        keepalive_timeout 65;
        proxy_read_timeout 60s;
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    location / {
        proxy_pass http://centrifugo;
    }

    error_page   500 502 503 504  /50x.html;

    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

If you want to use web interface then you should also add /socket location to handle admin websocket connections:

    location /socket {
        proxy_pass http://centrifugo;
        proxy_buffering off;
        keepalive_timeout 65;
        proxy_read_timeout 60s;
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

embed to a location of web site

upstream centrifugo {
    # Enumerate all the Tornado servers here
    #sticky;
    ip_hash;
    server 127.0.0.1:8000;
    #server 127.0.0.1:8001;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {

    # ... your web site Nginx config

    location /centrifugo/ {
        rewrite ^/centrifugo/(.*)        /$1 break;
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://centrifugo;
    }

    location /centrifugo/socket {
        rewrite ^/centrifugo(.*)        $1 break;

        proxy_next_upstream error;
        proxy_buffering off;
        keepalive_timeout 65;
        proxy_pass http://centrifugo;
        proxy_read_timeout 60s;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    location /centrifugo/connection {
        rewrite ^/centrifugo(.*)        $1 break;

        proxy_next_upstream error;
        gzip on;
        gzip_min_length 1000;
        gzip_proxied any;
        proxy_buffering off;
        keepalive_timeout 65;
        proxy_pass http://centrifugo;
        proxy_read_timeout 60s;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

}

sticky

You may be noticed commented sticky; directive in nginx upstream configuration.

When using SockJS and client connects to Centrifugo - SockJS session created - and to communicate client must send all next requests to the same upstream backend.

In this configuration we use ip_hash; directive to proxy clients with the same ip address to the same upstream backend.

But ip_hash; is not the best choice in this case, because there could be situations where a lot of different browsers are coming with the same IP address (behind proxies) and the load balancing system won't be fair. Also fair load balancing does not work during development - when all clients connecting from localhost.

So the best solution would be using something like nginx-sticky-module which uses setting a special cookie to track the upstream server for client.

worker_connections

You may also need to update worker_connections option of Nginx:

events {
    worker_connections 40000;
}

upstream keepalive

See chapter about operating system tuning for more details.