by kleinee

kleinee / jns

jupyter notebook and lab on Raspberry Pi

220 Stars 59 Forks Last release: Not found MIT License 347 Commits 0 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

hardware os

python nodejs Julia R

jupyter piwheels

Jupyter Notebook & Lab Server on Raspberry Pi


Project Jupyter not only revolutionizes data-heavy research across domains - it also boosts personal productivity for problems on a much smaller scale. Due to openness it is an amazing platform for exploring concepts and learning new things.

I started setting up a Jupyter Notebook Server on a Raspberry Pi following this blog post by Arun Durvasula. Convinced of the potential of the platform I followed the development.

My personal exercise soon taught me a great deal about the underlying architcture. Given Jupyter's complexity, speed of growth and scale, it is remarkable that such a system runs fine on a Raspberry Pi.

This repository isn't really anything genuine: I owe big thanks to many contributors in the Jupyter, Raspberry Pi, Linux, Python, Julia and greater Open Source communities for providing all the beautiful building blocks - small and large.

What is new?

  • Rather than installing the latest version of Python as I did the past, I decided that the new version would use the latest Python 3 version supported in Raspbian - as of this writing Python 3.5.3.
  • Whilst this seems to be a step backwards, it is a in fact a giant step forward as you benefit from significant installation speedups made possible by the recently released piwheels project.
  • The scripts work across the entire range of Raspberry Pis - including the early models with just 256MB of memory.
  • Python support for GPIO, Sense HAT and PICAMERA is installed without the earlier worries of breaking things on system level.
  • All Python modules are pip installed into a virtual environment following advice found online: You should never use
    sudo pip install
    . Well I did this in the past and it certainly had me and users confused. We have to learn certain things the hard way to really appreciate the benefits of doing them right. It is worth reading up on this in this blogpost.
  • You now install Python packages into a virtual environment created with
    using a
    file. This really achieves a more maintainable setup, opens up more possibilities and hopefully makes this project more useful for the Raspberry Pi and Jupyter communities.
  • Python 3, Julia and Bash kernels are installed and configured during installation.
  • JupyterLab is installed and ready to use.

What do you need to follow along?

  • a Raspberry Pi model of your choice complete with micro-usb power-supply - I recommend a Raspberry Pi 3 but the setup should work across the entire range of Pi models, perhaps with the exception of the very early models that featured only 256MB of memory. I tested on a ZeroW, 1, 2 and 3.
  • a (micro) SD card with 16GB capacity or more to suit your Pi model with Raspbian Stretch Lite installed and configured to permit access via ssh as user pi.
  • an ethernet or wifi connection for the Pi
  • internet access on the Pi
  • a computer to carry out the installation connected to the same network as the Pi
  • LESS TIME THAN EVER BEFORE due to the recent release of piwheels. Users new to this project might argue that the setup is still time-consuming. Believe me: In the past 6 hours+ were not uncommon and installing the system on a Raspberry Pi 1 was not impossible but required quite some patience and time. Note that some packages listed in
    may not yet be available as Python wheels. Such packages are then built from source and this takes some time...


IMPORTANT NOTE on fresh installations

  • An increasing number of users seem to install on top of images that have 'nodejs' already installed.
  • The scripts in this repository were initially designed to work based on Raspbian Stretch Lite as a starting point with the intention to run the server headless in order to maximise memory available for data analysis.
  • One such starting point is the desktop version of Raspbian Stretch which comes with

    ) pre-installed.
    explained later now checks for the existence of
    and only installs it if not yet present on the system.
  • For the scripts to run properly on the desktop version of Raspbian or any other startingpoint with

    installed, it is necessary that
    is version 5 or higher !!!

  • If you start with a fresh Raspbian Stretch Desktop image, you can uninstall

    apt purge nodejs
    and then execute the scripts.

First boot with fresh SD card

  • ssh into your Raspberry Pi with the the fresh install of Raspbian Stretch Lite as user pi. Then run

    sudo raspi-config
    and set the memory split to 16MB, expand the file-system and set a new password for the user pi. When done, reboot and log in again via ssh.
  • If not yet present, install

sudo apt install -y git
  • To increase the size of swapfile to 2048MB run: ```bash sudo sed -i -e 's/CONFSWAPSIZE=100/CONF_SWAPSIZE=2048/' /etc/dphys-swapfile sudo /etc/init.d/dphys-swapfile stop sudo /etc/init.d/dphys-swapfile start ```

  • With preparations out of the way clone this repository into the home directory of user pi

git clone https://github.com/kleinee/jns
  • Change into the new directory
    just created with
cd ~/jns/scripts

