Skip to content

Nginx Configuration

The next step is to modify the Nginx config file so that Nginx uses our SSL certificates and routes requests on to JupyterHub.

The Nginx configuration step was the hardest part for me when I set up the first JupyterHub server. The Nginx config file isn't Python code or a bash script. I went through many different configurations until I finally got one that worked.

The big initial problem was that I copied the sample Nginx config that's up on the JupyterHub docs. But the Nginx config posted on the JupyterHub docs is not a complete Nginx config, it contains just the server portion. I didn't know that the whole server portion needed to be enclosed in another frame.

Determine number of cores and core limitations

According to this tutorial from Digital Ocean, you can determine the number of cores running on your server and what the limits of those cores are with a couple commands. Information about our server's cores will be put in the Nginx configuration later.

The first command determines the number of cores on the server:

$ grep processor /proc/cpuinfo | wc -l

If this command returns 1 then we have 1 core on your server. Then 1 is the number of worker_processes to set in our Nginx configuration

The second command determines the core's limitations:

$ ulimit -n

If this command returns 1024, then that's the number of worker_connections we should set in our Nginx configuration.

Based on the results of these two commands, we will modify the top of the nginx.conf file

worker_processes 1;
worker_connections 1024;

Modify nginx.conf

To modify nginx.conf, cd into the /etc/nginx directory. The nginx.conf file should be there along with a couple other files and directories.

$ cd /etc/nginx
$ ls
conf.d          koi-utf     nginx.conf    sites-available  ssl
fastcgi.conf    koi-win     proxy_params  sites-enabled    uwsgi_params
fastcgi_params  mime.types  scgi_params   snippets         win-utf
$ sudo nano nginx.conf

At the top of the file, paste in our information about worker_processes and worker_connections

user www-data;
worker_processes 1;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 1024;
        # multi_accept on;
}
...

Close the configuration file (using [Ctrl]+[x], [y]) and restart Nginx. Check the status to make sure Nginx is active

$ sudo systemctl restart nginx
$ sudo systemctl status nginx

You should see that Nginx is activate and running.

Next we will add a map block to the nginx.conf file to ensure http connections are upgraded. In nginx.conf find the lines:

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

Replace the block above with the block below:

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

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

Save the nginx.conf file and restart Nginx again and check the status:

$ sudo systemctl restart nginx
$ sudo systemctl status nginx

Nginx should be running fine with no errors.

Modify sites-available

According to this tutorial and [this tutorial], we should modify the sites-available file and create a symbolic link to the sites-enabled file. The nginx.conf file shows that it will include /etc/nginx/sites-enabled/*.

Open a new file in /etc/nginx/sites-available called jupyterhub

$ sudo nano /etc/nginx/sites-available/jupyterhub

Paste the following server blocks into the file:

# /etc/nginx/sites-available/jupyterhub

    # All regular http requests on port 80 become SSL/HTTPS requests on port 32
    server {
        listen 80;
        server_name mydomain.org;
        # Tell all requests to port 80 to be 302 redirected to HTTPS
        return 302 https://$host$request_uri;
    }

    server {
        #listen 443 ssl default_server;
        listen 443;
        ssl on;
        # !!! make sure to change to your domain name !!!
        server_name engr101lab.org;
        ## SSL Protocals
        ssl_certificate /etc/letsencrypt/live/engr101lab.org/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/engr101lab.org/privkey.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_dhparam /srv/jupyterhub/dhparam.pem;
        # Make site accessible from http://localhost/ server_name localhost; certs sent to the client in SERVER HELLO are concatenated in
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;
        ssl_stapling on;
        ssl_stapling_verify on;
        # modern configuration. tweak to your needs.
        ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
        # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
        add_header Strict-Transport-Security max-age=15768000;

        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-NginX-Proxy true;
            #proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
        location ~ /.well-known {
            allow all;
        }
    }

Save and close the `/etc/nginx/sites-available/jupyterhub file.

Now that we have a jupyterhub server configuration in the sites-available directory, we need to create a symbolic link to the sites-enabled directory so the server blocks we wrote will be run by Nginx.

To create the symbolic link, use the command below:

$ sudo ln -s /etc/nginx/sites-available/jupyterhub /etc/nginx/sites-enabled

Test out the new Nginx configuration

OK, fingers crossed... Now we are going to check if our Nginx configuration is valid and works correctly. First we can check for configuration errors with the command below:

$ sudo nginx -t

What we are looking for is output like below. If there are any errors, you need to go back and trouble shoot the Nginx configuration.

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Now restart Nginx and take a look at the status.

$ sudo systemctl restart nginx
$ sudo systemctl status nginx
# [Ctrl]+[c] to exit

We are looking for something like:

   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2019-02-07 00:40:10 UTC; 7s ago

Now browse to the domain name we added to the server. If you use http://mydomain.org you should be re-directed to https://mydomain.org.

Since JupyterHub isn't hooked up to Nginx yet, you should see a 502 Bad Gateway Error. But that error should show nginx in the error text.

502_bad_gateway

Summary

In this section we modified the nginx.conf file and created a server configuration in /etc/nginx/sites-available. Then we created a symbolic link betweeefile in sites-available to sites-enabled

Then we checked the Nginx configuration was valid and restarted Nginx.

Next Steps

The next step configure JupyterHub by creating and modifying a jupyterhub_config.py file.