{"id":14977363,"url":"https://github.com/mam-dev/debianized-jupyterhub","last_synced_at":"2025-10-28T03:31:23.858Z","repository":{"id":45100741,"uuid":"140589567","full_name":"mam-dev/debianized-jupyterhub","owner":"mam-dev","description":":package: ♃ Debian packaging of JupyterHub, a multi-user server for Jupyter notebooks","archived":false,"fork":false,"pushed_at":"2022-12-21T20:16:50.000Z","size":387,"stargazers_count":29,"open_issues_count":2,"forks_count":14,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-01T10:51:11.087Z","etag":null,"topics":["data-science","debian-packages","deployment","devops","dh-virtualenv","jupyter-notebook","jupyterhub","omnibus-packages","python-3"],"latest_commit_sha":null,"homepage":"https://jupyterhub.readthedocs.io/en/stable/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mam-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-11T14:47:17.000Z","updated_at":"2023-10-02T14:45:32.000Z","dependencies_parsed_at":"2023-01-30T04:46:01.041Z","dependency_job_id":null,"html_url":"https://github.com/mam-dev/debianized-jupyterhub","commit_stats":null,"previous_names":["1and1/debianized-jupyterhub"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mam-dev%2Fdebianized-jupyterhub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mam-dev%2Fdebianized-jupyterhub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mam-dev%2Fdebianized-jupyterhub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mam-dev%2Fdebianized-jupyterhub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mam-dev","download_url":"https://codeload.github.com/mam-dev/debianized-jupyterhub/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238590593,"owners_count":19497351,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-science","debian-packages","deployment","devops","dh-virtualenv","jupyter-notebook","jupyterhub","omnibus-packages","python-3"],"created_at":"2024-09-24T13:55:31.469Z","updated_at":"2025-10-28T03:31:18.811Z","avatar_url":"https://github.com/mam-dev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \"jupyterhub\" Debian Packaging\n\n**STATUS** :ballot_box_with_check: Package building works, and you can launch the server\nusing ``systemctl start jupyterhub``, or via the provided ``Dockerfile.run``.\nIt'll use PAM authorization, i.e. starts the notebook servers in a local user's context.\nThe package is tested on *Ubuntu Bionic*, on *Ubuntu Xenial* (with Python 3.8.1 from the *Deadsnakes PPA*),\nand on *Debian Stretch*. You can also use a Docker container\n(see ``Dockerfile.run`` / user: ``admin`` / pwd: ``test1234``).\n\n\n![BSD 3-clause licensed](https://img.shields.io/badge/license-BSD_3--clause-red.svg)\n[![debianized-jupyterhub](https://img.shields.io/pypi/v/debianized-jupyterhub.svg)](https://pypi.python.org/pypi/debianized-jupyterhub/)\n[![jupyterhub](https://img.shields.io/pypi/v/jupyterhub.svg)](https://pypi.python.org/pypi/jupyterhub/)\n\n**Contents**\n\n * [What is this?](#what-is-this)\n * [Customizing the Package Contents](#customizing-the-package-contents)\n * [“Devops Intelligence” showcase](#devops-intelligence-showcase)\n * [How to build and install the package](#how-to-build-and-install-the-package)\n * [Trouble-Shooting](#trouble-shooting)\n   * ['npm' errors while building the package](#npm-errors-while-building-the-package)\n   * ['pkg-resources not found' or similar during virtualenv creation](#pkg-resources-not-found-or-similar-during-virtualenv-creation)\n   * ['no such option: --no-binary' during package builds](#no-such-option---no-binary-during-package-builds)\n * [Updating requirements](#updating-requirements)\n * [How to set up a simple service instance](#how-to-set-up-a-simple-service-instance)\n * [Securing your JupyterHub web service with an SSL off-loader](#securing-your-jupyterhub-web-service-with-an-ssl-off-loader)\n * [Changing the Service Unit Configuration](#changing-the-service-unit-configuration)\n * [Configuration Files](#configuration-files)\n * [Data Directories](#data-directories)\n * [References](#references)\n   * [Documentation Links](#documentation-links)\n   * [Related Projects](#related-projects)\n   * [Things to Look At](#things-to-look-at)\n\n\n## What is this?\n\nThis project provides packaging of the core *JupyterHub* components, so they can be easily installed on Debian-like target hosts.\nThis makes life-cycle management on production hosts a lot easier, and\n[avoids common drawbacks](https://nylas.com/blog/packaging-deploying-python/) of ‘from source’ installs,\nlike needing build tools and direct internet access in production environments.\n\nThe Debian packaging metadata in\n[debian](https://github.com/mam-dev/debianized-jupyterhub/tree/master/debian)\nputs the `jupyterhub` Python package and its dependencies as released on PyPI into a DEB package,\nusing [dh-virtualenv](https://github.com/spotify/dh-virtualenv).\nThe resulting *omnibus package* is thus easily installed to and removed from a machine,\nbut is not a ‘normal’ Debian `python-*` package. If you want that, look elsewhere.\n\nSince the dynamic router of *JupyterHub* is a *Node.js* application, the package also has a dependency on `nodejs`,\nlimited to the current LTS version range (that is 12.x or 10.x as of this writing).\nIn practice, that means you should use the\n[NodeSource](https://github.com/nodesource/distributions#nodesource-nodejs-binary-distributions)\npackages to get *Node.js*,\nbecause the native *Debian* ones are typically dated (*Stretch* comes with ``4.8.2~dfsg-1``).\nAdapt the ``debian/control`` file if your requirements are different.\n\n\n## Customizing the Package Contents\n\nTo add any plugins or other optional *Python* dependencies, list them in ``install_requires`` in ``setup.py`` as usual\n– but only use versioned dependencies so package builds are reproducible.\nThese packages are then visible in the default Python3 kernel.\nOr add a ``requirements.txt`` file, which has the advantage that you don't need to change any git-controlled files.\n\nSome standard extensions are already contained in ``setup.py`` as setuptools *extras*.\nThe ``viz`` extra installs ``seaborn`` and ``holoviews``,\nwhich in turn pulls large parts of the usual data science stack,\nincluding ``numpy``, ``scipy``, ``pandas``, and ``matplotlib``.\nThe related ``vizjs`` extra adds several Javascript-based frameworks like ``bokeh``,\nand image rendering support for SVG/PNG writing.\nActivating extras increases the package size by 10s or even 100s of MiB,\nso be aware of that and keep an eye on package size.\n\nActivate the ``spark`` extra to get PySpark and related utilities.\nThe systemd unit already includes support for auto-detection or explicit configuration\nof an installed JVM.\n\nTo activate extras, you need ``dh-virtualenv`` v1.1 which supports the\n[--extras](https://dh-virtualenv.readthedocs.io/en/latest/usage.html#cmdoption-extras) option.\nThat option is used as part of the ``EXTRA_REQUIREMENTS`` variable in ``debian/rules``\n– add or remove extras there as you see fit.\nThere are two special extras named ``default`` and ``full``\n– the ``DEFAULT_EXTRAS`` are listed in ``setup.py``, and ``full`` is simply everything.\n\n\n## “Devops Intelligence” showcase\n\nHere is an example of what you can do using this package, without any great investment of effort or capital. Within a simple setup adding a single JupyterHub host, you can use the built-in Python3 kernel to access existing internal data sources (see figure below).\n\nSuch a setup supports risk analysis and decision making within development and operations processes – typical business intelligence / data science procedures can be applied to the ‘business of making and running software’. The idea is to create feedback loops, and facilitate human decision making by automatically providing reliable input in form of up-to-date facts. After all development is our business — so let's have KPIs for developing, releasing, and operating software.\n\n\u003e ![Architecture Overview](https://github.com/mam-dev/debianized-jupyterhub/raw/master/docs/_static/img/devops-intelligence.png)\n\nSee [this notebook](https://nbviewer.jupyter.org/github/jhermann/jupyter-by-example/blob/master/complete-scenarios/devops-intelligence.ipynb)\nor [this blog post](https://blog.jupyter.org/devops-intelligence-3ff48a76b525)\nfor more details and a concrete example of how to use such a setup.\n\n\n## How to build and install the package\n\nPackages are built in Docker using the ``Dockerfile.build`` file.\nThat way you do not need to install tooling and build dependencies on your machine,\nand the package always gets built in a pristine environment.\nThe only thing you need on your workstatioon is a ``docker-ce`` installation of version 17.06 or higher\n(either on [Debian](https://docs.docker.com/install/linux/docker-ce/debian/)\nor on [Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/)).\n\nAfter initializing your work environment with ``command . .env --yes``,\ncall ``./build.sh debian:buster`` to build the package for *Debian Buster*.\nBuilding for *Ubuntu Bionic* with ``./build.sh ubuntu:bionic`` is also supported,\nas are the old-stable releases, but those aren't regularly tested and might fail.\nSee [Building Debian Packages in Docker](https://dockyard.readthedocs.io/en/latest/packaging-howto.html#dpkg-in-docker)\nfor more details.\n\nGenerated package files are placed in the ``dist/`` directory.\nYou can upload them to a Debian package repository via e.g. `dput`, see\n[here](https://github.com/jhermann/artifactory-debian#package-uploading)\nfor a hassle-free solution that works with *Artifactory* and *Bintray*.\n\nTo test the resulting package, read the comments at the start of ``Dockerfile.run``.\nOr install the package locally into `/opt/venvs/jupyterhub/`, using ``dpkg -i …``.\n\n```sh\nsudo dpkg -i $PWD/dist/jupyterhub_*.deb\n/usr/sbin/jupyterhub --version  # ensure it basically works\n```\n\nTo list the installed version of `jupyterhub` and all its dependencies\n(around 150 in the default configuration), call this:\n\n```sh\n/opt/venvs/jupyterhub/bin/pip freeze | column\n```\n\n\n## Trouble-Shooting\n\n### 'npm' errors while building the package\n\nWhile installing the ``configurable-http-proxy`` Javascript module,\nyou might get errors like ``npm ERR! code E403``.\nThat specific error means you have to provide authorization with your *Node.js* registry.\n\n``npm`` uses a configuration file which can provide both\na local registry URL and the credentials for it.\nCreate a ``.npmrc`` file in the root of your git working directory,\notherwise ``~/.npmrc`` is used.\n\n**Example ‘.npmrc’ file:**\n\n```ini\n_auth = xyzb64…E=\nalways-auth = true\nemail = joe.schmoe@example.com\n```\n\n\n### 'pkg-resources not found' or similar during virtualenv creation\n\nSee the [related section](https://dh-virtualenv.readthedocs.io/en/latest/trouble-shooting.html#pkg-resources-not-found-or-similar)\nin the dh-virtualenv manual.\n\n\n### 'no such option: --no-binary' during package builds\n\nThis package needs a reasonably recent `pip` for building.\nTo upgrade `pip` (which makes sense anyway if your system is still on the ancient version 1.5.6),\ncall ``sudo python3 -m pip install -U pip``.\n\nWhen using `dh-virtualenv 1.1` or later releases, this problem should not appear anymore.\n\n\n### “Unknown lvalue 'ProtectControlGroups' in section 'Service'” at runtime\n\nThis appears in the service logs (`journalctl`) when you use the provided systemd unit files\non older systems (e.g. Xenial). They're just warnings, and can be safely ignored.\n\n\n## Updating requirements\n\nAs previously mentioned, additional packages are listed in ``setup.py``.\nGeneral dependencies can be found in ``install_requires``,\nwhile groups of optional extensions are part of ``extras_require``.\n\nTo assist upgrading to newer versions, call these commands in the project workdir:\n\n```sh\n./setup.py egg_info\npip-upgrade --skip-package-installation --skip-virtualenv-check debianized_jupyterhub.egg-info/requires.txt \u003c\u003c\u003c\"q\"\n```\n\nThis will list any available newer version numbers, that you can then edit into ``setup.py``.\n\n\n## How to set up a simple service instance\n\nAfter installing the package, [JupyterHub](https://jupyterhub.readthedocs.io/) is launched by default\nand available at http://127.0.0.1:8000/.\n\nThe same is true when you used the ``docker run`` command as mentioned in ``Dockerfile.run``.\nThe commands as found in ``Dockerfile.run`` also give you a detailed recipe for a manual install,\nwhen you cannot use Docker for any reason – the only difference is process control, read on for that.\n\nThe package contains a ``systemd`` unit for the service, and starting it is done via ``systemctl``:\n\n```sh\nsudo systemctl enable jupyterhub\nsudo systemctl start jupyterhub\n\n# This should show the service in state \"active (running)\"\nsystemctl status 'jupyterhub' | grep -B2 Active:\n```\n\nThe service runs as ``jupyterhub.daemon``.\nNote that the ``jupyterhub`` user is not removed when purging the package,\nbut the ``/var/{log,opt,run}/jupyterhub`` directories and the configuration are.\n\nBy default, the ``sudospawner`` is used to start a user's notebook process\n– for that purpose, the included ``/etc/sudoers.d/jupyterhub`` configuration\nallows the ``jupyterhub`` system user to create these on behalf of any user\nlisted in the ``JUPYTER_USERS`` alias. Unless you change it, that means\nall accounts in the ``users`` group.\n\nIn case you want to enable a specific user group for the sudo spawner, change the sudoers file like this:\n\n    sed -i.orig~ -e s/%users/%jhub-users/ /etc/sudoers.d/jupyterhub\n\nIf you want certain users to have admin access, add them to the set named `c.Authenticator.admin_users`\nin `/etc/jupyterhub/jupyterhub_config.py`.\n\nAfter an upgrade, the service restarts automatically by default\n– you can change that using the ``JUPYTERHUB_AUTO_RESTART`` variable in ``/etc/default/jupyterhub``.\n\nIn case of errors or other trouble, look into the service's journal with…\n\n```sh\njournalctl -eu jupyterhub\n```\n\nTo identify your instance, and help users use the right login credentials,\nadd something similar to this to your `/etc/jupyterhub/jupyterhub_config.py`\n(see [this issue](https://github.com/jupyterhub/jupyterhub/pull/1913) for details):\n\n```py\nc.JupyterHub.template_vars = dict(\n    announcement=\n        '\u003ca href=\"https://confluence.example.com/x/123456\" target=\"_blank\"\u003e'\n        \"\u003ch1\u003eDevOps Intelligence Platform\u003c/h1\u003e\u003c/a\u003e\",\n    announcement_login=\n        '\u003ca href=\"https://confluence.example.com/x/123456\" target=\"_blank\"\u003e'\n        \"\u003ch1\u003eDevOps Intelligence Platform\u003c/h1\u003e\u003c/a\u003e\",\n        \"\u003cbig\u003e\u0026#128274; \u003cb\u003eUse your company LDAP credentials!\u003c/b\u003e\u003c/big\u003e\",\n)\n```\n\nIf you add a PNG image at `/etc/jupyterhub/banner.png`, it is used instead\nof the original banner image (sized 208 × 56 px). Note that this is done\nvia a `postinst` script, so you must call `dpkg-reconfigure jupyterhub`\nif you change or add such an image *after* the package installation.\n\n\n## Securing your JupyterHub web service with an SSL off-loader\n\nNote that JupyterHub can directly offer an SSL endpoint,\nbut there are a few reasons to do that via a local proxy:\n\n * JupyterHub needs no special configuration to open a low port (remember, we do not run it as ``root``).\n * Often there are already configuration management systems in place that,\n   for commodity web servers and proxies, seamlessly handle certificate management and other complexities.\n * You can protect sensitive endpoints (e.g. metrics) against unauthorized access using\n   the built-in mechanisms of the chosen SSL off-loader.\n\nTo hide the HTTP endpoint from the outside world,\nchange the bind URL in ``/etc/default/jupyterhub`` as follows:\n\n    # Bind to 127.0.0.1 only\n    sed -i.orig~ -e s~//:8000~//127.0.0.1:8000~ /etc/default/jupyterhub\n\nRestart the service and check that port 8000 is bound to localhost only:\n\n    systemctl restart jupyterhub.service\n    netstat -tulpn | grep :8000\n\nThen install your chosen webserver / proxy for SSL off-loading,\nlistening on port 443 and forwarding to port 8000.\nTypical candidates are NginX, Apache httpd, or Envoy.\nFor an internet-facing service, consider [https-portal](https://github.com/SteveLTN/https-portal),\nwhich is a NginX docker image with easy configuration and built-in *Let's Encrypt* support.\n\nOtherwise, install the Debian `nginx-full` package and copy\n[docs/examples/nginx-jhub.conf](https://github.com/mam-dev/debianized-jupyterhub/blob/master/docs/examples/nginx-jhub.conf)\nto the `/etc/nginx/sites-enabled/default` file (or another path depending on your server setup).\nMake sure to read through the file, most likely you have to adapt the certificate paths in\n`ssl_certificate` and `ssl_certificate_key` (and create a certificate, e.g. a self-signed one).\n\nYou also need to create Diffie-Hellman parameters using the following command,\nwhich can take several minutes to finish:\n\n    openssl dhparam -out /etc/ssl/private/dhparam.pem 4096\n\nThen (re-)start the `nginx` service and try to login.\n\n:bangbang: Note that this does not protect against any local users\nand their notebook servers and terminals, at least as long as you\nuse the default spawner that launches local processes.\n\n\n## Changing the Service Unit Configuration\n\nThe best way to change or augment the configuration of a *systemd* service\nis to use a ‘drop-in’ file.\nFor example, to increase the limit for open file handles\nabove the default of 8192, use this in a **``root``** shell:\n\n```sh\nunit='jupyterhub'\n\n# Change max. number of open files for ‘$unit’…\nmkdir -p /etc/systemd/system/$unit.service.d\ncat \u003e/etc/systemd/system/$unit.service.d/limits.conf \u003c\u003c'EOF'\n[Service]\nLimitNOFILE=16384\nEOF\n\nsystemctl daemon-reload\nsystemctl restart $unit\n\n# Check that the changes are effective…\nsystemctl cat $unit\nlet $(systemctl show $unit -p MainPID)\ncat \"/proc/$MainPID/limits\" | egrep 'Limit|files'\n```\n\n\n## Configuration Files\n\n * ``/etc/default/jupyterhub`` – Operational parameters like log levels and port bindings.\n * ``/etc/jupyterhub/jupyterhub_config.py`` – The service's configuration.\n\nA few configuration parameters are set in the ``/usr/sbin/jupyterhub-launcher`` script\nand thus override any values provided by ``jupyterhub_config.py``.\n\n :information_source: Please note that the files in ``/etc/jupyterhub``\n are *not* world-readable, since they might contain passwords.\n\n\n## Data Directories\n\n * ``/var/log/jupyterhub`` – Extra log files.\n * ``/var/opt/jupyterhub`` – Data files created during runtime (``jupyterhub_cookie_secret``, ``jupyterhub.sqlite``, …).\n * ``/run/jupyterhub`` – PID file.\n\nYou should stick to these locations, because the maintainer scripts have special handling for them.\nIf you need to relocate, consider using symbolic links to point to the physical location.\n\n\n## References\n\n### Documentation Links\n\nThese links point to parts of the documentation especially useful for operating a JupyterHub installation.\n\n * [Troubleshooting](https://jupyterhub.readthedocs.io/en/stable/troubleshooting.html#troubleshooting)\n\n   * [Troubleshooting Commands](https://jupyterhub.readthedocs.io/en/stable/troubleshooting.html#troubleshooting-commands)\n\n\n### Related Projects\n\n * [Springerle/debianized-pypi-mold](https://github.com/Springerle/debianized-pypi-mold) – Cookiecutter that was used to create this project.\n\n### Things to Look At\n\n * [Tutorial: Getting Started with JupyterHub](https://jupyterhub-tutorial.readthedocs.io/)\n * https://github.com/jupyterhub/the-littlest-jupyterhub\n * Notebook culling: https://github.com/jupyterhub/jupyterhub/issues/2032\n * https://github.com/jupyterhub/systemdspawner\n   * As for “Simple sudo rules do not help, since unrestricted access to systemd-run is equivalent to root”, sudo command patterns or a wrapper script could probably fix that.\n * [Dockerize and Kerberize Notebook for Yarn and HDFS](https://www.youtube.com/watch?v=7m9VK0kXdcM\u0026feature=youtu.be) [YouTube]\n   * [bloomberg/jupyterhub-kdcauthenticator](https://github.com/bloomberg/jupyterhub-kdcauthenticator) – A Kerberos authenticator module for the JupyterHub platform.\n   * [jupyter-incubator/sparkmagic](https://github.com/jupyter-incubator/sparkmagic) – Jupyter magics and kernels for working with remote Spark clusters.\n   * [Apache Livy](https://github.com/apache/incubator-livy#apache-livy) – An open source REST interface for interacting with Apache Spark from anywhere.\n * [jupyter/docker-stacks](https://github.com/jupyter/docker-stacks) – Ready-to-run Docker images containing Jupyter applications.\n * [jupyter/repo2docker](https://github.com/jupyter/repo2docker) – Turn git repositories into Jupyter-enabled Docker Images.\n * [vatlab/SoS](https://github.com/vatlab/SOS) – Workflow system designed for daily data analysis.\n * [sparklingpandas/sparklingpandas](https://github.com/sparklingpandas/sparklingpandas) – SparklingPandas builds on Spark's DataFrame class to give you a polished, pythonic, and Pandas-like API.\n * [data-8/nbzip](https://github.com/data-8/nbzip) – Zips and downloads all the contents of a Jupyter notebook.\n * [data-8/nbgitpuller](https://github.com/data-8/nbgitpuller)  – One-way git pull with auto-merging, most suited for classroom settings.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmam-dev%2Fdebianized-jupyterhub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmam-dev%2Fdebianized-jupyterhub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmam-dev%2Fdebianized-jupyterhub/lists"}