Technically you can now run

sudo ./inst_jns.sh
which is the installer script that combines the steps described below. If you follow along I assume that you run all scripts from inside the directory

You might not want all features on your system. Feel free to edit `inst_jns.sh' to suit your requirements.

Install required Raspbian packages with apt

sudo ./prep.sh

A couple of packages from the Raspbian repository are required during installation and later for a some Python packages to work properly. The script just fetches these packages and installs them.

# script name:     prep.sh
# last modified:   2018/09/09
# sudo:            yes

script_name=$(basename -- "$0")

if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$script_name" exit 1 fi

apt update && apt -y upgrade apt -y install pandoc apt -y install libxml2-dev libxslt-dev apt -y install libblas-dev liblapack-dev apt -y install libatlas-base-dev gfortran apt -y install libtiff5-dev libjpeg62-turbo-dev apt -y install zlib1g-dev libfreetype6-dev liblcms2-dev apt -y install libwebp-dev tcl8.5-dev tk8.5-dev apt -y install libharfbuzz-dev libfribidi-dev apt -y install libhdf5-dev apt -y install libnetcdf-dev apt -y install python3-pip apt -y install python3-venv apt -y install libzmq3-dev apt -y install sqlite3

Install required Python 3 packages with pip

  • This creates a virtual Python 3 environment '/home/pi/.venv/jns' and activates it temporarily
  • It then updates
    to the latest version available from the Python package repository before it processes the
    file line by line.
  • This is a workaround to prevent
    from failing if one or more requirements listed fail to install.
# script name:     inst_stack.sh
# last modified:   2018/01/14
# sudo: no

script_name=$(basename -- "$0") env="/home/pi/.venv/jns"

if [ $(id -u) = 0 ] then echo "usage: ./$script_name" exit 1 fi

if [ ! -d "$venv" ]; then python3 -m venv $env fi

activate virtual environment

source $env/bin/activate

pip3 install pip==9.0.0 pip3 install setuptools pip3 install -U pip

cat requirements.txt | xargs -n 1 pip3 install

Configure Jupyter


With this script you generate a jupyter notebook configuration directory and in it a file called

that holds the configuration settings for your notebook / lab server. You also create a folder notebooks in the home directory of user
as the
for your server. In the configuration file, you apply the following changes:
  • tell jupyter not to sart a browser upon start - we access the server from a remote machine on the same network
  • set the IP address to '*'
  • set the port for the notebook server to listen to 8888
  • enable
    for rendering math in notebooks
  • set the notebook_dir to
  • use the password hash for the default server password

NOTE: This setup still uses password authentication. If you prefer token-based authentication, you have to change settings in the config file

. Documentation of possible configuration settings can be found here.

After the basic configuration the script activates the bash kernel and activates extensions for Jupyter Notebook and JupyterLab. At the JupyterLab end this requires intstallation of

followed by installation of the underlying JS infrastructure which is a bit time-consuming but ultimately allows you to use
and potentially other extensions.
# script name:     conf_jupyter.sh
# last modified:   2018/09/09
# sudo:            no

script_name=$(basename -- "$0") env="/home/pi/.venv/jns"

if [ $(id -u) = 0 ] then echo "usage: ./$script_name" exit 1 fi

activate virtual environment

source $env/bin/activate

generate config and create notebook directory

if notebook directory exists, we keep it (-p)

if configuration file exeists, we overwrite it (-y)

jupyter notebook -y --generate-config cd $home mkdir -p notebooks


set up dictionary of changes for jupyter_config.py

declare -A arr app='c.NotebookApp' arr+=(["$app.open_browser"]="$app.open_browser = False") arr+=(["$app.ip"]="$app.ip ='*'") arr+=(["$app.port"]="$app.port = 8888") arr+=(["$app.enable_mathjax"]="$app.enable_mathjax = True") arr+=(["$app.notebook_dir"]="$app.notebook_dir = '/home/pi/notebooks'") arr+=(["$app.password"]="$app.password = 'sha1:5815fb7ca805:f09ed218dfcc908acb3e29c3b697079fea37486a'")

apply changes to jupyter_notebook_config.py

for key in ${!arr[@]};do if grep -qF $key ${target}; then # key found -> replace line sed -i "/${key}/c ${arr[${key}]}" $target else # key not found -> append line echo "${arr[${key}]}" >> $target fi done

install bash kernel

python3 -m bash_kernel.install

install extensions

jupyter serverextension enable --py jupyterlab jupyter nbextension enable --py widgetsnbextension --sys-prefix jupyter nbextension enable --py --sys-prefix bqplot

activate clusters tab in notebook interface

/home/pi/.venv/jns/bin/ipcluster nbextension enable --user

install nodejs and node version manager n

if node is not yet installed

if which node > /dev/null then echo "node is installed, skipping..." else # install nodejs and node version manager n cd ~/jns # fix for issue #22 # install nodejs and node version manager n # see: https://github.com/mklement0/n-install curl -L https://git.io/n-install | bash -s -- -y lts fi

install jupyter lab extensions

bash -i ./inst_lab_ext.sh

The script

- introduced by @Kevin--R to fix issue#23 has the following content:
# script name:     inst_lab_ext.sh
# last modified:   2019/04/06
# sudo:            no

script_name=$(basename -- "$0") env="/home/pi/.venv/jns"

if [ $(id -u) = 0 ] then echo "usage: ./$script_name" exit 1 fi

. /home/pi/.bashrc . $env/bin/activate jupyter lab clean jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build jupyter labextension install bqplot --no-build jupyter labextension install jupyterlab_bokeh --no-build jupyter labextension install jupyter-leaflet --no-build jupyter lab build

Start and access your server

Activate the virtual environment

Since you used a virtual environment to install Python modules, you need to activate this environment before you can start your server:

source /home/pi/.venv/jns/bin/activate

The prompt will change to indicate successfull activation preceding

[email protected]:
with the envireonment name - in case pf this setup
. With hostname set to
it looks like this:
(jns) [email protected]:~ $

Before you proceed

After installation completes, you will still need to activate the change made to

when node was installed before doing anything that requires node.

You can be accomplish this by any of the following: - reboot - logout and log back in - call

. ~/.bashrc
from the command line

That's the reason for this warning during node installation:

  IMPORTANT: OPEN A NEW TERMINAL TAB/WINDOW or run `. /home/pi/.bashrc`
             before using n and Node.js.
You can see this by running the following commands after your installation completes:
[email protected]:~/jns $ echo $PATH
[email protected]:~/jns $ . ~/.bashrc
[email protected]:~/jns $ echo $PATH
[email protected]:~/jns $ 
If you look at your $PATH environment variable and see /home/pi/n/bin you are ready to use node.

Also note that if you uninstall node with

/home/pi/n/bin will remain in your $PATH environment variable until you reboot or logout and log back in.

Start the server

To start your server just type

jupyter notebook
jupyter lab

Access the server

To access your server form a webbrowser on a computer running on the same network as your Raspberry Pi, just open a browser and use the Pi's IP address / port 8888 as the url.


Change `xxx.xxx.xxx.xxx' to the IP address of the Raspberry Pi.


