Docker – How to setup Jupyter behind Nginx proxy

Andrei Maksimov

Andrei Maksimov

0
(0)

This article describes how to configure Jupyter behind Nginx reverse proxy, as such configuration is not very obvious for many people. There are many issues and gists on GitHub, and it is tough to choose the right solution for this problem. Also, you may find many different articles describing how to do it, but most of them are outdated and not covering CORS configuration well.

As the topic is still trendy, I decided to renew the article and simplify the configuration.

Thanks for your comments below, Andrew Barker. Catch the updated version of the article.

JupyterHub configuration

First, let’s create a folder where we’ll put all our configuration files.

Here’s our final structure:

mkdir docker
tree docker

docker
├── docker-compose.yaml
├── jupyter_notebook_config.py
└── nginx.conf

Docker Compose

To simplify everything, I created docker-compose.yaml, which describes our services:

version: "3.7"
services:
  nginx:
    image: nginx:alpine
    volumes:
      - "./nginx.conf:/etc/nginx/nginx.conf:ro"
    ports:
      - 8080:8080
    links:
      - "jupyterhub"
  jupyterhub:
    image: jupyterhub/jupyterhub
    container_name: jupyterhub
    volumes:
      - "./jupyter_notebook_config.py:/root/.jupyter/jupyter_notebook_config.py:ro"

The configuration is straightforward – a simple small Nginx Docker container in front of Jupyterhub.

Both launched from their latest versions.

Nginx configuration

Nginx is sitting on port 8080 and listening on port 8080 as well.

VERY IMPORTANT: nginx.conf contains the reverse proxy configuration.

If your Nginx is sitting on a port other than 80 or 443, you need to use the following configuration directive:

  • proxy_set_header Host $host:$server_port;

For Nginx, which is sitting at default ports, use default configuration:

  • proxy_set_header Host $host;

If you make a mistake here, you’ll start receiving Blocking Cross Origin API request for /api/contents error messages.

Again, the primary reason for these messages is not equal ports for service binding and export for Nginx containers.

Here’s my nginx.conf for listening on port 8080:

worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    upstream ml {
        server jupyterhub:8000;
    }

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

    server {
        listen 8080;

        location / {
            proxy_pass http://ml;

            proxy_redirect   off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # websocket headers
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }

}

Jupyter configuration

For JupyterHub configuration, we’ll use the following configuration placed to /root/.jupyter/jupyter_notebook_config.py:

# get the config object
c = get_config()
# JupyterHub listen address
c.JupyterHub.hub_ip = '0.0.0.0'
# JupyterHub connect hostname.
# In our case it's 'jupyterhub' by the name of the service from docker-compose.yaml
c.JupyterHub.hub_connect_ip = 'jupyterhub'
# Location for users notebooks
c.Spawner.notebook_dir = '~/'

Launching Jupyrter behind Nginx Proxy

I did not have too much time to build a new container and/or play with user authentication settings. The main purpose is to provide a solution for Blocking Cross Origin API request for /api/contents issues.

So, here’s how you may launch this configuration:

docker-compose up -d

Connect to just launched containers and create a user and install notebook package:

docker exec -it jupyterhub /bin/bash

adduser 

pip install notebook

Now you may connect to JupterHub and use your created username and password as login credentials.

JupyterHub-behind-Nginx-proxy

Summary

I hope this small note will help you to save some time. If you found it useful, please, help spread it to the world!

Stay tuned!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

As you found this post useful...

Follow us on social media!

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Subscribe to our updates

Like this article?

Share on facebook
Share on Facebook
Share on twitter
Share on Twitter
Share on linkedin
Share on Linkdin
Share on pinterest
Share on Pinterest

Want to be an author of another post?

We’re looking for skilled technical authors for our blog!

Leave a comment

If you’d like to ask a question about the code or piece of configuration, feel free to use https://codeshare.io/ or a similar tool as Facebook comments are breaking code formatting.