During the configuration the default password for the server was set to

. You can change this by typing:
(jns) [email protected]:~ $ jupyter notebook password
Enter password:  ****
Verify password: ****

Install TeX (optional)

sudo ./inst_tex.sh
  • TeX (and Pandoc) are used under the hood to convert Jupyter notebooks to other formats including PDF.
  • Whilst not strictly necessary if no PDF export is rquired, I still recommend to run this step.
# script name:     inst_tex.sh
# last modified:   2018/03/11
# sudo:            yes

script_name=$(basename -- "$0")

if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$script_name" exit 1 fi

#------------------------------------------------------ apt install -y texlive-xetex apt install -y latexmk #------------------------------------------------------

Install Julia and the IJulia kernel (optional)

  • Julia is a relatively new high-level, high-performance dynamic programming language for numerical computing trying to combine the ease of Python with the speed of C. Thanks to the efforts of the Raspberry Pi community

    Julia 0.6.0
    is available in the Raspbian Stretch Repository. It is really worth a try as the language is a rising star in scientific computing.
  • IJulia is the kernel required for Jupyter Notebook / JupyterLab. Backgroud information on Julia on the Raspberry Pi can be found here.

Alternative 1: Julia 1.1.0 (RECOMMENDED)

sudo ./inst_julia-1.1.0.sh
  • NOTE tha the installer assumes that *** Julia IS NOT yet installed***. If it is and you want to proceed with installing Julia-1.1.0, I suggest to remove the existing Julia installation before you proceed.

  • Julia 1.1.0
    binaries were cross-compiled by Mr Satoshi Terasaki (for more information follow this link) and Mr Satoshi thankfully hosts his binaries here.
  • We need to download a large file from Google Drive and need to install the right binary based on the CPU architecture. The download contains binaries for both architectures.

The Download Helper

As per comments in the script this is literally a 1:1 copy of code found on stack overflow - adjustments were only necessary to set the FILE_ID and the DESTINATION as required in the context of this repository. The helper is called by the installer script and is not meant to be exceuted manually.



last modified 2019/05/26


Python helper script to download Julia 1.1.0 binaries

not meant to be executed manually



FILE_ID = '1fj6pNAJgmUD7bsSXqh8ocC1wESx8jkRh' DESTINATION = './julia-1.1.0-arm32bit.zip'

import requests

def download_file_from_google_drive(id, destination): URL = "https://docs.google.com/uc?export=download"

session = requests.Session()

response = session.get(URL, params = { 'id' : id }, stream = True)
token = get_confirm_token(response)

if token:
    params = { 'id' : id, 'confirm' : token }
    response = session.get(URL, params = params, stream = True)

save_response_content(response, destination)    

def get_confirm_token(response): for key, value in response.cookies.items(): if key.startswith('download_warning'): return value

return None

def save_response_content(response, destination): CHUNK_SIZE = 32768

with open(destination, "wb") as f:
    for chunk in response.iter_content(CHUNK_SIZE):
        if chunk: # filter out keep-alive new chunks

if name == "main": file_id = FILE_ID destination = DESTINATION download_file_from_google_drive(file_id, destination)

The Installer

Note that the code assumes that julia is not present.

  • installs necessary dependencies as suggested by Mr Terasaki
  • uses the downlaod helper to downlaod his binaries
  • detects the CPU architecture and installs the matching julia binary
  • creates a soft link in
  • installs the the IJulia kernel
# script name:     inst_julia-1.1.0.sh
# last modified:   2019/05/26
# sudo:            yes

SCRIPT_NAME=$(basename -- "$0") JNS_USER='pi' HOME_DIR="/home/$JNS_USER" ENV="$HOME_DIR/.venv/jns"


if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$SCRIPT_NAME" exit 1 fi


apt install dependencies

# apt install -y build-essential apt install -y libatomic1 apt install -y gfortran apt install -y perl apt install -y wget apt install -y m4 apt install -y cmake apt install -y pkg-config apt install -y libopenblas-base libopenblas-dev apt install -y libatlas3-base libatlas-base-dev apt install -y liblapack-dev apt install -y libmpfr-dev libgmp3-dev apt install -y libgfortran3


download and install julia based on architecture

# su pi <<one cd . unzip architecture="$(python" os print if then mv else fi julia-1.1.0.zip julia-1.1.0 julia rm julia1.1.0-arm32bit __macosx one add symbolic link for executable ln install ijulia kernel su pi pkg pkg.add two>

Alternative 2: Julia 0.6.0 provided with Raspbian Stretch (NOT RECOMMENDED)

This is NOT RECOMMENDED as Julia 0.6.0 is no longer maintained. I keep the installer here for reference only.

sudo ./inst_julia-0.6.0.sh
# script name:     inst_julia-0.6.0.sh
# last modified:   2019/05/23
# sudo:            yes

env=/home/pi/.venv/jns script_name=$(basename -- "$0")

if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$script_name" exit 1 fi


apt -y install julia

su pi <

Install R-3.6.0 and the IRkernel (optional)

Since the R binaries that come with Raspbian Stretch are quite dated, I decided to install R from source. Compilation takes a while. So be patient when running the script.

Note that this installer checks whether R is alredy present and if it is, skips compilition and just installs the IRkernel.

   sudo ./inst_R-3.6.0.sh
# script name:     inst_R-3.6.0.sh
# last modified:   2019/05/19
# sudo:            yes

SCRIPT_NAME=$(basename -- "$0") JNS_USER='pi' HOME_DIR="/home/$JNS_USER" ENV="$HOME_DIR/.venv/jns"

R_VERSION="R-3.6.0" R_DOWNLOAD_URL="http://mirrors.psu.ac.th/pub/cran/src/base/R-3/$R_VERSION.tar.gz" R_EXEC=$(which R) R_HOME="$HOME_DIR/R"

if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$SCRIPT_NAME" exit 1 fi



apt install additional packages

# apt install -y libreadline-dev apt install -y libbz2-dev


download R source and compile

if R is not yet present

# su pi <<one if then mkdir fi wget tar rm cd . make install one create soft link in ln su pi echo repos="http://cran.rstudio.com/" r two>

Install the SQLite kernel (optional)

  • I found the SQLite kernel quite useful in some experiments with SQLite3 databases in Jupyter Notebooks.
# script name:     inst_sqlite.sh
# last modified:   2018/09/09
# sudo:            no

script_name=$(basename -- "$0") env="/home/pi/.venv/jns"

if [ $(id -u) = 0 ] then echo "usage: ./$script_name" exit 1 fi

activate virtual environment

source $env/bin/activate

clone SQLite kernel repository

git clone https://github.com/brownan/sqlite3-kernel.git

install kernel

cd sqlite3-kernel python setup.py install python -m sqlite3_kernel.install cd .. rm -rf sqlite3-kernel/

Install Python support for Raspberry Pi hardware (optional)


Setting up Python support for GPIO pins, the PICAMERA module and Sense HAT hardware in your virtual environment is almost as simple as you would commonly do without such environment.

# script name:     inst_pi_hardware.sh
# last modified:   2018/10/04
# sudo: no

script_name=$(basename -- "$0") env="/home/pi/.venv/jns"

if [ $(id -u) = 0 ] then echo "usage: ./$script_name" exit 1 fi

activate virtual environment

source $env/bin/activate

pip install RTIMULib pip install sense-hat pip install picamera pip install gpiozero

Install openCV (optional)

```bash #!/bin/bash

script name: inst_opencv.sh

last modified: 2018/11/27

sudo: yes

script_name=$(basename -- "$0") env="/home/pi/.venv/jns"

if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$script_name" exit 1 fi

#------------------------------------------------------ apt install -y libjasper1 libjasper-dev apt install -y libjpeg-dev libtiff5-dev libpng12-dev apt install -y libilmbase12 apt install -y libopenexr22 apt install -y libgstreamer1.0-0 apt install -y libavcodec-extra57 apt install -y libavformat-dev apt install -y libilmbase12 apt onstall -y libavcodec-dev apt install -y libswscale-dev apt install -y libv4l-dev apt install -y libgtk2.0-dev apt install -y libgtk-3-dev apt install -y libxvidcore-dev apt install -y libx264-dev #------------------------------------------------------

su - pi <

Start the server at boot with systemd (optional)

Credits for the following solution go to mt08xx:

  • create an executable file named 'start_jupyter.sh' in '/home/pi' used to start the server
  • create a file named 'jupyter.service' in '/etc/systemd/system'
  • start the service

To do this run:

sudo ./conf_service.sh

The file has the following content:

# script name:     conf_service.sh
# last modified:   2018/09/09
# credits:         mt08xx
# sudo:            yes

script_name=$(basename -- "$0")

if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$script_name" exit 1 fi

create jupyter.sh in /home/pi and make it executable

cat << 'ONE' > /home/pi/jupyter_start.sh && chmod a+x /home/pi/jupyter_start.sh #!/bin/bash . /home/pi/.venv/jns/bin/activate jupyter lab #jupyter notebook ONE

cat << 'TWO' | sudo tee /etc/systemd/system/jupyter.service [Unit] Description=Jupyter

[Service] Type=simple ExecStart=/home/pi/jupyter_start.sh User=pi Group=pi WorkingDirectory=/home/pi/notebooks Restart=always RestartSec=10

[Install] WantedBy=multi-user.target TWO

start jupyter

systemctl daemon-reload systemctl start jupyter systemctl enable jupyter

  • Next time you boot your Pi, the service is stared automatically.
  • To stop the service for system updates run:
sudo systemctl stop jupyter

Put it all together

This script is just convenience - it executes the individual steps described above in the order necessary. Note that installation of additinal languages and their respective kernels as well as installtion of opnencv is deactivated by default as not all users may need this functionality. I recommend to run

as is and install additional functionality using the individual scripts.
  • inst_sqlite.sh
  • inst_R-3.6.0.sh
  • inst_julia-0.6.0.sh
  • inst_julia-1.1.0.sh
# script name:     inst_jns.sh
# last modified:   2019/05/26
# sudo:            yes

script_name=$(basename -- "$0")

if ! [ $(id -u) = 0 ]; then echo "usage: sudo ./$script_name" exit 1 fi




make necessary preparations


install Python packages

sudo -u pi ./inst_stack.sh

configure server

sudo -u pi ./conf_jupyter.sh




install TeX


install support for Pi hardware

sudo -u pi ./inst_pi_hardware.sh

set up service to start the server on boot





install Julia 0.6.0 and the IJulia kernel NOT RECOMMENDED


install Julia 1.1.0 and the IJulia kernel


install R 3.6.0 and the IRkernel


install the SQLite3 kernel

sudo -u pi ./inst_sqlite.sh

install opencv


Keep your installation up to date

Raspbian operating system

  • just run
    sudo apt update && sudo apt -y upgrade

Python 3 packages

  • activate the virtual environment with

    source /home/pi/.venv/jns/bin/activate
  • list outdated packages with

    pip3 list --outdated
  • Update

    pip3 install -U package
    is the name of package you want to update.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.