<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - JupyterHub</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/tag-jupyterhub.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2026-09-11T08:54:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>JupyterHub 6.0</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterhub-6-0/" rel="alternate"/><published>2026-09-11T08:54:00+00:00</published><updated>2026-09-11T08:54:00+00:00</updated><author><name>Serena Bonaretti</name></author><id>tag:jasongrout.github.io,2026-09-11:/medium-archive/pelican/posts/2026/jupyterhub-6-0/</id><summary type="html">&lt;p&gt;By Serena Bonaretti and Min Ragan-Kelley&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;By Serena Bonaretti and Min Ragan-Kelley&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterhub-6-0/images/001-1_l3xTI5FmlszK3So0jg0-YA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyterhub.readthedocs.io"&gt;JupyterHub&lt;/a&gt; is a collection of tools that provides standardized, user-friendly computing environments for education and research. Today, we’re pleased to announce the release of &lt;a href="https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html#id2"&gt;&lt;strong&gt;JupyterHub 6.0&lt;/strong&gt;&lt;/a&gt;. This is a substantial release with several new features. The breaking changes ought to be minimal, and should affect very few deployments. There is a small database schema upgrade, so make sure to backup your database prior to upgrading, as usual.&lt;/p&gt;
&lt;p&gt;Some changes to highlight:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stricter named server rules&lt;/strong&gt;: Named servers now have stricter rules on their names, and a new less-restricted “display name” field. This might be the &lt;strong&gt;most significant change&lt;/strong&gt; affecting you! See the &lt;a href="https://jupyterhub.readthedocs.io/en/6.0.0/howto/upgrading-v6.html#named-server-restrictions"&gt;documentation&lt;/a&gt; for more details.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Python 3.10 and database upgrade&lt;/strong&gt;: JupyterHub 6.0 now requires Python 3.10 and includes a database schema upgrade. Make sure to &lt;strong&gt;back up your database&lt;/strong&gt; before upgrading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Updates to metrics&lt;/strong&gt;: The old StatsD metrics have been removed, in favor of the Prometheus metrics that most people are already using. For Spawner (and spawn hook) authors, JupyterHub 6.0 also introduces &lt;a href="https://jupyterhub.readthedocs.io/en/6.0.0/reference/spawners.html#exception-handling"&gt;SpawnException&lt;/a&gt;, which provides more granular information for classifying spawn failures in metrics, to help deployments separate &lt;em&gt;errors&lt;/em&gt; from &lt;em&gt;rejections&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Internal communication changes&lt;/strong&gt;: JupyterHub now uses &lt;em&gt;aiohttp&lt;/em&gt; for internal HTTP requests, which may require tuning for large deployments since internal HTTP requests. This update has facilitated using unix sockets for internal communication on single-node deployments.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Improved granularity in API access&lt;/strong&gt;: JupyterHub provides new API endpoints for working with single servers, along with more flexible ways to define permissions, such as &lt;em&gt;start:servers&lt;/em&gt; scope and &lt;em&gt;extra_user_scopes&lt;/em&gt;.&lt;br&gt;
See the &lt;a href="https://jupyterhub.readthedocs.io/en/stable/rbac/roles.html#overriding-default-roles"&gt;documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Beyond these main changes, there have been lots of other improvements and bugfixes. You can find more details in the &lt;a href="https://jupyterhub.readthedocs.io/en/stable/howto/upgrading-v6.html"&gt;upgrading documentation&lt;/a&gt; and &lt;a href="https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html#changelog"&gt;changelog&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="thank-you-to-our-contributors"&gt;Thank you to our contributors&lt;/h2&gt;
&lt;p&gt;The following people contributed discussions, new ideas, code and documentation contributions, and review: &lt;a href="https://github.com/agoose77"&gt;agoose77&lt;/a&gt;, &lt;a href="https://github.com/anny0811"&gt;anny0811&lt;/a&gt;, &lt;a href="https://github.com/ateucher"&gt;ateucher&lt;/a&gt;, &lt;a href="https://github.com/betatim"&gt;betatim&lt;/a&gt;, &lt;a href="https://github.com/bl-aire"&gt;bl-aire&lt;/a&gt;, &lt;a href="https://github.com/clhedrick"&gt;clhedrick&lt;/a&gt;, &lt;a href="https://github.com/consideRatio"&gt;consideRatio&lt;/a&gt;, &lt;a href="https://github.com/dometto"&gt;dometto&lt;/a&gt;, &lt;a href="https://github.com/dragz"&gt;dragz&lt;/a&gt;, &lt;a href="https://github.com/erawn"&gt;erawn&lt;/a&gt;, &lt;a href="https://github.com/ericvd-ucb"&gt;ericvd-ucb&lt;/a&gt;, &lt;a href="https://github.com/jezwilkinson"&gt;jezwilkinson&lt;/a&gt;, &lt;a href="https://github.com/jnywong"&gt;jnywong&lt;/a&gt;, &lt;a href="https://github.com/jrdnbradford"&gt;jrdnbradford&lt;/a&gt;, &lt;a href="https://github.com/kellyrowland"&gt;kellyrowland&lt;/a&gt;, &lt;a href="https://github.com/kreuzert"&gt;kreuzert&lt;/a&gt;, &lt;a href="https://github.com/LUC4R4T0R"&gt;LUC4R4T0R&lt;/a&gt;, &lt;a href="https://github.com/manics"&gt;manics&lt;/a&gt;, &lt;a href="https://github.com/mayli"&gt;mayli&lt;/a&gt;, &lt;a href="https://github.com/milliams"&gt;milliams&lt;/a&gt;, &lt;a href="https://github.com/minrk"&gt;minrk&lt;/a&gt;, &lt;a href="https://github.com/mynkyu"&gt;mynkyu&lt;/a&gt;, &lt;a href="https://github.com/nsychev"&gt;nsychev&lt;/a&gt;, &lt;a href="https://github.com/Paul2708"&gt;Paul2708&lt;/a&gt;, &lt;a href="https://github.com/Rameshkumar31595"&gt;Rameshkumar31595&lt;/a&gt;, &lt;a href="https://github.com/rgaiacs"&gt;rgaiacs&lt;/a&gt;, &lt;a href="https://github.com/shaneknapp"&gt;shaneknapp&lt;/a&gt;, &lt;a href="https://github.com/sixonenines"&gt;sixonenines&lt;/a&gt;, &lt;a href="https://github.com/tbizouerne"&gt;tbizouerne&lt;/a&gt;, &lt;a href="https://github.com/Tech-Abhang"&gt;Tech-Abhang&lt;/a&gt;, &lt;a href="https://github.com/wilkinvr"&gt;wilkinvr&lt;/a&gt;, &lt;a href="https://github.com/willingc"&gt;willingc&lt;/a&gt;, &lt;a href="https://github.com/yuvipanda"&gt;yuvipanda&lt;/a&gt;&lt;/p&gt;
</content><category term="JupyterHub"/></entry><entry><title>Positron Server available for academic use via JupyterHub</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/positron-server-available-for-academic-use-via/" rel="alternate"/><published>2026-07-29T18:19:00+00:00</published><updated>2026-07-31T12:28:00+00:00</updated><author><name>Jupyter Foundation</name></author><id>tag:jasongrout.github.io,2026-07-29:/medium-archive/pelican/posts/2026/positron-server-available-for-academic-use-via/</id><summary type="html">&lt;p&gt;Editor’s note: This is a guest blog post from Posit, a Jupyter Foundation member&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;Editor’s note: This is a guest blog post from Posit, a&lt;/em&gt; &lt;a href="https://jupyterfoundation.org/"&gt;&lt;em&gt;Jupyter Foundation&lt;/em&gt;&lt;/a&gt; &lt;em&gt;member&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/positron-server-available-for-academic-use-via/images/001-1_1jHlCkiGYDfmAd0mWDhf1A.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Academic institutions can now offer &lt;a href="https://positron.posit.co/"&gt;Positron&lt;/a&gt; directly within their existing JupyterHub environments, giving students access to a modern, full-featured data science IDE without requiring local installation or additional infrastructure. Licensed under the permissive, source-available &lt;a href="https://positron.posit.co/licensing.html"&gt;Elastic License 2.0&lt;/a&gt;, Positron as a desktop app is free for everyone to use, build on, and extend across personal, academic, and commercial projects. Through a free educational license, universities and other academic organizations can additionally make &lt;a href="https://positron.posit.co/education.html"&gt;Positron Server&lt;/a&gt; available to currently enrolled students for coursework, helping create a consistent learning experience across classrooms, labs, and remote environments.&lt;/p&gt;
&lt;p&gt;By integrating Positron into JupyterHub, educators can provide students with a powerful development environment alongside the tools they already use. Students simply launch Positron from the JupyterHub interface, just as they would open JupyterLab or a different environment, and can begin working immediately.&lt;/p&gt;
&lt;h2 id="bringing-a-modern-data-science-ide-to-the-classroom"&gt;Bringing a Modern Data Science IDE to the Classroom&lt;/h2&gt;
&lt;p&gt;Positron combines the familiarity of notebook-based workflows with the capabilities of a professional integrated development environment (IDE). Once launched through JupyterHub, students have access to the complete Positron experience, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comprehensive support for both Python and R&lt;/li&gt;
&lt;li&gt;Access to the OpenVSX extension marketplace&lt;/li&gt;
&lt;li&gt;Built-in data viewer and variables explorer&lt;/li&gt;
&lt;li&gt;Integrated help pane, debugging tools, and version control&lt;/li&gt;
&lt;li&gt;Advanced development features that help students grow their data science skills over time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This approach allows instructors to standardize course environments while reducing the technical barriers often associated with software installation and configuration.&lt;/p&gt;
&lt;h2 id="designed-to-work-with-existing-jupyterhub-deployments"&gt;Designed to Work with Existing JupyterHub Deployments&lt;/h2&gt;
&lt;p&gt;Positron Server is built to integrate directly into existing JupyterHub environments, making adoption straightforward for administrators already managing Jupyter-based infrastructure.&lt;/p&gt;
&lt;p&gt;The solution is compatible with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JupyterHub deployments running JupyterLab 4&lt;/li&gt;
&lt;li&gt;Python 3.9 and later environments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installation is provided through the &lt;a href="https://posit-dev.github.io/jupyter-positron-server/"&gt;jupyter-positron-server&lt;/a&gt; Python package, which is built on &lt;a href="https://jupyter-server-proxy.readthedocs.io/"&gt;Jupyter Server Proxy&lt;/a&gt;. For administrators who have previously configured services within JupyterHub, the setup process will feel familiar.&lt;/p&gt;
&lt;p&gt;Rather than functioning as a standalone desktop application, Positron Server extends existing JupyterHub deployments by making the Positron IDE available as another launch option within the environment.&lt;/p&gt;
&lt;h2 id="free-educational-licensing-for-teaching"&gt;Free Educational Licensing for Teaching&lt;/h2&gt;
&lt;p&gt;Academic institutions using Positron for instructional purposes are eligible for a free teaching license. Under this license, access can be provided to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Currently enrolled students&lt;/li&gt;
&lt;li&gt;Course participants&lt;/li&gt;
&lt;li&gt;Faculty and staff involved in delivering educational programs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This licensing model enables institutions to incorporate Positron into coursework without additional software costs, helping educators provide professional-grade tools to learners.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Institutions interested in offering Positron through JupyterHub can get started in three simple steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Review the eligibility requirements outlined in the &lt;a href="https://positron.posit.co/licensing.html#positron-education-license-rider"&gt;Positron Education License Rider&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://positron.posit.co/education.html#applying-for-a-teaching-license"&gt;Contact Posit&lt;/a&gt; to request a free teaching license.&lt;/li&gt;
&lt;li&gt;Follow the &lt;a href="https://posit-dev.github.io/jupyter-positron-server/"&gt;jupyter-positron-server&lt;/a&gt; installation and configuration documentation to deploy Positron within your JupyterHub environment.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="supporting-the-next-generation-of-data-scientists"&gt;Supporting the Next Generation of Data Scientists&lt;/h2&gt;
&lt;p&gt;As data science and computational research continue to grow across disciplines, educational institutions are looking for ways to provide students with professional tools that are easy to access and manage. By bringing Positron directly into JupyterHub, universities can deliver a modern, scalable development environment that supports both teaching and learning while minimizing setup complexity.&lt;/p&gt;
&lt;p&gt;Whether students are writing Python code, analyzing data in R, or exploring advanced development workflows, Positron Server helps create a seamless experience from the first day of class through more advanced coursework and research projects.&lt;/p&gt;
</content><category term="JupyterHub"/></entry><entry><title>Becoming the new JupyterHub and Jupyter Book community manager</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/becoming-the-new-jupyterhub-and-jupyter-book-community/" rel="alternate"/><published>2026-06-15T07:01:00+00:00</published><updated>2026-06-15T07:01:00+00:00</updated><author><name>Serena Bonaretti</name></author><id>tag:jasongrout.github.io,2026-06-15:/medium-archive/pelican/posts/2026/becoming-the-new-jupyterhub-and-jupyter-book-community/</id><summary type="html">&lt;p&gt;There are moments in life when things, simply, converge. You work and study, study and work, and then, simply, it happens. It starts one…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/becoming-the-new-jupyterhub-and-jupyter-book-community/images/001-1_FO2axICPqqSETF34ZMoaBw.jpeg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;There are moments in life when things, simply, converge. You work and study, study and work, and then, simply, it happens. It starts one day when somebody tells you “there is this new thing called Jupyter Notebook, you should give it a try!”. So you install it, play around with it. It works like a charm. It’s easy, fast, nearly playful. It’s beautiful–and if you grow up in Italy, beauty always matters. Then, you have the urge to tell others about it. You organize a workshop at a conference in musculoskeletal image analysis and introduce marvelous novel tools for computational research: “You know? Nowadays we can do open science easily because somebody has created Jupyter Notebook, Binder, new Python libraries!” And then, together with some friends, you write a proposal for a &lt;a href="/posts/2022/report-on-the-jupyter-community-workshop/"&gt;Jupyter Community Workshop&lt;/a&gt;, and–unbelievable!–you get it. Initially, you think you are just organizing a workshop. But somehow, the number of people interested becomes larger and larger, and you find yourself co-founding and coordinating what is now the Open and Reproducible Musculoskeletal Imaging Research (&lt;a href="https://www.ormir.org"&gt;ORMIR&lt;/a&gt;) Community. In ORMIR’s Python packages, the examples are in Jupyter Notebook, the documentation is in Jupyter Book. Sooner or later, we will have our JupyterHubs too; it’s just a matter of time. In parallel, life gives you some professional challenges. So you start teaching Python online, one-to-one. You look for material that fits your style, but you cannot find any. So you start teaching it your way, using Jupyter Notebook in JupyterLab and it works. Students find it easy, independent of their background. The teaching material seems to work, so you transcribe it into a book entitled &lt;a href="http://www.learnpythonwithjupyter.com"&gt;Learn Python with Jupyter&lt;/a&gt;, and you make it open. At the same time, you are obsessed with keeping up to date. You go to JupyterCon, meet the people, watch their YouTube videos, participate in online meetings. And then, there is a &lt;a href="/posts/2026/call-for-applications-community-manager-for-jupyterhub/"&gt;job posting&lt;/a&gt; for a community manager. And you are like: “Nah, they will never hire me”, so you set it aside. But then somebody out of the blue tells you to apply because “100% of lottery winners played!”. So you give it a chance, and here I am: my name is &lt;a href="https://sbonaretti.github.io/"&gt;Serena Bonaretti&lt;/a&gt; and I am the new JupyterHub and Jupyter Book community manager!&lt;/p&gt;
&lt;p&gt;In this new role, I am very much looking forward to working with the JupyterHub and Jupyter Book communities! I will collaborate with maintainers to understand and document projects’ priorities and define the best processes to achieve their vision. I will support contributors in finding their way into the projects and make the real-world impact that they hope to achieve. I will dialogue with users to make sure that the tools we create are useful for their work. And I will share our activities monthly and what we learn with anybody interested, so that we can all benefit from this journey!&lt;/p&gt;
&lt;p&gt;And you? How did you start using the tools of the Jupyter ecosystem? Do you work in JupyterHub? Have you ever written a Jupyter Book? Would you like to be a contributor? Are you an enthusiastic user? Write me on &lt;a href="https://discord.mystmd.org/"&gt;Discord&lt;/a&gt; (Jupyter Book/MyST) or &lt;a href="https://jupyter.zulipchat.com/#narrow/channel/469744-jupyterhub"&gt;Zulip&lt;/a&gt; (JupyterHub). The JupyterHub and Jupyter Book communities and I are looking forward to getting to know you!&lt;/p&gt;
</content><category term="community"/><category term="Jupyter Book"/><category term="JupyterHub"/></entry><entry><title>Better sharing UX with nbgitpuller and contextual error handling</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/" rel="alternate"/><published>2026-04-09T15:02:00+00:00</published><updated>2026-04-09T15:02:00+00:00</updated><author><name>Jenny Wong</name></author><id>tag:jasongrout.github.io,2026-04-09:/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/</id><summary type="html">&lt;p&gt;TL;DR nbgitpuller now has improved UX context-aware error handling. Update to version 1.3.0 and let us know what you think by opening …&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/images/001-1_d1wtukpXH7983Z0nCKv33Q.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h3 id="tldr"&gt;TL;DR&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyterhub/nbgitpuller"&gt;nbgitpuller&lt;/a&gt; now has improved UX context-aware error handling. Update to &lt;a href="https://pypi.org/project/nbgitpuller/1.3.0/"&gt;version 1.3.0&lt;/a&gt; and let us know what you think by opening an &lt;a href="https://github.com/jupyterhub/nbgitpuller/issues"&gt;issue&lt;/a&gt; 🚀&lt;/p&gt;
&lt;h3 id="what-is-nbgitpuller"&gt;What is nbgitpuller?&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyterhub/nbgitpuller"&gt;nbgitpuller&lt;/a&gt; lets you distribute content to a Jupyter user via the click of a button! This allows your users to focus on the content without needing to understand git or other version control machinery.&lt;/p&gt;
&lt;p&gt;nbgitpuller provides &lt;a href="https://nbgitpuller.readthedocs.io/en/latest/topic/automatic-merging.html"&gt;automatic, opinionated conflict resolution&lt;/a&gt; by using git under the hood. It is commonly used to distribute content to multiple users of a JupyterHub, though it works just fine on an individual person’s computer, if they have Jupyter installed.&lt;/p&gt;
&lt;p&gt;Here’s an example of nbgitpuller in action:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The author uses the &lt;a href="https://nbgitpuller.readthedocs.io/en/latest/link.html"&gt;nbgitpuller link generator&lt;/a&gt; to create a link.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/images/002-0_2M4f_Hh6iyGP7VCo.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;The consumer clicks the link, and the content is pulled into a live Jupyter session.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/images/003-0_J_A7MbZ2JI5hZh7n.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Example use cases for sharing with nbgitpuller include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interactive demos&lt;/li&gt;
&lt;li&gt;Workshop and training scenarios&lt;/li&gt;
&lt;li&gt;University exams&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and many more!&lt;/p&gt;
&lt;h3 id="how-does-nbgitpuller-work"&gt;How does nbgitpuller work?&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;nbgitpuller&lt;/code&gt; package is installed in the compute environment. The compute environment now has a &lt;code&gt;/git-pull&lt;/code&gt; endpoint URL that can understand where to pull content from using URL parameters. Syncing content critically depends on &lt;code&gt;git&lt;/code&gt; operations to &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;checkout&lt;/code&gt;, &lt;code&gt;clone&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt;, etc.&lt;/p&gt;
&lt;h3 id="when-it-goes-wrong"&gt;When it goes wrong&lt;/h3&gt;
&lt;p&gt;Based on the data sent through by the kind folks running the Berkeley DataHub, there were 2163 logs available, of which:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;983 (45%) were ‘merge’ conflicts&lt;/li&gt;
&lt;li&gt;493 (23%) were ‘fetch’ errors&lt;/li&gt;
&lt;li&gt;467 (22%) were ‘ls-remote’ errors&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="Bar chart showing the most common errors users faced.]" src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/images/004-1_v1NGeWyH5Frl9QDJ2qed0Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Bar chart showing the most common errors users faced.]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The remaining errors (10%) were mostly ‘checkout’ errors. This represents the most &lt;strong&gt;frequent&lt;/strong&gt; errors as seen by students.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Bar chart showing the most unique errors link authors made." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/images/005-1_6sYjI1QPnc_yz6IiIw442g.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Bar chart showing the most unique errors link authors made.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the same set of logs, there were 172 &lt;strong&gt;unique&lt;/strong&gt; errors: most ‘ls-remote’ errors come from mistakes in the content repo URL. This represents the most uniquely common errors made by instructors.&lt;/p&gt;
&lt;h3 id="merge-conflicts"&gt;Merge conflicts&lt;/h3&gt;
&lt;p&gt;If the link author changes content after the consumer clicks a link, then &lt;code&gt;nbgitpuller&lt;/code&gt; needs to sync updates for the consumer on subsequent link clicks. The &lt;code&gt;nbgitpuller&lt;/code&gt; merging strategy makes opinionated choices so that the link consumer &lt;em&gt;never&lt;/em&gt; has to interact with git, and will always preserve the consumer’s working changes.&lt;/p&gt;
&lt;p&gt;Things can go wrong when&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consumers can diverge the git history if they perform a git commit&lt;/li&gt;
&lt;li&gt;Authors can diverge the git history if they perform force push commits&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="error-ux-old"&gt;Error UX (old)&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt="Old UX for merge errors." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/images/006-1_prhJ0nFlArAhfFwFFZbbLA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Old UX for merge errors.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Problems with the old UX include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a scary terminal&lt;/li&gt;
&lt;li&gt;difficulty for the user to figure out what went wrong&lt;/li&gt;
&lt;li&gt;no suggestion for the user to fix the problem or a signpost to continue to the compute platform&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="error-ux-new"&gt;Error UX (new)&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt="New UX for merge errors." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/better-sharing-ux-with-nbgitpuller-and-contextual-error/images/007-1_iE8c5G51r3qL8ITBFiOZKg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;New UX for merge errors.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;New improvements to the UX include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The terminal is closed by default, but you can optionally toggle it open&lt;/li&gt;
&lt;li&gt;There is a copy to clipboard button to easily share the error log from the terminal&lt;/li&gt;
&lt;li&gt;A more user-friendly and context-aware helper message is displayed&lt;/li&gt;
&lt;li&gt;A link to the general documentation for reference&lt;/li&gt;
&lt;li&gt;A ‘Proceed without syncing’ button takes the user to the compute platform without making any changes&lt;/li&gt;
&lt;li&gt;In the case of merge errors, an extra &lt;strong&gt;Backup and resync&lt;/strong&gt; button option is presented&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="learn-more"&gt;Learn more&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nbgitpuller.readthedocs.io/en/latest/"&gt;nbgitpuller documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.berkeley.edu/"&gt;UC Berkeley&lt;/a&gt; and the &lt;a href="https://www.cloudbank.org/training/access-cloudbank-classroom"&gt;CloudBank Classroom&lt;/a&gt; project&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cal-icor.org/"&gt;CAL ICOR&lt;/a&gt; for co-funding this development work&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ericvd-ucb"&gt;Eric Van Dusen&lt;/a&gt; and &lt;a href="https://github.com/sean-morris"&gt;Sean Morris&lt;/a&gt; for championing this work&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/balajialg"&gt;Balaji Alwar&lt;/a&gt; for providing the error data and sharing feedback&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nthiery"&gt;Nicolas M. Thiéry&lt;/a&gt; for feedback on the UX design&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cmarmo"&gt;cmarmo&lt;/a&gt; for contributing the ‘Backup and resync button’&lt;/li&gt;
&lt;/ul&gt;
</content><category term="JupyterHub"/></entry><entry><title>Call for applications: Community Manager for JupyterHub and Jupyter Book</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/call-for-applications-community-manager-for-jupyterhub/" rel="alternate"/><published>2026-03-05T16:44:00+00:00</published><updated>2026-03-05T19:07:00+00:00</updated><author><name>Kirstie Whitaker</name></author><id>tag:jasongrout.github.io,2026-03-05:/medium-archive/pelican/posts/2026/call-for-applications-community-manager-for-jupyterhub/</id><summary type="html">&lt;p&gt;The JupyterHub and Jupyter Book communities are hiring a community manager.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The &lt;a href="https://jupyter.org/hub"&gt;JupyterHub&lt;/a&gt; and &lt;a href="https://jupyterbook.org"&gt;Jupyter Book&lt;/a&gt; communities are hiring a &lt;strong&gt;community manager&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="a cartoon of people working together. there are a group in the center and then spokes of people working in different contexts on different focus areas, some are alone but others are working in smaller groups, all part of the whole collaboration." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/call-for-applications-community-manager-for-jupyterhub/images/001-1__jfvloGZbtvcYdoZ0N6orA.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Join our community and support us all to grow, nurture, and build things together. Credit: The Turing Way and Scriberia, used under a CC-BY license &lt;a href="https://doi.org/10.5281/zenodo.3332808"&gt;https://doi.org/10.5281/zenodo.3332808&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In JupyterHub, we are democratizing access to interactive computing environments to allow everyone, no matter where they are, to explore data, develop code, and advance knowledge without barriers. In Jupyter Book, we are building the next generation of open, community-driven technical publication tooling, leveraging the &lt;a href="https://mystmd.org/"&gt;MyST document engine&lt;/a&gt; to create modular, reusable, and reproducible computational narratives.&lt;/p&gt;
&lt;p&gt;We are excited to fund dedicated time for someone — maybe you! — to join these teams.&lt;/p&gt;
&lt;p&gt;The community manager will be responsible for identifying and developing activities and communication pathways that align maintainer and contributor actions with the goals of each project, and that build bridges across the Jupyter ecosystem.&lt;/p&gt;
&lt;p&gt;You can read more about our motivation for this role in the &lt;a href="https://github.com/jupyter-governance/funding-proposals/issues/6"&gt;proposal&lt;/a&gt;, part of the Jupyter Foundation’s &lt;a href="https://jupyterfoundation.org/community-funding-proposals"&gt;Community Funding&lt;/a&gt; initiative. We particularly encourage applicants to review the section “&lt;em&gt;Impact Metrics: How do we define success?&lt;/em&gt;”&lt;/p&gt;
&lt;p&gt;In this blog post we have outlined the community manager’s &lt;strong&gt;responsibilities&lt;/strong&gt;, their &lt;strong&gt;preferred expertise&lt;/strong&gt;, and &lt;strong&gt;how to apply.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is a contract role, funded at $90,000 to be spent by the end of April 2027.&lt;/strong&gt; The community manager will be engaged as a contractor of the Linux Foundation and will follow the standard Linux Foundation contractor onboarding process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The deadline to apply is 29 March 2026.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="responsibilities"&gt;Responsibilities&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Strategic alignment and contributor guidance&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Collaborate with the maintainer teams from both projects to understand, document, and broadcast each project’s priorities to their communities.&lt;/li&gt;
&lt;li&gt;Actively connect contributors to open issues and workstreams that align with the respective project’s goals, and redirect well-intentioned contributions that are not aligned with the project’s currently prioritized efforts.&lt;/li&gt;
&lt;li&gt;Efficiently connect contributors with maintainers to provide them the guidance and review needed to make efficient progress, managing workload on both sides of the mentorship pair.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Contributor growth and leadership development&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Design, visualize, and manage a clear contributor journey, including identifying requisite skills and project context knowledge, for each community.&lt;/li&gt;
&lt;li&gt;Identify engaged community members and create pathways for them to take on more responsibility, such as triaging issues, reviewing pull requests, or mentoring others within their project of interest.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Fostering knowledge sharing and collaboration&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify and create opportunities for the community of each project to share information, best practices, and technical knowledge, while respecting their distinct roadmaps.&lt;/li&gt;
&lt;li&gt;Facilitate cross-community events, such as running “collaboration cafes” for both JupyterHub and JupyterBook, and strategically shape them into meetings that provide value and foster connection for both projects.&lt;/li&gt;
&lt;li&gt;Develop re-usable templates for explaining contributor pathways, such as onboarding documentation, skills maps/matrices for progression into contribution and maintenance roles, and collaboration cafe guides.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Learn in the open&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure all materials are publicly available and discoverable, including progress against our community health metrics described below.&lt;/li&gt;
&lt;li&gt;Promote these learnings across the Jupyter community including within the Zulip channel, at regular community calls, and by “visiting” other sub-projects (at synchronous meetings or in asynchronous discussion spaces) to discuss community management efforts as requested.&lt;/li&gt;
&lt;li&gt;Join the &lt;a href="https://jupyter.org/governance/communitybuildingworkinggroup/"&gt;Jupyter Community Building Working Group&lt;/a&gt; and be an active member throughout their paid time on the project.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="preferred-expertise"&gt;Preferred expertise&lt;/h2&gt;
&lt;p&gt;We have not marked any of the expertise categories listed here as &lt;em&gt;required&lt;/em&gt;. We recognize that we are looking for a unicorn, and they are curiously hard to find! &lt;strong&gt;If you do not have expertise across all of these categories we encourage you to apply.&lt;/strong&gt; We are looking forward to getting to know candidates better, including where they have identified opportunities for career and skill development as part of the JupyterHub and Jupyter Book teams.&lt;/p&gt;
&lt;p&gt;Our assessment pathway is included as part of this list to guide applicants in curating their strongest application. If you are uncertain how to answer any of the questions and/or prompts, please ask in our &lt;a href="https://jupyter.zulipchat.com/#narrow/channel/469744-jupyterhub"&gt;Jupyter Hub Zulip channel&lt;/a&gt; or email Kirstie Whitaker directly at &lt;a href="mailto:kirstie.whitaker@berkeley.edu"&gt;kirstie.whitaker@berkeley.edu&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track record of contributions to open source projects.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assessment: Cover letter &amp;amp; links&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Understanding of multi-stakeholder, community governed open source community dynamics.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assessment: Interview question. Describe two situations — one featuring a success and one a failure — where you have sought to align diverse stakeholders around a common goal. For both, share what you learned from the experiences.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Understanding of the challenges that Jupyter Hub and Jupyter Book are designed to solve (or reduce) in the world.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assessment: Interview question. Describe two deployments — one each of Jupyter Hub and Jupyter Book — that each meet a need in either education, research or technical development. You do not need to have run these deployments yourself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical skills in Python and/or TypeScript.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assessment: Cover letter &amp;amp; links&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Track record of identifying and completing tasks in the presence of significant uncertainty.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assessment: Cover letter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Ability to work effectively with two distinct and diverse teams whose community members are geographically distributed with significant time zone differences.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assessment: Cover letter. All instructions from this recruitment call followed, the letter is clearly and concisely written, includes concrete examples, and publicly accessible links where appropriate.&lt;/li&gt;
&lt;li&gt;Assessment: Interview question. Given your knowledge of Jupyter Hub and Jupyter Book, and your previous experience, how will you know if you are working effectively within these two communities?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Ability to ask for help and self manage time allocations and tasks.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assessment: Interview question. Which aspects of this role do you think will be hard for you? Which aspects will be easy (or easier) for you? Where do you expect to ask for help and/or support in the role?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-apply"&gt;How to apply&lt;/h2&gt;
&lt;p&gt;Submit a cover letter and resume through this Google Form: &lt;a href="https://forms.gle/Qw6ddyJ8rzZY5vQm6"&gt;https://forms.gle/Qw6ddyJ8rzZY5vQm6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Please include:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your motivation to apply for this role.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links to up to 3 publicly available GitHub repositories that highlight engagement in issues, pull requests, community discussions (or equivalent conversations on alternative git forge platforms).&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you don’t have publicly available links, provide a letter from a team member from a private initiative who can endorse these collaboration skills.&lt;/li&gt;
&lt;li&gt;If you can not meet this requirement, please note that in your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Links to up to 3 examples of code you have written in Python and/or TypeScript.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you don’t have publicly available links, provide a letter from a team member on a private initiative endorsing these development skills.&lt;/li&gt;
&lt;li&gt;If you can not meet this requirement, please note that in your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;A description of a previous role (paid or volunteer) where you have identified and delivered outcomes in the presence of uncertainty.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Include links to task documentation (issue / PR / report) if publicly available.&lt;/li&gt;
&lt;li&gt;If you can not meet this requirement, please note that in your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-to-expect"&gt;&lt;strong&gt;What to expect&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;We’ll accept applications from now through 29 March, 2026.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Once applications are in, here’s our process:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Resume and cover letter review:&lt;/strong&gt; The recruitment panel will review all applications and invite up to 5 applicants for an interview on zoom at a time that is convenient for the applicant and the panel members.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interview:&lt;/strong&gt; The recruitment panel will meet with applicants for 30 minutes over Zoom. They will ask the same questions of every applicant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Panel discussion &amp;amp; alignment:&lt;/strong&gt; The recruitment panel will discuss interview outcomes and align on a decision.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Offer:&lt;/strong&gt; We expect to communicate a final decision to all applicants by 30 April, 2026.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Timeline&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;29 March 2026, &lt;a href="https://en.wikipedia.org/wiki/Anywhere_on_Earth"&gt;AoE&lt;/a&gt;: Deadline for resume and cover letter applications.&lt;/li&gt;
&lt;li&gt;Before 5 April 2026: Invite up to 5 applicants to interview.&lt;/li&gt;
&lt;li&gt;8 April — 17 April 2026: Interviews conducted over Zoom.&lt;/li&gt;
&lt;li&gt;Before 11 May 2026: Communicate final decision to all applicants.&lt;/li&gt;
&lt;li&gt;May 2026: Post holder coordinates a contract with the Linux Foundation, following their standard onboarding process.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;This job description was co-created by April Johnson, Yuvi, and Kirstie Whitaker, with guidance from Chris Holdgraf, Min Ragan-Kelley, Raniere Silva, and Stéfan van der Walt, along with community input at the JupyterHub and Jupyter Book collaboration cafes.&lt;/p&gt;
</content><category term="community"/><category term="Jupyter Book"/><category term="JupyterHub"/></entry><entry><title>Scaling “Maintainer Intuition” with Pull Request Triage Boards</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/scaling-maintainer-intuition-with-pull-request-triage/" rel="alternate"/><published>2025-11-16T21:22:00+00:00</published><updated>2025-11-16T21:22:00+00:00</updated><author><name>yuvipanda</name></author><id>tag:jasongrout.github.io,2025-11-16:/medium-archive/pelican/posts/2025/scaling-maintainer-intuition-with-pull-request-triage/</id><summary type="html">&lt;p&gt;When I helped start 2i2c.org, one of my goals for the non-profit was to experiment with new and different ways of supporting the Jupyter…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;When I helped start &lt;a href="https://2i2c.org"&gt;2i2c.org&lt;/a&gt;, one of my goals for the non-profit was to experiment with new and different ways of supporting the Jupyter ecosystem’s long term health. As part of that, we have identified how &lt;a href="https://2i2c.org/blog/2025/good-citizen/"&gt;foundational contributions are very important (and distinct from directed contributions)&lt;/a&gt;, and have been experimenting with &lt;a href="https://2i2c.org/blog/2025/foundational-contributions/"&gt;new ways to make foundational contributions&lt;/a&gt; in sustainable and structured ways.&lt;/p&gt;
&lt;p&gt;One such way was via systematically doing code reviews for Pull Requests from non-maintainers in the JupyterHub ecosystem. Reviewing PRs is a critical way that maintainers keep an open source project moving forward, but identifying PRs that can productively be merged is hard. This is a post describing our system for scaling this in our team. Each 2 week sprint, we:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Looked at all open PRs in the JupyterHub org who were not maintainers&lt;/li&gt;
&lt;li&gt;Picked a PR that I deemed was reviewable and ideally mergeable within this time window&lt;/li&gt;
&lt;li&gt;Have an engineer on the team pick up that PR, and try to get it to close&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/2i2c-org/infrastructure/issues/5058"&gt;Report back&lt;/a&gt; on what we have learnt, so we can iterate on our process&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We managed to do this for a majority of sprints over the last roughly 12 months! One key bottleneck we identified in the process was Step 2. In particular, I was relying on my &lt;em&gt;maintainer intuition&lt;/em&gt; to pick a single PR that I &lt;em&gt;believe&lt;/em&gt; can be merged, so others in the team can do review work. I started exploring &lt;em&gt;what&lt;/em&gt; this intuition is, and if it can be scaled.&lt;/p&gt;
&lt;h2 id="what-is-this-maintainer-intuition"&gt;What is this maintainer intuition?&lt;/h2&gt;
&lt;p&gt;How did I pick a PR from a long list of open PRs? Observing my own behavior a few times, I noticed I was looking for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;PRs that aren’t too &lt;em&gt;big&lt;/em&gt;, and are &lt;strong&gt;a reasonable size&lt;/strong&gt; that can be merged within a 2 week window&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CI tests passing&lt;/strong&gt;, so at least our automated checks haven’t caught any issues with it&lt;/li&gt;
&lt;li&gt;Features or bug fixes that I believe &lt;strong&gt;add value to the project&lt;/strong&gt; and move us in the right direction towards being able to support our users as they need (this is the hardest!)&lt;/li&gt;
&lt;li&gt;If the author of the PR is a &lt;strong&gt;newish contributor&lt;/strong&gt;, as I want to encourage them to stick around by being responsive to their gift. All PRs are gifts that we may or may not choose to accept, but should do so with grace.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How long ago the PR was opened&lt;/strong&gt;. There is such a big difference between a response to your PR 2 days after you make it vs 2 months vs 2 years. I prioritized newer PRs.&lt;/li&gt;
&lt;li&gt;What &lt;strong&gt;kind of contribution&lt;/strong&gt; is it primarily? Different engineers on our team have different skillsets (JS, Python, etc) and I wanted to match the PR to what the engineer preferred code reviewing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While (3) is hard to scale, everything else seemed like something we could build systems for that let others follow a process, thus removing myself as a bottleneck. I experimented with some GitHub issue filters and project automations, and after finding them lacking, built out a brand new open source project to do this: &lt;a href="https://github.com/jupyter/pr-triage-board-bot"&gt;pr-triage-board-bot&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id="pr-triage-github-boards"&gt;PR Triage GitHub Boards&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyter/pr-triage-board-bot"&gt;pr-triage-board-bot&lt;/a&gt; automatically maintains PR Triage Boards for a Github organization. You can check out the current boards to get a sense of how it looks: &lt;a href="https://github.com/orgs/jupyterhub/projects/4"&gt;JupyterHub&lt;/a&gt;, &lt;a href="https://github.com/orgs/jupyterlab/projects/11"&gt;JupyterLab&lt;/a&gt;, and &lt;a href="https://github.com/orgs/geojupyter/projects/3"&gt;GeoJupyter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For each PR Triage Board, the bot will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add all open, ready for review PRs&lt;/li&gt;
&lt;li&gt;Remove all closed or draft PRs&lt;/li&gt;
&lt;li&gt;Annotate each PR with additional deterministic project fields that allow for sorting and filtering in the project board. To start with, it populates the following fields:
&lt;ol&gt;
&lt;li&gt;Author kind (Maintainer, Seasoned Contributor, First Time Contributor, Bot)&lt;/li&gt;
&lt;li&gt;Size (Number of lines touched)&lt;/li&gt;
&lt;li&gt;Date it was opened&lt;/li&gt;
&lt;li&gt;If a maintainer has already interacted with the PR (One, Many, None)&lt;/li&gt;
&lt;li&gt;Are there merge conflicts?&lt;/li&gt;
&lt;li&gt;What kind of files were mostly changed? (Python, JS, Docs)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Keep this up to date by automatically running every hour via GitHub actions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once these project fields are populated automatically, maintainers can create different Project Views for themselves to help with different workflows via filters and sorting. For example, &lt;a href="https://github.com/orgs/jupyterhub/projects/4"&gt;the JupyterHub board&lt;/a&gt; contains the following views:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;PRs split by author kind, sorted by newness and size so we can try to respond to PRs from new users as early as possible&lt;/li&gt;
&lt;li&gt;PRs that have not had a single maintainer interaction on them, so we can acknowledge people’s contributions to us even if we can’t fully review it at the moment&lt;/li&gt;
&lt;li&gt;Bot PRs with all tests passing and no merge conflicts, so we can more easily stay on top of automated updates&lt;/li&gt;
&lt;li&gt;PRs that have been approved by a maintainer but not merged yet, as sometimes a maintainer wants to give others time to object but needs to go back and hit merge.&lt;/li&gt;
&lt;li&gt;PRs that are primarily updating documentation, as the review process for this can be different&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For our original purpose of getting more people to do code review, this board has served well — we can roughly say ‘Pick a PR that looks good to you from the top of the “First Time Contributor” or “Seasoned Contributor” list’, and that relieves me from being the bottleneck quite a bit. Other maintainers are also finding a lot of value in this board. For example, if you only have 15 min, you can probably get a clean bot PR reviewed and merged. Or look for PRs that haven’t been acknowledged by a maintainer and engage with them. Our next step is to help run structured social experiments, where we try to establish specific ceremonies in the open source ecosystem to get specific lists of open PRs (such as “PRs with no maintainer engagement, or PRs older than 2y”) down to zero. Stay tuned to hear more :)&lt;/p&gt;
&lt;p&gt;We are essentially using GitHub Project Fields as a database, adding additional fields that are very valuable to maintainers but not available in GitHub by default. While ideally these would be contributed by us directly into GitHub, only Microsoft has control over what gets added to the product. So we find interesting workarounds like this to accomplish our goals :)&lt;/p&gt;
&lt;h2 id="successful-adoption"&gt;Successful Adoption&lt;/h2&gt;
&lt;p&gt;I was just playing with this, and Raniere from the JupyterHub team spotted it and &lt;a href="https://jupyter.zulipchat.com/#narrow/channel/469744-jupyterhub/topic/.22PR.20triage.20.28experimental.29.22.20project.20on.20GitHub/with/536668935"&gt;asked about it&lt;/a&gt; on Zulip. &lt;a href="https://jasongrout.org/"&gt;Jason Grout&lt;/a&gt; from the JupyterLab team was also super interested, and with contributions from him, the bot quickly got adopted by the JupyterLab GitHub org as well. We cleaned this up a bit more, and found adoption within the GeoJupyter project too with help from &lt;a href="https://mfisher87.github.io/"&gt;Matt Fisher&lt;/a&gt;. As with everything we do at 2i2c, I had tried to design this to be widely useful to many orgs and maintainers rather than just us, and looks like I have wildly succeeded :)&lt;/p&gt;
&lt;p&gt;I am happy to announce today that 2i2c is officially donating pr-triage-board-bot to Project Jupyter! I will still continue to contribute to maintaining the project, and welcome contributions from everyone else too!&lt;/p&gt;
&lt;p&gt;If this looks useful to your open source project, consider adopting it by &lt;a href="https://github.com/jupyter/pr-triage-board-bot?tab=readme-ov-file#set-up"&gt;following these instructions&lt;/a&gt;. The project is still fairly new, so if you run into issues please let us know on the &lt;a href="https://jupyter.zulipchat.com/"&gt;Project Jupyter Zulip chat&lt;/a&gt; or by &lt;a href="https://github.com/jupyter/pr-triage-board-bot/issues"&gt;opening an issue&lt;/a&gt;.&lt;/p&gt;
</content><category term="community"/><category term="JupyterHub"/></entry><entry><title>Simplifying and speeding up Binder builds with BuildKit</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/simplifying-and-speeding-up-binder-builds-with-buildkit/" rel="alternate"/><published>2025-03-03T14:05:00+00:00</published><updated>2025-03-03T14:05:00+00:00</updated><author><name>Chris Holdgraf</name></author><id>tag:jasongrout.github.io,2025-03-03:/medium-archive/pelican/posts/2025/simplifying-and-speeding-up-binder-builds-with-buildkit/</id><summary type="html">&lt;p&gt;The Binder Project allows users to build reproducible, sharable environments for interactive computing. To accomplish this, Binder uses a…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The &lt;a href="http://mybinder.org"&gt;Binder Project&lt;/a&gt; allows users to build reproducible, sharable environments for interactive computing. To accomplish this, Binder uses a tool called &lt;a href="https://repo2docker.readthedocs.io/"&gt;repo2docker&lt;/a&gt; to generate an executable Docker image using the &lt;a href="https://repo2docker.readthedocs.io/en/latest/specification.html"&gt;Reproducible Execution Environment Specification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/simplifying-and-speeding-up-binder-builds-with-buildkit/images/001-1_NOEeFGGHdpGZMHSq5ykl9A.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The first time a repository is launched on Binder, repo2docker must build the reproducible environment for it. This process can take a long time because of all the dependencies that need to be installed and turned into the image. As a result, Binder launches can feel slow and clunky, which is a poor UX for workflows that are designed around quick interactive sessions.&lt;/p&gt;
&lt;p&gt;repo2docker was built several years ago, and followed patterns that were commonplace at the time. However, in the past few years the Docker community has made significant advances in optimizing the image building process. One-such improvement is the creation of &lt;a href="https://docs.docker.com/build/buildkit/"&gt;BuildKit&lt;/a&gt;, a replacement for Docker’s historical build system that is much more sophisticated. However, repo2docker hasn’t leveraged these improvements because it was still using the original Docker Build system.&lt;/p&gt;
&lt;p&gt;So, we’ve decided to spend a few cycles modernizing repo2docker’s image building logic by using the more modern &lt;a href="https://docs.docker.com/build/buildkit/"&gt;BuildKit API&lt;/a&gt; (via ‘docker buildx build’). This allows for optimizations like build parallelization, better build caching, and supporting some &lt;code&gt;Dockerfile&lt;/code&gt; features that Binder didn’t support earlier (particularly, &lt;code&gt;COPY --chown&lt;/code&gt;). It also lays a foundation for significantly simplifying the repo2docker build infrastructure and leveraging more of BuildKit’s parallelization functionality. For example, we’d like to &lt;a href="https://docs.docker.com/build/builders/drivers/kubernetes/"&gt;leverage BuildKit’s Kubernetes driver&lt;/a&gt; which distributes builds much more efficiently and in parallel.&lt;/p&gt;
&lt;p&gt;Authors of Binder repositories won’t need to take any action¹, and they’ll simply notice that mybinder.org (and any other community-run BinderHub instance) will be a bit snappier at building images.&lt;/p&gt;
&lt;p&gt;If you’d like to learn more about the changes that enabled this, &lt;a href="https://github.com/jupyterhub/mybinder.org-deploy/pull/3225"&gt;check out this mybinder.org pull request&lt;/a&gt; which has links to the repo2docker pull requests that added this functionality. We’re excited keep improving Binder, and are hopeful that this makes the experience of using mybinder.org and community Binders a little bit better.&lt;/p&gt;
&lt;h2 id="authors-and-acknowledgements"&gt;Authors and acknowledgements&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://github.com/yuvipanda"&gt;Yuvi Panda&lt;/a&gt; is a co-founder and the Technical Lead at &lt;a href="http://2i2c.org"&gt;2i2c&lt;/a&gt;. He is passionate about building participatory open infrastructure for scientific &amp;amp; educational use cases. He is a Project Jupyter team member primarily focused on infrastructure related projects (JupyterHub, Binder, etc). He also wrote most of the code for this &lt;code&gt;docker buildx&lt;/code&gt; transition, and shepherded it through to deployment on mybinder.org as well. He is ex-Wikimedia and ex-GNOME. Let’s eliminate accidental complexities wherever we find them.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://chrisholdgraf.com"&gt;Chris Holdgraf&lt;/a&gt; is a co-founder and the Executive Director of &lt;a href="http://2i2c.org"&gt;2i2c&lt;/a&gt;. He is on the Executive Council of Project Jupyter, and co-leads the &lt;a href="https://jupyterhub-team-compass.readthedocs.io/en/latest/team.html#jupyterhub-team"&gt;JupyterHub and Binder team&lt;/a&gt; as well as the &lt;a href="https://compass.jupyterbook.org"&gt;Jupyter Book team&lt;/a&gt;). He was previously a post-doctoral researcher in the Department of Statistics at &lt;a href="https://www.berkeley.edu/"&gt;UC Berkeley&lt;/a&gt;, and a Community Architect with the &lt;a href="https://data.berkeley.edu/"&gt;Division of Data Science&lt;/a&gt; at Berkeley. He’s interested in using open infrastructure to support interactive computing workflows in research and education.&lt;/p&gt;
&lt;p&gt;Many thanks to &lt;a href="https://github.com/minrk"&gt;@minrk&lt;/a&gt;, &lt;a href="https://github.com/manics"&gt;@manics&lt;/a&gt;, and &lt;a href="https://github.com/consideRatio"&gt;@consideRatio&lt;/a&gt; for their help reviewing and shaping this work.&lt;/p&gt;
&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;
&lt;p&gt;¹ Unless they were relying on undocumented implementation details of the old builder — in particular, the presence of a &lt;code&gt;/.dockerenv&lt;/code&gt; file to detect if you are running in repo2docker &lt;a href="https://github.com/scikit-learn/scikit-learn/pull/30835"&gt;scikit-learn/scikit-learn#30835&lt;/a&gt; has an example.&lt;/p&gt;
</content><category term="Binder"/><category term="cloud computing"/><category term="JupyterHub"/><category term="reproducibility"/></entry><entry><title>Join JupyterHub on Zulip chat!</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/join-jupyterhub-on-zulip-chat/" rel="alternate"/><published>2025-02-06T16:05:00+00:00</published><updated>2025-02-06T16:05:00+00:00</updated><author><name>Sarah Gibson</name></author><id>tag:jasongrout.github.io,2025-02-06:/medium-archive/pelican/posts/2025/join-jupyterhub-on-zulip-chat/</id><summary type="html">&lt;p&gt;The JupyterHub team have just moved all their chat to a Zulip channel!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/join-jupyterhub-on-zulip-chat/images/001-1_szQUtcTU4TbmrkGrSNzBpw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The JupyterHub team have just moved all their chat to a &lt;a href="https://jupyter.zulipchat.com/#narrow/channel/469744-jupyterhub"&gt;Zulip channel&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://jupyter.zulipchat.com"&gt;Jupyter Zulip instance&lt;/a&gt; is a Jupyter-wide chat space allowing cross-team communication in channels, some of which can be viewed publicly without an account.&lt;/p&gt;
&lt;p&gt;For the JupyterHub team, we have found better engagement from team members and across the wider eco-system by using the Zulip instance. To that end, we recently updated all of our Gitter rooms to “invite-only” so no new members could join, and left a pinned message with a link to the JupyterHub channel on Zulip and an invite to join the conversation there. We have also updated any of our repositories that had Gitter links to point to Zulip as well.&lt;/p&gt;
&lt;p&gt;If you would like to keep in touch with the JupyterHub, we would love to hear from you over on &lt;a href="https://jupyter.zulipchat.com/#narrow/channel/469744-jupyterhub"&gt;Zulip&lt;/a&gt;!&lt;/p&gt;
</content><category term="community"/><category term="JupyterHub"/></entry><entry><title>Desktop GIS software in the cloud with JupyterHub: A QGreenland workshop success story</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/desktop-gis-software-in-the-cloud-with-jupyterhub/" rel="alternate"/><published>2023-08-02T16:23:00+00:00</published><updated>2023-08-02T16:51:00+00:00</updated><author><name>Matt Fisher</name></author><id>tag:jasongrout.github.io,2023-08-02:/medium-archive/pelican/posts/2023/desktop-gis-software-in-the-cloud-with-jupyterhub/</id><summary type="html">&lt;p&gt;A QGreenland workshop success story&lt;/p&gt;
</summary><content type="html">&lt;p&gt;👋 We are Trey Stafford and Matt Fisher, co-authors of the &lt;a href="https://github.com/nsidc/qgreenland"&gt;QGreenland&lt;/a&gt; data package’s source code. This year, we had the pleasure of running a hands-on geospatial data and open science &lt;a href="http://qgreenland-workshop-2023-researcher.github.io/"&gt;QGreenland Researcher Workshop&lt;/a&gt;. It was important for attendees to participate in the workshop in a hands-on way while minimizing the negative impacts of installing software, requiring expensive personal computers, and troubleshooting unique computer configurations. We felt a &lt;a href="https://jupyter.org/hub"&gt;JupyterHub&lt;/a&gt; was a good fit for our workshop for this reason, if it could accommodate our need for running QGIS — a desktop application.&lt;/p&gt;
&lt;p&gt;In this blog post, we will introduce QGreenland, describe our experience using JupyterHub in the cloud for our workshop’s computing environment, and discuss challenges we overcame to enable our attendees to use QGIS in a cloud graphical desktop environment. Finally, we will highlight some workshop outcomes and discuss opportunities for enhancement based on new developments in the Jupyter ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In our workshop, 25–30 international learners (including from Germany, India, France, Canada, Poland, and the United States) used &lt;em&gt;QGIS&lt;/em&gt; in a &lt;em&gt;JupyterHub&lt;/em&gt;’s browser-based Linux desktop environment to collaboratively test, explore, visualize, and process Earth science data simultaneously with the same user experience they expect from using &lt;em&gt;QGIS&lt;/em&gt; on their personal computers! Better yet, getting started was as simple as logging in.&lt;/strong&gt; Our workshop was a success story not just in education, but also in open source and collaborative development, and we want to share what we learned.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A map depicting Greenland is displayed within QGIS software running in a cloud-based desktop environment. A visualization of September 2012 Arctic sea ice concentration is overlaid." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/desktop-gis-software-in-the-cloud-with-jupyterhub/images/001-1_ZG76hcKdS3-4PppQ6lSnuQ.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Note the browser tabs at the top of this screenshot; this is a full desktop-based GIS environment running QGreenland in the cloud!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The JupyterHub used by the QGreenland 2023 Researcher Workshop was generously provided by the &lt;a href="https://cryointhecloud.com/"&gt;NASA CryoCloud&lt;/a&gt; team, whose mission is to help researchers transition to cloud-based collaboration.&lt;/p&gt;
&lt;h2 id="about-qgreenland"&gt;About QGreenland&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://qgreenland.org/"&gt;QGreenland&lt;/a&gt; is an open-source Greenland-focused geospatial data package for &lt;a href="https://qgis.org/en/site/"&gt;QGIS&lt;/a&gt;, a community-owned graphical Geographic Information System (GIS) platform. Researchers and members of the public leverage QGreenland’s ready-to-use interdisciplinary datasets to do field planning, teach about glaciers, and much more.&lt;/p&gt;
&lt;p&gt;QGreenland’s MIT-licensed &lt;a href="https://github.com/nsidc/qgreenland/"&gt;source code&lt;/a&gt; uses community-maintained open software like &lt;a href="https://gdal.org/index.html"&gt;GDAL&lt;/a&gt; and &lt;a href="https://qgis.org/pyqgis/latest"&gt;PyQGIS&lt;/a&gt; to automate data normalization and populate the QGIS project with important information like data provenance and the order of layers in the QGIS Layers Panel. Check out &lt;a href="https://qgreenland.readthedocs.io"&gt;our documentation&lt;/a&gt; to learn more! QGreenland also has a &lt;a href="https://www.youtube.com/@qgreenland/videos"&gt;YouTube channel&lt;/a&gt; with tutorials produced by &lt;a href="https://cires.colorado.edu/outreach/programs/qgreenland"&gt;CIRES Education and Outreach&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A map depicting Greenland is displayed within QGIS software running in a cloud-based desktop environment. Visualizations of bathymetric depth, Greenland ice sheet thickness, and Arctic sea routes are overlaid." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/desktop-gis-software-in-the-cloud-with-jupyterhub/images/002-0__t2MDU659mlYEvxD.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;em&gt;Once QGIS is installed, opening QGreenland is as easy as double-clicking the included “.qgs” file. Here, a representative view of QGreenland v3 alpha in QGIS is displayed with newly updated layers: Arctic sea routes (National Geospatial Intelligence Agency), bathymetric depth (General Bathymetric Chart of the Oceans (GEBCO)), and ice thickness (IceBridge BedMachine Greenland v5) layers.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Based on user research, QGreenland has enabled:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the public to more easily access data gathered by researchers visiting Greenland: &lt;em&gt;“In Greenland, people are often asking, ‘how can we find the data the foreign scientists bring back from Greenland?’ Now we can directly utilize much of it.”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;researchers to plan field work: &lt;em&gt;“Being able to use QGreenland at our field station was critical to our research process!”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;educators to develop interactive lessons about Greenland and climate change: &lt;em&gt;“…using QGreenland for presentations because it is presentation quality already.”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="qgreenlands-2023-researcher-workshop"&gt;QGreenland’s 2023 researcher workshop&lt;/h2&gt;
&lt;p&gt;One of the QGreenland team’s most important forms of direct user interaction and support is facilitating workshops. Most recently, we hosted a 3-day (total of 9 hours) virtual workshop for researchers focused on working with geospatial data in an open science framework. All of the &lt;a href="https://qgreenland-workshop-2023-researcher.github.io/content/materials.html"&gt;materials&lt;/a&gt; covered in the workshop were built &lt;a href="https://qgreenland-workshop-2023-researcher.github.io/content/built-with-open-source.html"&gt;using open-source tools&lt;/a&gt; and are MIT-licensed and &lt;a href="https://github.com/qgreenland-workshop-2023-researcher/qgreenland-workshop-2023-researcher.github.io"&gt;published on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="a-personal-computer-in-the-cloud"&gt;A “personal computer” in the cloud&lt;/h3&gt;
&lt;p&gt;We decided early on that we wanted to use JupyterHub to solve the diverse problems that come with “bring your own device” workshops. We experimented with administering our own &lt;a href="http://z2jh.jupyter.org"&gt;JupyterHub on Kubernetes&lt;/a&gt;, but the setup overhead was too high for our short workshop. CryoCloud’s JupyterHub enabled us to avoid this overhead and focus on serving our participants. Because the software that comprises CryoCloud is open-source and developed in collaboration with the communities CryoCloud serves, we could directly contribute to curating a computing environment ideal for our participants.&lt;/p&gt;
&lt;p&gt;JupyterHub is known for providing access to Jupyter Notebooks via JupyterLab, but it turns out it can also be used to host pretty much any interactive web based application! The &lt;a href="https://github.com/jupyterhub/jupyter-server-proxy/"&gt;jupyter-server-proxy&lt;/a&gt; project enables this, and there are additional packages that make running specific applications easier. &lt;a href="https://github.com/jupyterhub/jupyter-rsession-proxy"&gt;jupyter-rsession-proxy&lt;/a&gt; makes it easy to run &lt;a href="https://posit.co/download/rstudio-server/"&gt;RStudio&lt;/a&gt; inside JupyterHub, &lt;a href="https://github.com/betatim/vscode-binder/"&gt;jupyter-vscode-proxy&lt;/a&gt; allows running code-server (fully open source self-hosted version of Visual Studio Code) inside a JupyterHub, etc. Pertinent to our use case is &lt;a href="https://github.com/jupyterhub/jupyter-remote-desktop-proxy"&gt;jupyter-remote-desktop-proxy&lt;/a&gt;, which lets you run a complete Linux desktop environment inside your JupyterHub! This was critical for our workshop, as it allowed us to use QGIS — purely desktop software, not adapted for the web — from inside a web browser. Workshop participants did not need to install anything. This enabled participants to focus on the &lt;em&gt;content&lt;/em&gt; of our workshop rather than the logistics of setting up and debugging tools on their varied machines.&lt;/p&gt;
&lt;p&gt;The CryoCloud JupyterHub enabled each of our workshop participants to provision their own compute environment (JupyterLab + Linux Desktop) with all of our workshop’s dependencies pre-installed. It also set everyone on equitable footing — someone accessing the workshop on a 10 year old laptop would get the same computing resources as someone on a brand new MacBook Pro.&lt;/p&gt;
&lt;h3 id="challenges-scaling-qgreenland"&gt;Challenges scaling QGreenland&lt;/h3&gt;
&lt;p&gt;The CryoCloud JupyterHub already had &lt;em&gt;jupyter-remote-desktop-proxy&lt;/em&gt; and &lt;em&gt;QGIS&lt;/em&gt; installed, so we could validate this approach to our workshop quickly. However, to use &lt;em&gt;QGreenland&lt;/em&gt; at this scale, we needed to solve a couple of usability problems. The first issue was a user experience problem: the operating system did not have appropriate file type associations for &lt;em&gt;QGIS&lt;/em&gt;, so files like the &lt;em&gt;QGreenland&lt;/em&gt; project file would not open in &lt;em&gt;QGIS&lt;/em&gt; when double-clicked in the desktop file browser. We quickly discovered a solution and integrated it with a &lt;a href="https://github.com/CryoInTheCloud/hub-image/pull/59"&gt;simple pull request&lt;/a&gt; to the Docker image we were using.&lt;/p&gt;
&lt;p&gt;The second problem was a performance problem: QGIS would take several minutes to open QGreenland from the hub’s shared storage drive. After some investigation, it turned out this was due to us loading multiple GB of data from an NFS share! While a long term solution might involve getting QGIS to load data directly from cloud object storage (like S3), we instead decided to go a different route — provision each user a small, fast and temporary &lt;a href="https://aws.amazon.com/ebs/"&gt;Elastic Block Store&lt;/a&gt; disk. At the start of the workshop, we provided all users a small script that would copy the dataset from NFS to this faster disk once, and this drastically reduced load times from about 5 minutes to under 3 seconds! You can follow our debugging process &lt;a href="https://github.com/CryoInTheCloud/hub-image/issues/60"&gt;on this issue&lt;/a&gt;, and find the JupyterHub config used to provision these disks &lt;a href="https://github.com/2i2c-org/infrastructure/blob/5078e5b6e2115930468f910f3095a9ffd1261eb3/helm-charts/basehub/values.yaml#L616"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By overcoming these challenges, we created a smooth, intuitive, and performant computing experience for all of our participants, most of whom had never been exposed to this sort of collaborative computing environment.&lt;/p&gt;
&lt;h3 id="outcomes"&gt;Outcomes&lt;/h3&gt;
&lt;p&gt;The workshop participants engaged in small group work to complete various exercises, group discussions, and data scenarios. Each group produced Jupyter Notebooks and GitHub Discussions posts as deliverables. We created an &lt;a href="https://qgreenland-workshop-2023-researcher.github.io/outcomes/"&gt;outcomes&lt;/a&gt; webpage to summarize our participants’ accomplishments. One highlight was &lt;a href="https://github.com/orgs/qgreenland-workshop-2023-researcher/discussions/categories/fair-care"&gt;participants’ insightful commentary on FAIR &amp;amp; CARE principles.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Based on these outcomes, we consider our workshop a success. While we put in a significant amount of time creating our materials, CryoCloud’s cloud costs and our time investment in preparing computing resources were relatively small. For approximately 25 people, our cloud costs break down to roughly &lt;strong&gt;$1/person/day&lt;/strong&gt;!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The CryoCloud JupyterHub met our workshop needs and provided a delightful experience for administrators and participants alike, and we are excited for what’s next. JupyterLab 4 and jupyter_collaboration v1.0.0, a real-time collaboration extension, were &lt;a href="/posts/2023/jupyterlab-4-0-is-here/"&gt;just announced&lt;/a&gt;, and the CryoCloud team is currently working to integrate these new releases into their hub. Real-time collaboration will enable exciting cloud use cases, like small groups working together on the same notebook without a screen share, or organizers providing technical support in a live notebook. We anticipate running this workshop again. We are excited to use JupyterHub again and look forward to experimenting with these new features!&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;h3 id="reviewers"&gt;Reviewers&lt;/h3&gt;
&lt;p&gt;In alphabetical order, thanks to Twila Moon, Yuvi Panda, Tasha Snow, and Alyse Thurber for their time contributing to this post!&lt;/p&gt;
&lt;h3 id="cryocloud"&gt;CryoCloud&lt;/h3&gt;
&lt;p&gt;Snow, Tasha, Millstein, Joanna, Scheick, Jessica, Sauthoff, Wilson, Leong, Wei Ji, Colliander, James, Pérez, Fernando, James Munroe, Felikson, Denis, Sutterley, Tyler, &amp;amp; Siegfried, Matthew. (2023). CryoCloud JupyterBook (2023.01.26). Zenodo.&lt;a href="https://doi.org/10.5281/zenodo.7576602"&gt;https://doi.org/10.5281/zenodo.7576602&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="2i2c"&gt;2i2c&lt;/h3&gt;
&lt;p&gt;2i2c is a non-profit organization that runs open-source &lt;a href="https://github.com/2i2c-org/infrastructure"&gt;infrastructure&lt;/a&gt; for collaborative computing, and maintains the CryoCloud JupyterHub used in this workshop. You can see the complete configuration of this JupyterHub in this &lt;a href="https://github.com/CryoInTheCloud/hub-image"&gt;public repository&lt;/a&gt;.&lt;/p&gt;
</content><category term="accessibility"/><category term="cloud computing"/><category term="education"/><category term="events"/><category term="geoscience"/><category term="JupyterHub"/><category term="science"/><category term="workshops"/></entry><entry><title>From intern to mentor</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/from-intern-to-mentor/" rel="alternate"/><published>2023-06-19T08:02:00+00:00</published><updated>2023-06-19T08:02:00+00:00</updated><author><name>Georgiana Dolocan</name></author><id>tag:jasongrout.github.io,2023-06-19:/medium-archive/pelican/posts/2023/from-intern-to-mentor/</id><summary type="html">&lt;p&gt;Four years ago, Project Jupyter participated for the first time in the Outreachy program. And at the end of last year, during the December…&lt;/p&gt;
</summary><content type="html">&lt;blockquote&gt;
&lt;p&gt;Four years ago, Project Jupyter participated for &lt;a href="/posts/2018/outreachy-jupyter-supporting-diversity-in-open/"&gt;the first time in the Outreachy program&lt;/a&gt;. And at the end of last year, during the December round, t&lt;a href="/posts/2022/introducing-jupyterhubs-outreachy-interns-december-2022/"&gt;hree more awesome interns joined the project&lt;/a&gt;. Because I was lucky enough to be part of this program both times, first time as an intern and second time as a mentor, in this post I will share the parts that were the most impactful and contributed the most to my growth as both mentor and engineer. I hope this will inspire other interns, to start their journey, but also other community members into becoming mentors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;When I first interacted with the Jupyter community, I remember I was impressed by how welcoming everyone was, how well organized they seemed, how promptly questions got answered and how fast code got reviewed. This was especially impressive during the &lt;a href="https://www.outreachy.org/docs/applicant/#make-contributions"&gt;contribution phase&lt;/a&gt; when prospect interns were everywhere at the same time, we were generating crazy amount of work for mentors and the community when asking for guidance and tackling tasks.&lt;/p&gt;
&lt;p&gt;When the internship ended I realized how much I appreciated this process compared to other internships. The contribution phase made me feel very motivated because of the opportunity to work on &lt;strong&gt;real&lt;/strong&gt; tasks, with &lt;strong&gt;impact&lt;/strong&gt; for the entire community.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;All the work traffic, immense quantity of GitHub notifications, emails, questions, etc., were totally opaque to me as an intern, they seemed to just be magically handled by mentors. I now know this seamless interaction is made of some very special ingredients like team work, time commitment and hard work, nothing magic about it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Scoping the project, the most challenging part&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I believe scoping the project was the part that I found the most challenging as a first time mentor.&lt;/p&gt;
&lt;p&gt;The plan I had in mind for how to create the project roadmap, was to &lt;strong&gt;define a few tasks&lt;/strong&gt; that were not very tightly scoped, &lt;strong&gt;choose one&lt;/strong&gt; to start with, allocate time for the intern to &lt;strong&gt;research&lt;/strong&gt; about it, have synchronous meetings to &lt;strong&gt;collaboratively refine&lt;/strong&gt; it, then &lt;strong&gt;implement&lt;/strong&gt; it, &lt;strong&gt;review&lt;/strong&gt;, and then &lt;strong&gt;repeat&lt;/strong&gt; for next task. And although this was a good first plan, I failed to recognize when it stopped working and didn’t realize it’s important to pause and adapt the plan.&lt;/p&gt;
&lt;p&gt;It took me some time and guidance from others in the community, to realize that sometimes, &lt;strong&gt;constraints are beneficial&lt;/strong&gt; and allowing complete implementation freedom over a task, can be overwhelming. For finding the appropriate &lt;em&gt;[freedom — constraint]&lt;/em&gt; balance I needed to experimented with different task sizes, until tasks had a less overwhelming learning curve. This fosters and grows confidence and improves time management skills.&lt;/p&gt;
&lt;p&gt;I found this to be particularly tricky because this balance can be hard to achieve when creating a project roadmap. The hidden complexities of a project, existing skills, or any prior experience managing a project from scratch, are hard to know and plan around in the very first iteration.&lt;/p&gt;
&lt;p&gt;Another challenge of being a mentor that is usually hidden is the &lt;strong&gt;context switch penalty&lt;/strong&gt; between mentor time and daily job responsibilities. A mentor needs to fine tune the amount of intern interaction and involvement in the project implementation so that context switches from daily responsibilities are as soft as possible. It’s also critical that a mentor has full support and transparent communication with the daily job management so that work responsibilities don’t spill over into mentoring time, otherwise this can lead to stress and a sub-optimal experience for the intern.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Asking questions publicly, the biggest achievement&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The open source comes with a challenge attached to it by default, which is &lt;strong&gt;being comfortable communicating in the open and asking questions publicly&lt;/strong&gt;. The time and work that needs to go into fostering this skill needs to be taken into account from the very beginning and it’s extremely important to consider it from the project proposal and roadmap definition phase. It will help set the correct project expectations, and expose an otherwise hidden complexity of open source project maintainability.&lt;/p&gt;
&lt;p&gt;This was a challenge that was more difficult to overcome than expected, because in our case it involved, &lt;strong&gt;a first time mentor, a first time intern&lt;/strong&gt;, and a project that required diving into the codebase of lots of other different projects in the ecosystem, so &lt;strong&gt;guidance from the entire community&lt;/strong&gt; and asking questions was crucial.&lt;/p&gt;
&lt;p&gt;My intern, Sheila was very determined and courageous and she received great care and support from the entire community in overcoming this obstacle. She is now a key maintainer &lt;a href="https://github.com/jupyterhub/pytest-jupyterhub"&gt;of the internship project&lt;/a&gt; she worked on, asking and answering questions without issues. Checkout out her many achievements captured in &lt;a href="https://hackmd.io/cvqPq6XVQbaYVndwFvJ0Kw?view"&gt;this list (not complete)&lt;/a&gt; and &lt;a href="https://cfp.jupytercon.com/2023/talk/HNTKXF/"&gt;her presentation&lt;/a&gt; about the Reusable JupyterHub Pytest Plugin at JupyterCon. A recording of her talk should soon be available on &lt;a href="https://www.youtube.com/@JupyterCon"&gt;JupyterCon’s YouTube channel.&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mentor training, mentor shadowing, co-mentoring, the best allies&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am extremely grateful to have had the support of the other mentors and the community during theses three mentoring months.&lt;/p&gt;
&lt;p&gt;In particular, I am very grateful for Sarah Gibson, &lt;a href="/posts/2021/czi-awards-three-eoss-grants-to-jupyter-community/"&gt;the Jupyter strategic lead&lt;/a&gt; and our Community Coordinator with Outreachy, for organizing for us to get &lt;strong&gt;mentor training&lt;/strong&gt; beforehand through the &lt;a href="https://openlifesci.org/"&gt;OLS community&lt;/a&gt;. I found that to be very helping and ground setting and it made me feel more empowered.&lt;/p&gt;
&lt;p&gt;I also appreciated and found really useful the &lt;strong&gt;mentor shadowing sessions&lt;/strong&gt; that I did with Sarah and the &lt;strong&gt;demo session&lt;/strong&gt; that Min RK hold with all the interns and mentors. They were both great opportunities for me to observe and learn from the other mentors and get feedback from them.&lt;/p&gt;
&lt;p&gt;Co-mentoring was something that was considered during this round, but because we wanted to select all these great interns we ended up not mentoring in teams. But this appears as a great approach of introducing first-time mentors.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It was all worth it, the takeway&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The JupyterHub project got to improve its documentation, accessibility and testing thanks to this program and to the three amazing interns that worked with us during this round ❤️. In addition, this internship round helped us create &lt;a href="https://jupyterhub-outreachy.readthedocs.io/en/latest/"&gt;a place where people can learn about JupyterHub’s experience with Outreachy&lt;/a&gt; and &lt;a href="https://github.com/jupyterhub/jupyterhub-python-repo-template"&gt;a template repository&lt;/a&gt; to be used for quickly generating other JupyterHub Python sub-repositories that follow the standards of the organization.&lt;/p&gt;
&lt;p&gt;On the personal side now that the internship is over, I can happily say that it was very enlightening and humbling to be on the mentoring side as well. Even if it’s challenging, you get an amazing feeling when you give back and channel all the effort and kindness that shaped you and that you received as a mentee from other people.&lt;/p&gt;
</content><category term="community"/><category term="JupyterHub"/><category term="Outreachy"/></entry><entry><title>Accurately counting Daily, Weekly &amp; Monthly active users on JupyterHub</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/accurately-counting-daily-weekly-monthly-active-users/" rel="alternate"/><published>2023-03-27T09:16:00+00:00</published><updated>2023-03-27T09:16:00+00:00</updated><author><name>yuvipanda</name></author><id>tag:jasongrout.github.io,2023-03-27:/medium-archive/pelican/posts/2023/accurately-counting-daily-weekly-monthly-active-users/</id><summary type="html">&lt;p&gt;Being able to say ‘we served X unique users over the last month’ (the Monthly Active User metric) is very helpful when advocating for…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Being able to say ‘we served X unique users over the last month’ (the &lt;a href="https://en.wikipedia.org/wiki/Active_users"&gt;Monthly Active User metric&lt;/a&gt;) is very helpful when advocating for resources for a JupyterHub your organization is running. However, until now, figuring out that number &lt;em&gt;accurately&lt;/em&gt; has been difficult, requiring keeping and analysing JupyterHub logs.&lt;/p&gt;
&lt;p&gt;That changes with JupyterHub 3.1! &lt;a href="https://github.com/jupyterhub/jupyterhub/pull/4214"&gt;This Pull Request&lt;/a&gt; adds daily, weekly, and monthly active user metrics to JupyterHub, accessible via the prometheus interface by hitting the &lt;code&gt;/metrics&lt;/code&gt; URL on your JupyterHub. These metrics are calculated by JupyterHub itself, and are pretty accurate as it already keeps track of when a user was last active. This relies metric on each JupyterHub user matching an actual user, so if you are using your JupyterHub deployment purely as an API with ephemeral users (as &lt;a href="https://github.com/jupyterhub/binderhub/"&gt;binderhub&lt;/a&gt; does, for example) or delete inactive users, these will not be useful numbers.&lt;/p&gt;
&lt;p&gt;Ideally, you should have a &lt;a href="https://prometheus.io/"&gt;prometheus&lt;/a&gt; instance to scrape and store metrics from your JupyterHub over time, so you can track this metric over time. However, in a pinch, you can also go directly to &lt;code&gt;https://&amp;lt;your-hub-url&amp;gt;/hub/api/metrics&lt;/code&gt; and see the current value of these metrics. They are in the &lt;a href="https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md"&gt;prometheus exposition format&lt;/a&gt;, but you can look for something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# HELP jupyterhub_active_users number of users who were active in the given time period&lt;/span&gt;
&lt;span class="c1"&gt;# TYPE jupyterhub_active_users gauge&lt;/span&gt;
jupyterhub_active_users&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;24h&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;610&lt;/span&gt;.0
jupyterhub_active_users&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;7d&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2800&lt;/span&gt;.0
jupyterhub_active_users&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;30d&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4526&lt;/span&gt;.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This denotes that this particular hub had 610 daily active users (active over the last 24 hours), 2800 weekly users (over last 7d) and 4526 monthly ones (over the last 30 days). Very helpful if you want to quickly add numbers to a report :)&lt;/p&gt;
&lt;p&gt;Note that depending on your hub’s configuration, access to the &lt;code&gt;/metrics&lt;/code&gt; endpoint might &lt;a href="https://jupyterhub.readthedocs.io/en/stable/api/app.html#jupyterhub.app.JupyterHub.authenticate_prometheus"&gt;require authentication&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you &lt;em&gt;do&lt;/em&gt; have a prometheus installation for your JupyterHub, you may benefit from deploying &lt;a href="https://github.com/jupyterhub/grafana-dashboards"&gt;the JupyterHub Grafana Dashboards&lt;/a&gt;. These are targeted at installations of &lt;a href="https://z2jh.jupyter.org"&gt;zero-to-jupyterhub on kubernetes&lt;/a&gt;, and provide a lot of useful usage &amp;amp; diagnostic information.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Grafana Dashboard showing “Hub Usage Stats” for a JupyterHub deployment. Four panels, clockwise: Current Active Users, Daily Active Users, Weekly Active Users, Monthly Active Users." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/accurately-counting-daily-weekly-monthly-active-users/images/001-0_VYHSKwQ5Jp-L3ArS.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Grafana Dashboard showing “Hub Usage Stats” for a JupyterHub deployment. Four panels, clockwise: Current Active Users, Daily Active Users, Weekly Active Users, Monthly Active Users.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Happy report writing!&lt;/p&gt;
</content><category term="DevOps"/><category term="JupyterHub"/></entry><entry><title>Updating defaults on mybinder.org</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/updating-defaults-on-mybinder-org/" rel="alternate"/><published>2023-03-09T08:31:00+00:00</published><updated>2023-03-10T15:53:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2023-03-09:/medium-archive/pelican/posts/2023/updating-defaults-on-mybinder-org/</id><summary type="html">&lt;p&gt;This week, we are deploying some updates to repo2docker, the image-building tool that underpins mybinder.org. This may affect your repos…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/updating-defaults-on-mybinder-org/images/001-1_0dd02RZZwHXDo-Fr3tdB5w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;This week, we are deploying some updates to &lt;a href="http://repo2docker.readthedocs.io"&gt;repo2docker&lt;/a&gt;, the image-building tool that underpins &lt;a href="https://mybinder.org/"&gt;mybinder.org&lt;/a&gt;. This may affect your repos. Please &lt;a href="https://discourse.jupyter.org/c/binder/12"&gt;get in touch&lt;/a&gt; if you have problems! Hopefully this will make things better, more stable, and &lt;em&gt;less&lt;/em&gt; surprising overall, but change always breaks &lt;em&gt;something&lt;/em&gt;, however hard we work to keep things working.&lt;/p&gt;
&lt;p&gt;To highlight the changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.10 is now the default Python version&lt;/li&gt;
&lt;li&gt;Python 3.11 is now supported, and new versions of Python should be supported more quickly&lt;/li&gt;
&lt;li&gt;If a repo requests an old version of Python (≤ 3.6), it will be installed in a separate environment, as we’ve been doing with Python 2.7 for a long time.&lt;/li&gt;
&lt;li&gt;The server implementation is switched from &lt;code&gt;jupyter-notebook&lt;/code&gt; to &lt;code&gt;jupyter-server&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More detail on each change, the reasons behind them, and their consequences below.&lt;/p&gt;
&lt;h2 id="python-311-and-separate-envs-for-legacy-python-support"&gt;Python 3.11 and separate envs for legacy Python support&lt;/h2&gt;
&lt;p&gt;When building an image, repo2docker installs packages in one or two environments — the &lt;strong&gt;kernel&lt;/strong&gt; environment where your code runs, and the &lt;strong&gt;server&lt;/strong&gt; environment, which runs the Jupyter server. If you’re using a reasonably supported version of Python, these are actually the same environment. This is the vast majority of repositories on mybinder.org. In order to function properly, we have certain requirements of what goes in the ‘server’ environment. Eventually, those requirements start making it difficult to setup the server environment with an outdated version of Python, as packages march ahead with their minimum required Python (The widely adopted &lt;a href="https://numpy.org/neps/nep-0029-deprecation_policy.html#support-table"&gt;NEP 29&lt;/a&gt; suggested dropping Python 3.7 support in December, 2021). Our solution for this has been to separate the kernel environment from the server environment, so they don’t create a conflict. Until recently, only Python 2 was handled this way (most packages dropped Python 2 support years ago). As part of adding support for Python 3.11, we have expanded this behavior to include Python ≤3.6, so if you are using a ‘legacy Python’, currently defined to be Python ≤3.6, your requested packages will be installed in the kernel environment instead of the server environment. This &lt;em&gt;should&lt;/em&gt; only have a noticeable effect if you are installing something with server extension components , which includes anything modifying the JupyterLab or notebook interfaces— those will not be available in the server environment. To address this, you will have to upgrade Python &lt;em&gt;or&lt;/em&gt; handle the server extension installation in &lt;code&gt;postBuild&lt;/code&gt; . &lt;a href="https://github.com/jupyterhub/repo2docker/pull/1239"&gt;PR adding Python 3.11 and adding Python ≤3.6 to legacy Python versions&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="default-python-is-now-310"&gt;Default Python is now 3.10&lt;/h2&gt;
&lt;p&gt;The default Python for repo2docker and thereby mybinder.org is now Python 3.10, upgraded from 3.7. 3.10 will be used when a repository does not specify which version of Python to use. Following Python’s own annual release cadence, we plan to update this more regularly in the future, lagging behind Python itself by a version or two. This has no effect on your repos if they specify a Python version, which is why we &lt;a href="https://repo2docker.readthedocs.io/en/latest/howto/languages.html#specifying-a-version-of-python"&gt;&lt;strong&gt;encourage you to specify your Python version&lt;/strong&gt;&lt;/a&gt;! If you don’t, then your Python version &lt;em&gt;will&lt;/em&gt; change over time, probably about once a year. &lt;a href="https://github.com/jupyterhub/repo2docker/pull/1219"&gt;PR switching the default Python&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="now-launching-jupyter-server"&gt;Now launching Jupyter Server&lt;/h2&gt;
&lt;p&gt;The default UI for mybinder.org has been JupyterLab for some time, with the classic notebook still available if you specify a url of &lt;code&gt;/tree/&lt;/code&gt;or &lt;code&gt;/notebooks/mynb.ipynb&lt;/code&gt;. When we rolled out that change, we kept the default &lt;em&gt;server&lt;/em&gt; as the &lt;code&gt;jupyter-notebook&lt;/code&gt; server, for improved compatibility with existing server extensions. As the newer Jupyter Server has taken over, the situation is becoming reversed — using the older &lt;code&gt;notebook&lt;/code&gt; server is now the &lt;em&gt;cause&lt;/em&gt; of compatibility issues with server extensions, so we are switching the server to launch &lt;code&gt;jupyter-server&lt;/code&gt; instead of &lt;code&gt;jupyter-notebook&lt;/code&gt;. The classic UI is still available at the same URLs, thanks to &lt;a href="https://nbclassic.readthedocs.io"&gt;nbclassic&lt;/a&gt;. This &lt;em&gt;should&lt;/em&gt; have no visible effects, as &lt;code&gt;jupyter-server&lt;/code&gt; and &lt;code&gt;nbclassic&lt;/code&gt; make a great deal of effort to keep classic notebook extensions working, but some extensions may need an update for jupyter-server compatibility if they haven’t been updated already. Because of where the server is specified, there is no supported way for images to express a preference for the deprecated &lt;code&gt;notebook&lt;/code&gt; server (&lt;a href="https://github.com/jupyterhub/binderhub/pull/1635"&gt;PR switching the default server command&lt;/a&gt;.&lt;/p&gt;
</content><category term="Binder"/><category term="Docker"/><category term="JupyterHub"/><category term="reproducibility"/></entry><entry><title>Online Collaboration Café launch: JupyterHub team meetings to become more collaborative spaces!</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/online-collaboration-cafe-launch-jupyterhub-team/" rel="alternate"/><published>2023-02-27T08:49:00+00:00</published><updated>2023-02-27T08:49:00+00:00</updated><author><name>Sarah Gibson</name></author><id>tag:jasongrout.github.io,2023-02-27:/medium-archive/pelican/posts/2023/online-collaboration-cafe-launch-jupyterhub-team/</id><summary type="html">&lt;p&gt;The JupyterHub team are refactoring our monthly meeting into a collaborative, co-working space that is more accessible and inclusive to…&lt;/p&gt;
</summary><content type="html">&lt;figure&gt;
&lt;img alt="A cartoon of a person and their black and white dog, sat at a desk using a laptop. Squares containing profiles of different people wearing headphones are emanating from the laptop, indicating that the person at the desk is participating in a remote, collaborative activity." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/online-collaboration-cafe-launch-jupyterhub-team/images/001-1_9GSBPtjxJNpvUXCFcaL9ng.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;em&gt;This illustration is created by Scriberia with The Turing Way community. Used under a CC-BY 4.0 licence. DOI:&lt;/em&gt; &lt;a href="https://doi.org/10.5281/zenodo.3332807"&gt;&lt;em&gt;10.5281/zenodo.3332807&lt;/em&gt;&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The JupyterHub team are refactoring our monthly meeting into a collaborative, co-working space that is more accessible and inclusive to those who are just getting started in the community — an Online Collaboration Café! This blog post aims to explain what that means, and what to expect when you attend. We plan to hold our first Online Collaboration Café on &lt;strong&gt;21st March 2023&lt;/strong&gt;. Please come along and let us know your feedback!&lt;/p&gt;
&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;The team meeting will be held in a new format for 2 hours in the original time slot. Instead of a typical meeting style with an agenda, the group will be divided into breakout rooms to work more collaboratively on ideas and perhaps begin actioning them. There will always be breakout rooms available for onboarding newcomers and those who wish some dedicated time to undertake maintenance tasks, as well as a quiet working space in the main room. Participants should feel free to swap rooms and join/drop out as they need.&lt;/p&gt;
&lt;h2 id="why-the-change"&gt;Why the change?&lt;/h2&gt;
&lt;p&gt;The JupyterHub community often cite the team meetings as a touchpoint for newcomers to familiarise themselves with the JupyterHub project. However, this is not always the case. The team meetings have an emergent agenda built by the community members — which is great! — but it also means that it is a potluck as to whether the meeting you happen to attend will actually be useful depending on the agenda, and a newcomer may have to attend multiple meetings over a long period before feeling comfortable to ask questions and know where they can begin to help.&lt;/p&gt;
&lt;p&gt;By reformatting the meeting into a collaborative co-working space using breakout rooms, we can cater for both the need of newcomers to be oriented to the project, and for the community to discuss in-depth topics in an emergent nature.&lt;/p&gt;
&lt;h2 id="what-is-an-online-collaboration-cafe"&gt;What is an Online Collaboration Café?&lt;/h2&gt;
&lt;p&gt;The Collaboration Café is a concept that was developed by &lt;a href="https://the-turing-way.netlify.app"&gt;&lt;em&gt;The Turing Way&lt;/em&gt; community&lt;/a&gt;, and you can read more about it and how they are run in their &lt;a href="https://the-turing-way.netlify.app/community-handbook/coworking/coworking-collabcafe.html#chairing-an-online-collaboration-cafe"&gt;Community Handbook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In short, an Online Collaboration Café utilises breakout rooms and &lt;a href="https://en.wikipedia.org/wiki/Pomodoro_Technique"&gt;pomodoro sprints&lt;/a&gt; to allow groups of community members to work together on a topic that best suits them. The space between the pomodoros are used as shareouts to the rest of the group, or as biobreaks.&lt;/p&gt;
&lt;h2 id="what-are-the-logistics"&gt;What are the logistics?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;This is an &lt;a href="https://en.wikipedia.org/wiki/Pomodoro_Technique"&gt;online&lt;/a&gt; café. We will meet on a video call.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We will be using the same alternating time slot that the team meetings used to occur in, but the slot will now be two hours. You can view the &lt;a href="https://jupyterhub-team-compass.readthedocs.io/en/latest/meetings/index.html#meeting-calendars"&gt;Team Calendar&lt;/a&gt; for details.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A chair will always be present in the main room to manage breakout rooms and greet folk who may arrive mid-sprint.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There will always be some default breakout rooms available:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Onboarding:&lt;/strong&gt; For new arrivals to the community. A member of the team will be available on the call to support anyone wanting to learn more about collaborating on GitHub, getting a virtual tour of our GitHub organisation, and help you in any way we can.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance:&lt;/strong&gt; Folks working in this room will be triaging issues, reviewing pull requests, and other maintenance-related activities across the JupyterHub organisation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quiet working in the main room:&lt;/strong&gt; If you would just like some quiet time dedicated to any JupyterHub-related work you have, you are invited to hang out in the main room (and keep the chair company 😉)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-will-happen-when-i-attend"&gt;What will happen when I attend?&lt;/h2&gt;
&lt;p&gt;When you arrive to the Online Collaboration Café, there will first be some housekeeping, such as introductions, Code of Conduct review. There will then be some goal setting around what folks are hoping to achieve during the time. These goals will determine the topics for the breakout rooms. Any attendee is welcome to: suggest their own topic, join a suggested topic, join one of the default rooms, or work quietly in the main room. The breakouts will then run in sprints with breaks to share their progress. The Café is closed with some reflections, for example: how did your work progress, what should the project think about working towards next?&lt;/p&gt;
&lt;p&gt;Here is an example schedule of how an Online Collaboration Café could happen inspired by &lt;a href="https://the-turing-way.netlify.app/community-handbook/coworking/coworking-collabcafe.html#schedule"&gt;&lt;em&gt;The Turing Way&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time: Activity&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Start: Welcome, CoC review&lt;/li&gt;
&lt;li&gt;10 mins: Introductions and goal setting&lt;/li&gt;
&lt;li&gt;20 mins: Pomodoro 1&lt;/li&gt;
&lt;li&gt;5 mins: Break&lt;/li&gt;
&lt;li&gt;20 mins: Pomodoro 2&lt;/li&gt;
&lt;li&gt;5 mins: Break&lt;/li&gt;
&lt;li&gt;20 mins: Open discussion: celebrations, reflections, future plans&lt;/li&gt;
&lt;li&gt;5 mins: Close&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-hours-seems-like-a-long-time-why-not-multiple-meetings"&gt;2 hours seems like a long time… Why not multiple meetings?&lt;/h2&gt;
&lt;p&gt;Various individual meetings covering separate topics, such as onboarding and maintenance, were considered. However given that the majority of JupyterHub’s community are volunteers, it didn’t seem practical to fill up the calendar with lots of meetings. Another reason to use parallel breakout rooms is that participants can swap rooms if the current conversation doesn’t appeal to them, as if you were at tables in a real café. &lt;em&gt;This practice is highly encouraged.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The extension to 2 hours is important because this is a pivot towards collaboration and co-working, and we want to provide a dedicated time for folk to achieve that and begin actioning ideas. However it is a large chunk of time to devote when we are all busy, and so it is not a requirement to arrive on time and participate for the full 2 hours. &lt;em&gt;Joining when you can and dropping when you need to is encouraged and doesn’t require apologies.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="will-there-be-cake-at-this-cafe"&gt;Will there be cake at this café?&lt;/h2&gt;
&lt;p&gt;It is an online café so there will be as many virtual cakes as you like! 🍰🍰🍰&lt;/p&gt;
&lt;p&gt;This is an informal space, so you are encouraged to bring along any beverages and/or snacks. We hope you will join us!&lt;/p&gt;
</content><category term="collaboration"/><category term="community"/><category term="JupyterHub"/></entry><entry><title>Introducing JupyterHub’s Outreachy interns! — December 2022 Cohort</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2022/introducing-jupyterhubs-outreachy-interns-december-2022/" rel="alternate"/><published>2022-12-07T07:45:00+00:00</published><updated>2022-12-07T07:45:00+00:00</updated><author><name>Sarah Gibson</name></author><id>tag:jasongrout.github.io,2022-12-07:/medium-archive/pelican/posts/2022/introducing-jupyterhubs-outreachy-interns-december-2022/</id><summary type="html">&lt;p&gt;As part of the community strategic support project funded by CZI’s EOSS grant series, the JupyterHub sub-project has funding to support…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/introducing-jupyterhubs-outreachy-interns-december-2022/images/001-1_mRtgDdoRwjO3Kb2nSGOsdw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;As part of the &lt;a href="/posts/2021/czi-awards-three-eoss-grants-to-jupyter-community/"&gt;community strategic support project funded by CZI’s EOSS grant series&lt;/a&gt;, the JupyterHub sub-project has funding to support Outreachy interns through four cohorts over the next two years. We would like to introduce you to the first cohort and the projects they will be working on!&lt;/p&gt;
&lt;h2 id="ogoh-blessing-onyowoicho-improve-accessibility-in-jupyterhub"&gt;Ogoh Blessing Onyowoicho — Improve Accessibility in JupyterHub&lt;/h2&gt;
&lt;p&gt;Accessibility is the ability of tools (in our case web tools) to be used by a variety of communities with different disabilities. There are a variety of standards and tools for evaluating and ensuring that a web page can be used effectively by as many people as possible. Work by the &lt;a href="https://jupyter-accessibility.readthedocs.io"&gt;Accessibility team&lt;/a&gt; is ongoing to define a set of standard tools to improve accessibility across the Jupyter ecosystem.&lt;/p&gt;
&lt;p&gt;The JupyterHub project is working to improve the accessibility of its pages to ensure we are providing tools that are as useful as they can be to as many people as we can. During the internship, we will evaluate JupyterHub’s accessibility, find ways to improve it, and integrate accessibility testing into the development process, in collaboration with the Accessibility team, to ensure we do a better job going forward.&lt;/p&gt;
&lt;h3 id="ogoh-blessing-says"&gt;Ogoh Blessing says:&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;My name is Ogoh Blessing Onyowoicho. I am a self-taught Frontend developer based in Lagos, Nigeria and I am an Outreachy intern working on Improving the accessibility of JupyterHub.&lt;br&gt;
I am excited to work on JupyterHub because it is giving me the opportunity to use skills that I have accrued over the years to proffer solutions to problems that affect people’s lives directly. On hearing accessibility, the first thought one has is that it involves building web products that people with special needs can use seamlessly. Though this is part of it, accessibility goes way beyond this. It involves building products that different users (e.g users at different locations, users with different devices etc) can use easily. The thought of contributing to improving the experience of so many people alone excites me.&lt;br&gt;
In the coming months, I hope to learn and continue to hone my skills as I am guided by my mentors and members of the community I get to interact with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="allan-wasega-restructure-and-improve-jupyterhub-documentation-by-implementing-the-diataxis-framework"&gt;Allan Wasega — Restructure and improve JupyterHub documentation by implementing the Diátaxis framework&lt;/h2&gt;
&lt;p&gt;JupyterHub has a range of documentation that covers both developer and user audiences in order to help them deploy, maintain, and use their own instance of a JupyterHub. The success of an open source software project to (i) be adopted by users, and (ii) receive meaningful contributions relies heavily on the quality, navigability and accessibility of documentation so that users and developers have all the information they need to achieve what they want to do.&lt;/p&gt;
&lt;p&gt;A framework for organising technical documentation has arisen called &lt;a href="https://diataxis.fr"&gt;diátaxis&lt;/a&gt;. It takes a systematic approach to understanding user requirements of documentation throughout the lifecycle of interaction with a product and posits that different user needs require different approaches in creation of the documentation, as well as a layout to navigate these different “modes” of documentation.&lt;/p&gt;
&lt;p&gt;This project will focus on a refactoring of the documentation for the &lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub package&lt;/a&gt;. We will begin by performing a review of the present documentation, categorise these into the diataxis framework, and then restructure the documentation files in the repository. Once we have transformed the documentation into this framework, it will be much easier to identify missing and unclear documentation (those that were difficult to categorise). We can then begin to curate resources that can fill the gaps and improve documentation that is not specific enough.&lt;/p&gt;
&lt;h3 id="allan-says"&gt;Allan says:&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;I am Allan Wasega, from Nairobi, Kenya. Broadly, I like to describe myself as a researcher and a writer. Researcher because looking into things to find patterns or hidden information has always been of interest to me. Writer because I figured early on that words allow me to express myself and to communicate to a larger audience than that inside my head :). As a Computer Science student, I looked for ways to bring these two skills together within the realm of computing and that is how I found myself in the technical writing space.&lt;br&gt;
As an undergraduate student, I used Jupyter Notebooks extensively for most of my programming assignments and projects. As a result, when making my Outreachy application, the JupyterHub project immediately piqued my interest because I did not know that there was a way to serve Jupyter Notebooks to multiple users, such as within a classroom setting. Presently, I serve as a mentor at KamiLimu, which is a mentorship program for students pursuing technology-aligned courses in tertiary institutions in Kenya. One of the objectives of KamiLimu is to introduce students to tech specializations such as Data Science and Machine Learning, which, from experience, uses Jupyter Notebooks! Therefore, I am excited to work on JupyterHub because I will be learning about and helping to improve a product I hold dear and which I can use to advance the skills of the next generation of computer technologists in Kenya.&lt;br&gt;
First, I hope to gain a deep understanding of how JupyterHub works so as to spread the word about it and its functionalities. Second, I hope to apply and advance my technical writing skills. I encountered the Diataxis Framework earlier this year while working as a technical writer at Tingle Software, a Nairobi-based software company. I have also been following Daniele Procida (the author of the framework) on Twitter for a while now and his work has been quite inspiring. Through this project, I will apply the Diataxis Framework to restructure JupyterHub’s documentation and, in doing so, further my understanding of the framework. Finally, I am passionate about mentorship, especially in the tech field. Therefore, besides expanding my professional and personal networks, this project will enable me to learn how to conduct mentorship within a global setting, and I can, in turn, apply this knowledge within my local community.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="sheila-kahwai-create-a-reusable-jupyterhub-pytest-plugin"&gt;Sheila Kahwai — Create a reusable JupyterHub pytest plugin&lt;/h2&gt;
&lt;p&gt;JupyterHub is a modular and extensible project, with parts, like the proxy, authenticator and spawner, that can be easily changed and extended. Testing the functionality of these components against JupyterHub is important and it requires various hub setups that can sometimes become complicated.&lt;/p&gt;
&lt;p&gt;Currently, each of these hub components and the hub itself define their own testing infrastructure, building everything from the ground up using the pytest framework. But some of this complex work is either repetitive across JupyterHub sub-projects, or under-specified for some of them.&lt;/p&gt;
&lt;p&gt;This project will abstract out these common parts into a separate testing framework. This framework should be a pip-installable pytest plugin that would provide various hub functionalities through pytest fixtures. These fixtures can then be re-used by JupyterHub and its components to bootstrap their own testing suite.&lt;/p&gt;
&lt;p&gt;Integrating this plugin will drive some important refactoring work of the current testing architecture of JupyterHub and have a great impact in improving the overall test coverage, maintainability and continuity of the JupyterHub project.&lt;/p&gt;
&lt;h3 id="sheila-says"&gt;Sheila says:&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;My name is Sheila Kahwai. I am a self-taught python developer from Nairobi, Kenya, working to specialize in back-end engineering.&lt;br&gt;
I am excited to work on JupyterHub because it has made many products I have used throughout my learning journey. It is a privilege to work with a diverse community that has created products that are very useful to equally diverse users like myself.&lt;br&gt;
While creating a reusable JupyterHub pytest plugin, I hope to gain more insight into creating plugins for massive codebases to improve maintainability and scalability with clean and reusable code. Through this project, I look forward to improving the overall testing infrastructure of the various JupyterHub components.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Welcome to the interns! We’re so excited to start working with you!&lt;/p&gt;
</content><category term="accessibility"/><category term="documentation"/><category term="JupyterHub"/><category term="Outreachy"/></entry><entry><title>Securely pushing to GitHub from a JupyterHub with gh-scoped-creds</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2022/securely-pushing-to-github-from-a-jupyterhub/" rel="alternate"/><published>2022-04-21T16:54:00+00:00</published><updated>2022-04-21T16:57:00+00:00</updated><author><name>yuvipanda</name></author><id>tag:jasongrout.github.io,2022-04-21:/medium-archive/pelican/posts/2022/securely-pushing-to-github-from-a-jupyterhub/</id><summary type="html">&lt;p&gt;Many JupyterHub users want to push and pull their content from GitHub in order to collaborate and share their work. However, working on a…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/securely-pushing-to-github-from-a-jupyterhub/images/001-1_9E0cif7g07xWAOfsFFmijw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Many JupyterHub users want to push and pull their content from GitHub in order to collaborate and share their work. However, working on a JupyterHub means working on &lt;em&gt;shared infrastructure&lt;/em&gt;, not your own laptop, and this poses some extra security risks that have made two-way sync with GitHub more difficult. This post describes &lt;code&gt;gh-scoped-creds&lt;/code&gt;, a new tool to make it quick and easy to authorize a JupyterHub session with push access to GitHub in a secure and simple manner.&lt;/p&gt;
&lt;p&gt;GitHub user credentials are &lt;a href="https://github.blog/2022-04-15-security-alert-stolen-oauth-user-tokens/"&gt;high value targets&lt;/a&gt; for cybercriminals in today’s security environment, and any system that stores these credentials long term paints an unwanted target on itself. Current solutions — putting an ssh key on the JupyterHub, using a &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token"&gt;personal access token&lt;/a&gt; or deploy keys — involve storing long term valid GitHub credentials in the filesystem. As users can do this by themselves without admin intervention, admins often are not aware these (often unencrypted) credentials are on their filesystems. If an attacker compromises an ssh key or a personal access token, they have unlimited access to all GitHub repos the compromised user had access to, including repos in high-impact GitHub organizations. In the recent credential theft incident, Travis-CI and Heroku were ‘lucky’ in that the &lt;a href="https://github.blog/2022-04-15-security-alert-stolen-oauth-user-tokens/"&gt;attackers accessed npm infrastructure&lt;/a&gt; — and since npm is owned by GitHub, GitHub was able to detect that Travis CI and Heroku had compromised credentials. You and the users of repositories you have rights to might not be so lucky. It’s 2022, and &lt;a href="https://github.com/cncf/tag-security/blob/main/supply-chain-security/compromises/README.md"&gt;supply chain attacks are everywhere&lt;/a&gt; — you aren’t special, you’re just one link in a long chain attackers use to get to someone else.&lt;/p&gt;
&lt;p&gt;There is a clear need for a simple solution that lets users push to GitHub from JupyterHub in a secure manner without admins having to worry about securing high-value GitHub credentials long term. It is not acceptable to “Just Say No” to users wanting this functionality either — if you try to ‘sacrifice’ usability for security, you end up getting neither.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/yuvipanda/gh-scoped-creds/"&gt;&lt;code&gt;gh-scoped-creds&lt;/code&gt;&lt;/a&gt; attempts to solve this problem by allowing users to grant &lt;em&gt;time-limited&lt;/em&gt; push access to &lt;em&gt;specific repositories&lt;/em&gt; to &lt;em&gt;specific JupyterHub installations&lt;/em&gt; in a user friendly way.&lt;/p&gt;
&lt;p&gt;Here’s a quick GIF running through the user workflow.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/securely-pushing-to-github-from-a-jupyterhub/images/002-1_B3qjACXLBG9pBOlzY8WNxA.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Push access is scoped both by time (credentials expire after 8 hours) as well as repository (access is granted per-repository, per-hub). While you need to refresh credentials every 8 hours, the list of repositories is remembered until you explicitly revoke access. You can always grant access to your own personal repositories, but repositories belonging to organisations might require admins to approve push access to them.&lt;/p&gt;
&lt;p&gt;You can also run the command from the terminal as &lt;code&gt;gh-scoped-creds&lt;/code&gt; instead of using the IPython magic &lt;code&gt;%ghscopedcreds&lt;/code&gt; as shown in the demo. This way, you can also use this from a HPC system, not just a JupyterHub!&lt;/p&gt;
&lt;p&gt;Setting this up for your JupyterHub requires a tiny bit of work from the admin — see &lt;a href="https://github.com/yuvipanda/gh-scoped-creds/"&gt;the project README&lt;/a&gt; for more details. Shouldn’t take long, and it’s a one-time task. Once that’s set up, your users can securely push to GitHub from the comfort of their JupyterHubs!&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href="https://twitter.com/fperez_org"&gt;Fernando Perez&lt;/a&gt; for using his &lt;a href="https://classes.berkeley.edu/content/2021-spring-stat-159-001-lec-001"&gt;stat159 class&lt;/a&gt; at &lt;a href="https://www.berkeley.edu/"&gt;UC Berkeley&lt;/a&gt; to test this project out.&lt;/p&gt;
</content><category term="GitHub"/><category term="JupyterHub"/></entry><entry><title>JupyterHub 2.0</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterhub-2-0/" rel="alternate"/><published>2021-12-01T14:15:00+00:00</published><updated>2021-12-01T14:15:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2021-12-01:/medium-archive/pelican/posts/2021/jupyterhub-2-0/</id><summary type="html">&lt;p&gt;Hubs and roles and scopes, oh my!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Hubs and roles and scopes, oh my!&lt;/p&gt;
&lt;p&gt;We’ve just published JupyterHub 2.0, our biggest release in a while.&lt;/p&gt;
&lt;h2 id="roles-and-scopes-rbac"&gt;Roles and scopes (RBAC)&lt;/h2&gt;
&lt;p&gt;Thanks to the work of Omar Richardson and Ivana Huskova and more contributors along the way, JupyterHub now has fine-grained control of permissions, via &lt;strong&gt;scopes&lt;/strong&gt; (a &lt;strong&gt;role&lt;/strong&gt; is a named collection of scopes).&lt;/p&gt;
&lt;p&gt;This makes JupyterHub more secure because you no longer need to give every user or service full admin permissions if they need to take one elevated action (such as a cull-idle service). Now, you can give users and services exactly the permissions they actually need. &lt;code&gt;admin&lt;/code&gt; still exists, but nothing should need to be an admin anymore. Fine-grained scopes also allow new configurations that weren’t previously available, such as granting instructors permission to access their students’ servers, which is useful with JupyterLab’s new real-time collaboration features, without needing to grant them full admin permissions, or even access to servers of users not in their specific class.&lt;/p&gt;
&lt;p&gt;The significant change in how permissions work means that upgrading to 2.0 requires some downtime. You will need to shut down all services and single-user servers across the upgrade, and make sure you upgrade JupyterHub to 2.0 in both the user environment and the Hub environment at the same time.&lt;/p&gt;
&lt;p&gt;All tokens issued prior to the 2.0 upgrade will be revoked.&lt;/p&gt;
&lt;p&gt;As always, &lt;a href="https://jupyterhub.readthedocs.io/en/2.0.0/admin/upgrading.html"&gt;&lt;strong&gt;back up your database&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;before upgrading&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;Read more about roles and scopes &lt;a href="https://jupyterhub.readthedocs.io/en/2.0.0/rbac/"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="jupyterlab-by-default"&gt;JupyterLab by default&lt;/h2&gt;
&lt;p&gt;2.0 also marks JupyterHub’s transition to using JupyterLab as the default UI. You can stick with the classic notebook server with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c.Spawner.environment = {
    &amp;quot;JUPYTERHUB_SINGLEUSER_APP&amp;quot;: &amp;quot;notebook.notebookapp.NotebookApp&amp;quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or try &lt;strong&gt;RetroLab&lt;/strong&gt;, a classic-style UI built on the newer Jupyter Server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c.Spawner.default_url = &amp;quot;/retro/&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(assuming you’ve installed it in the user environment, with e.g. &lt;code&gt;pip install retrolab&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="and-more"&gt;And more&lt;/h2&gt;
&lt;p&gt;There are various other changes and improvements in 2.0, including a new admin UI, API pagination, and no longer relying on any command-line arguments for single-user servers. Check out the &lt;a href="https://jupyterhub.readthedocs.io/en/2.0.0/changelog.html"&gt;changelog&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;We encourage you to take 2.0 for a spin, try out the roles and scopes, and &lt;a href="https://discourse.jupyter.org/c/jupyterhub/10"&gt;let us know&lt;/a&gt; how it goes.&lt;/p&gt;
&lt;p&gt;Thanks to all the contributors who helped us with 2.0, especially prerelease testers!&lt;/p&gt;
</content><category term="JupyterHub"/></entry><entry><title>Setting up a “Production Ready” TLJH deployment</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/setting-up-a-production-ready-tljh-deployment/" rel="alternate"/><published>2021-06-04T17:05:00+00:00</published><updated>2021-06-04T17:05:00+00:00</updated><author><name>yuvipanda</name></author><id>tag:jasongrout.github.io,2021-06-04:/medium-archive/pelican/posts/2021/setting-up-a-production-ready-tljh-deployment/</id><summary type="html">&lt;p&gt;The Littlest JupyterHub is an extremely capable hub distribution that I’d recommend for situations where you expect, on average, under 100…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="http://tljh.jupyter.org/"&gt;The Littlest JupyterHub&lt;/a&gt; is an extremely capable hub distribution that I’d recommend for situations where you expect, on average, under 100 active users.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The Littlest JupyterHub is a distribution of JupyterHub for single VM instances, best-used with 1–100 users." src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/setting-up-a-production-ready-tljh-deployment/images/001-1_4zZBoheuyfPsemCzc_O7jg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Littlest JupyterHub is a distribution of JupyterHub for single VM instances, best-used with 1–100 users.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="why-not-kubernetes"&gt;Why not Kubernetes?&lt;/h2&gt;
&lt;p&gt;The primary reason to use &lt;a href="https://z2jh.jupyter.org"&gt;Zero to JupyterHub on k8s&lt;/a&gt; over TLJH in cases with a smaller number of users is to reduce costs — Kubernetes can spin down nodes when not in use. However, you’ll always have at least one node running (for the hub / proxy pods) and the extra complexity that comes with it — particularly around needing to build your own docker images — may not be worth it. TLJH works perfectly well for these cases!&lt;/p&gt;
&lt;h2 id="what-is-production"&gt;What is ‘production’?&lt;/h2&gt;
&lt;p&gt;A JupyterHub that you can run securely without lots of intervention from the person who created it is what I’ll call a &lt;em&gt;production-ready&lt;/em&gt; JupyterHub. It’s a pretty arbitrary standard. In this blog post, I’ll lay out what &lt;strong&gt;I&lt;/strong&gt; want in the TLJH hubs I run before I let users on them.&lt;/p&gt;
&lt;h2 id="authentication"&gt;Authentication&lt;/h2&gt;
&lt;p&gt;Use a &lt;em&gt;real&lt;/em&gt; &lt;a href="https://tljh.jupyter.org/en/latest/howto/index.html#authentication"&gt;Authenticator&lt;/a&gt;, not the default &lt;a href="https://github.com/jupyterhub/firstuseauthenticator"&gt;&lt;code&gt;FirstUseAuthenticator&lt;/code&gt;&lt;/a&gt;. The default authenticator is pretty insecure, and should really not be used in production. If you don’t know what to use, I’ll suggest the &lt;a href="https://tljh.jupyter.org/en/latest/howto/auth/google.html"&gt;Google&lt;/a&gt; or &lt;a href="https://tljh.jupyter.org/en/latest/howto/auth/github.html"&gt;GitHub&lt;/a&gt; authenticators.&lt;/p&gt;
&lt;h2 id="enable-https"&gt;Enable HTTPS&lt;/h2&gt;
&lt;p&gt;Enable &lt;a href="https://tljh.jupyter.org/en/latest/howto/admin/https.html"&gt;HTTPS&lt;/a&gt;. An absolute security requirement now, and TLJH makes it quite easy. You &lt;em&gt;do&lt;/em&gt; need to get a domain for this to work, which can be a source of friction. Totally worth it, though.&lt;/p&gt;
&lt;h2 id="resource-limits"&gt;Resource Limits&lt;/h2&gt;
&lt;p&gt;In many systems, a single user can often write code that accidentally crashes the whole system. By default, TLJH doesn’t have any memory limits enforced per-user, but it is very easy to configure it to enforce &lt;a href="https://tljh.jupyter.org/en/latest/topic/tljh-config.html#user-server-limits"&gt;memory limits&lt;/a&gt;. Tuning these to match your needs will help prevent a single student from accidentally taking down your whole hub. I’d highly recommend &lt;a href="https://tljh.jupyter.org/en/latest/howto/admin/nbresuse.html"&gt;checking&lt;/a&gt; how much memory your typical notebook uses, and making sure you have user limits set to above that.&lt;/p&gt;
&lt;h2 id="sizing-your-vm-correctly"&gt;Sizing your VM correctly&lt;/h2&gt;
&lt;p&gt;If you choose a VM that’s too big, you’ll end up spending a lot of cash for unused resources. If it’s too small, your users will not have the resources they need to do their work. TLJH provides &lt;a href="https://tljh.jupyter.org/en/latest/howto/admin/resource-estimation.html"&gt;some helpful docs&lt;/a&gt; estimating your VM size, and you can always &lt;a href="https://tljh.jupyter.org/en/latest/howto/admin/resize.html"&gt;resize&lt;/a&gt; your VM afterwards if you get it wrong.&lt;/p&gt;
&lt;h2 id="disk-backups"&gt;Disk backups&lt;/h2&gt;
&lt;p&gt;TLJH contains everything on the VM’s disk — your user environment, users’ home directories, current hub configuration, etc. It is very important you back this up, to recover in case of disasters. Automated disk snapshots from your cloud provider are an easy way to do this. Most major cloud providers offer a way to do this — &lt;a href="https://cloud.google.com/compute/docs/disks/create-snapshots"&gt;Google Cloud&lt;/a&gt;, &lt;a href="https://www.digitalocean.com/docs/images/snapshots/"&gt;Digital Ocean&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSSnapshots.html"&gt;AWS&lt;/a&gt;, etc. Some let you automate it as well — Google &amp;amp; AWS certainly do, I’m not sure about other cloud providers. This isn’t the &lt;em&gt;best&lt;/em&gt; way to do backup — there’s approximately 1 billion ways to do so. However, this is an absolute minimum, and it might just be enough.&lt;/p&gt;
&lt;p&gt;If you want to be more fancy, I’d suggest using a separate disk / volume for your user home directories, possibly on &lt;a href="https://wiki.ubuntu.com/ZFS"&gt;ZFS&lt;/a&gt;, and snapshot much more aggressively. Talk to your nearest google search bar for your options.&lt;/p&gt;
&lt;h2 id="pin-your-public-ip"&gt;Pin your public IP&lt;/h2&gt;
&lt;p&gt;Some cloud providers change your VM’s public IP address if you start / stop them. This can be pretty bad — you’ll have to change your domain’s DNS entry, and re-acquire HTTPS. A hassle! You can tell your cloud provider to hang on to your IP even if your VM goes down / changes. And you should! DigitalOcean doesn’t require this, but &lt;a href="https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address"&gt;Google Cloud does&lt;/a&gt;. I think AWS does too, but I’m not sure how you can reserve the public IP for it — since it’s usually a domain name itself.&lt;/p&gt;
&lt;h2 id="base-environment-setup-snapshot"&gt;Base environment setup + snapshot&lt;/h2&gt;
&lt;p&gt;TLJH has a shared &lt;a href="https://conda.io"&gt;conda&lt;/a&gt; environment that is used by &lt;em&gt;all&lt;/em&gt; users. Everyone can read from it, but only users who are &lt;code&gt;admin&lt;/code&gt; can write to it (via &lt;code&gt;sudo&lt;/code&gt;). This is one of TLJH’s core design trade-offs - admins can install packages the way they are used to, without requiring a separate image-build step. But it also means the admin can mess it up - conda environments can be sometimes fickle! So it’s not a bad idea to spend some time in the beginning setting everything up - python packages, JupyterLab extensions, etc. Then make a disk snapshot, so you can revert to it if things go bad. This is where having a separate disk for your user home directories comes in handy, so you can reset your hub environment without losing your user home directories.&lt;/p&gt;
&lt;h2 id="ssh-admin-access"&gt;SSH admin access&lt;/h2&gt;
&lt;p&gt;The TLJH documentation strives hard to make sure SSH isn’t &lt;em&gt;required&lt;/em&gt; for setup and most common usage. However, if your TLJH breaks in certain ways, you can no longer access the machine — since all access is via TLJH! For this, I recommend making sure someone who is admin has SSH access to the VM. Most cloud providers offer a way to set the root ssh key on creation. If not, you can follow the many guides on the internet to making it happen.&lt;/p&gt;
&lt;p&gt;You can also just put your ssh keys in &lt;code&gt;$HOME/.ssh/authorized_keys&lt;/code&gt;, and ssh in as &lt;code&gt;jupyter-&amp;lt;username&amp;gt;@&amp;lt;hub-ip&amp;gt;&lt;/code&gt;. This works for any / all users!&lt;/p&gt;
&lt;h2 id="others"&gt;Others?&lt;/h2&gt;
&lt;p&gt;I’m sure this isn’t the end — probably need something about firewalls, monitoring and automated system package upgrades. But hey, great start!&lt;/p&gt;
</content><category term="JupyterHub"/><category term="TLJH"/></entry><entry><title>Plasma: A learning platform powered by Jupyter</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/" rel="alternate"/><published>2020-05-11T07:55:00+00:00</published><updated>2020-05-11T07:55:00+00:00</updated><author><name>Jeremy Tuloup</name></author><id>tag:jasongrout.github.io,2020-05-11:/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/</id><summary type="html">&lt;p&gt;Jupyter has been a great choice for education for many years. The Jupyter Notebook has become one of the most popular tools to conduct…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Jupyter has been a &lt;a href="/posts/2019/teaching-and-learning-with-jupyter/"&gt;great choice&lt;/a&gt; for &lt;a href="https://jupyter4edu.github.io/jupyter-edu-book/"&gt;education&lt;/a&gt; for many years. The Jupyter Notebook has become one of the most popular tools to conduct workshops, tutorials, and teach online classes.&lt;/p&gt;
&lt;p&gt;Recently we have seen the emergence and adoption of JupyterHub distributions to facilitate the deployment of Jupyter-based platforms, both on private servers and in the cloud.&lt;/p&gt;
&lt;p&gt;We would like to share with you an open-source learning platform called &lt;a href="https://twitter.com/PlasmaBio"&gt;Plasma,&lt;/a&gt; built with Jupyter at its core.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/001-0_wWdglu7KBVC08Lcc.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="jupyterhub-distributions"&gt;JupyterHub Distributions&lt;/h2&gt;
&lt;p&gt;JupyterHub is a highly customizable and modular framework. To simplify its adoption, &lt;a href="https://jupyterhub.readthedocs.io/en/stable/#jupyterhub"&gt;JupyterHub distributions&lt;/a&gt; target specific deployment scenarios with opinionated defaults. They make it easier to deploy JupyterHub on a single server and in the cloud.&lt;/p&gt;
&lt;p&gt;There are currently two popular JupyterHub distributions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://tljh.jupyter.org"&gt;The Littlest JupyterHub (TLJH)&lt;/a&gt;: for running JupyterHub on a single machine, targeting between 1 and 100 users&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zero-to-jupyterhub.readthedocs.io"&gt;Zero To JupyterHub on Kubernetes (ZTJH)&lt;/a&gt;: for running JupyterHub on top of &lt;a href="https://k8s.io/"&gt;Kubernetes,&lt;/a&gt; to scale to a large number of users and machines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although not an official distribution, &lt;a href="https://github.com/jupyterhub/jupyterhub-deploy-docker"&gt;jupyterhub-deploy-docker&lt;/a&gt; is also a good resource for a full Docker-based setup. It runs JupyterHub itself in a Docker container and orchestrates the stack with &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-plasma-stack"&gt;The Plasma Stack&lt;/h2&gt;
&lt;p&gt;Plasma stands for &lt;em&gt;PLateforme d’e-Learning pour l’Analyse de données Scientifiques MAssives&lt;/em&gt;, which can be translated to “An e-learning platform for massive scientific data analysis”.&lt;/p&gt;
&lt;p&gt;The platform is typically meant to be deployed on high-end machines with multiple cores and GB of RAM.&lt;/p&gt;
&lt;p&gt;Some of the requirements for the Plasma project fall somewhere between the TLJH and ZTJH distributions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The deployment should be on a single server, and reproducible on other machines too (running on Ubuntu 18.04+)&lt;/li&gt;
&lt;li&gt;It should support multiple user environments with different sets of dependencies&lt;/li&gt;
&lt;li&gt;Users should authenticate as system users and their data should be persisted in their home directories on the host machine&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although TLJH doesn’t officially support container technology, its &lt;a href="http://tljh.jupyter.org/en/latest/contributing/plugins.html"&gt;plugin system&lt;/a&gt; opens the door to many other use cases.&lt;/p&gt;
&lt;p&gt;Because of this and to foster the TLJH plugin ecosystem, we decided to develop the Plasma stack as a plugin for TLJH and consolidate the deployment story with Ansible playbooks.&lt;/p&gt;
&lt;p&gt;Plasma is an opinionated JupyterHub stack powered by The Littlest JupyterHub, with the following defaults:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jupyterhub.readthedocs.io/en/stable/api/auth.html#pamauthenticator"&gt;PAMAuthenticator&lt;/a&gt; to authenticate JupyterHub as users existing on the host machine&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyterhub/dockerspawner#systemuserspawner"&gt;SystemUserSpawner&lt;/a&gt; to start single-user servers in Docker containers, using the system user home directories for data persistence&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To enable extra functionalities, the Plasma stack relies on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://github.com/plasmabio/tljh-repo2docker"&gt;tljh-repo2docker&lt;/a&gt; plugin to create multiple user environments&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/plasmabio/plasma/tree/master/tljh-plasma"&gt;tljh-plasma&lt;/a&gt; plugin to define the JupyterHub config&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/plasmabio/plasma/tree/master/ansible"&gt;Ansible playbooks&lt;/a&gt; to automate the deployment and configuration of the server and add additional tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Plasma stack also contains tools to monitor the system, create and configure users, and add hub admins. It can be visualized with the diagram below:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The Plasma Overview Diagram" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/002-1_r-RSTILR7yfT7By30eaeTg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Plasma Overview Diagram&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The stack is defined in the following repository: &lt;a href="https://github.com/plasmabio/plasmabio"&gt;https://github.com/plasmabio/plasma&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is also extensive documentation with detailed explanations on how to deploy the stack on a new server: &lt;a href="https://docs.plasmabio.org"&gt;https://docs.plasmabio.org&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="a-repo2docker-plugin-for-the-littlest-jupyterhub"&gt;A repo2docker plugin for The Littlest JupyterHub&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/plasmabio/tljh-repo2docker"&gt;tljh-repo2docker&lt;/a&gt; plugin lets JupyterHub admins create new user environments using &lt;a href="https://repo2docker.readthedocs.io"&gt;repo2docker&lt;/a&gt;. This plugin starts a &lt;a href="https://jupyterhub.readthedocs.io/en/stable/reference/services.html"&gt;JupyterHub service&lt;/a&gt; to manage user environments from the JupyterHub UI.&lt;/p&gt;
&lt;p&gt;For those already using &lt;a href="https://mybinder.org"&gt;Binder&lt;/a&gt;, the idea will sound very familiar. Under the hood, the tljh-repo2docker plugin also uses &lt;a href="https://repo2docker.readthedocs.io/en/latest/"&gt;repo2docker&lt;/a&gt; to build the Docker images. It follows the same patterns and naming conventions as Binder, which makes it easier and more natural to test the environments on Binder before adding them to JupyterHub.&lt;/p&gt;
&lt;p&gt;New environments can be added by clicking on the &lt;em&gt;Add New&lt;/em&gt; button and providing a URL to the repository. Optional names, memory, and CPU limits can also be set for the environment:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Adding a new environment" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/003-1_KMOJ3287DPh6aG8Kg4EkEg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Adding a new environment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;em&gt;Environments&lt;/em&gt; page shows the list of built environments, as well as the ones currently being built:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Building a new environment" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/004-1_VpXTXOEt4Owlf5O713ktLw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Building a new environment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The status of the environment changes once the underlying Docker image has been built:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The list of available user environments" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/005-1_uHAYqgSCAfswFXvRHTO43w.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The list of available user environments&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Once ready, the environments can be selected from the JupyterHub spawn page:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Selecting an environment" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/006-0_nOCNtzHaQ_dj3-Jg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Selecting an environment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Because it is separate from the Plasma stack, this plugin can also be used for other temporary TLJH deployments on a virtual machine. For example, the organizer of a workshop can prepare a list of environments before the event, just like they would with Binder.&lt;/p&gt;
&lt;h2 id="automating-deployments-with-ansible"&gt;Automating deployments with Ansible&lt;/h2&gt;
&lt;p&gt;To minimize the number of manual steps involved in the setup of the stack, Plasma also provides a list of &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks.html"&gt;Ansible playbooks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.ansible.com/ansible/latest/index.html"&gt;Ansible&lt;/a&gt; is an open-source tool to automate the provisioning of servers, configuration management, and application deployment.&lt;/p&gt;
&lt;p&gt;Playbooks define a list of tasks that should be executed and declare the desired state of the server.&lt;/p&gt;
&lt;p&gt;The list of playbooks and instructions on how to use them are provided in the &lt;a href="https://docs.plasmabio.org/en/latest/install/index.html"&gt;Installation section of the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Overall, the playbooks make it easier to perform upgrades, to automate the deployment process, and to replicate the setup at other institutions and universities.&lt;/p&gt;
&lt;p&gt;Here is an example of what an upgrade looks like:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Upgrading the stack with an Ansible playbook" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/007-1_0n1kaev7c81I45SZ0HoLxQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Upgrading the stack with an Ansible playbook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This playbook defines the tasks to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;download the TLJH installer&lt;/li&gt;
&lt;li&gt;execute the TLJH installer to perform the upgrade&lt;/li&gt;
&lt;li&gt;update the TLJH plugins&lt;/li&gt;
&lt;li&gt;set the idle culler timeout&lt;/li&gt;
&lt;li&gt;set the default memory and CPU limits&lt;/li&gt;
&lt;li&gt;reload JupyterHub&lt;/li&gt;
&lt;li&gt;pulls the latest &lt;code&gt;jupyter/repo2docker&lt;/code&gt; Docker image&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="further-reading"&gt;Further reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The Plasma documentation: &lt;a href="https://docs.plasmabio.org/"&gt;https://docs.plasmabio.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;To use the tljh-repo2docker as a standalone plugin for TLJH: &lt;a href="https://github.com/plasmabio/tljh-repo2docker"&gt;https://github.com/plasmabio/tljh-repo2docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Also part of the Plasma project, the new &lt;a href="https://github.com/QuantStack/ipycytoscape"&gt;ipycytoscape&lt;/a&gt; package for interactive graph visualization in Jupyter: &lt;a href="/posts/2020/interactive-graph-visualization-in-jupyter-with/"&gt;https://blog.jupyter.org/interactive-graph-visualization-in-jupyter-with-ipycytoscape-a8828a54ab63&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;The development of the Plasma stack at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt; was funded as part of the &lt;a href="https://twitter.com/PlasmaBio"&gt;Plasma&lt;/a&gt; project, led by &lt;a href="https://twitter.com/CVandiedonck"&gt;Claire Vandiedonck&lt;/a&gt;, &lt;a href="https://twitter.com/pierrepo"&gt;Pierre Poulain&lt;/a&gt;, and &lt;a href="https://twitter.com/SCaburet"&gt;Sandrine Caburet&lt;/a&gt;, associate professors at Université de Paris.&lt;/p&gt;
&lt;p&gt;Sponsors to the Plasma initiative include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/iledefrance"&gt;Région Île-de-France&lt;/a&gt;, via the “Trophées franciliens de l’innovation numérique dans le supérieur” (&lt;a href="https://www.iledefrance.fr/trophees-franciliens-de-linnovation-numerique-dans-le-superieur-les-laureats-2018"&gt;EdTech 2018&lt;/a&gt;) grant program,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/Univ_Paris"&gt;Université de Paris&lt;/a&gt;, via the &lt;a href="https://u-paris.fr/en/the-initiative-of-excellence-idex-label/"&gt;Initiative of Excellence (IdEx) Label&lt;/a&gt; and its “inovating teaching” grant program,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/EURGENEPARIS"&gt;EUR G.E.N.E.&lt;/a&gt;, the graduate school on Genetics and Epigenetics,&lt;/li&gt;
&lt;li&gt;the university training “Création, analyse et valorisation de données biologiques omiques” (&lt;a href="https://omics-school.net/"&gt;DU Omiques&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/plasma-a-learning-platform-powered-by-jupyter/images/008-1_-mk2TL1LTFBzkfW4JX30Pw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="education"/><category term="JupyterHub"/></entry><entry><title>The superheroes and the magic wand</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2020/the-superheros-and-the-magic-wand/" rel="alternate"/><published>2020-03-19T18:23:00+00:00</published><updated>2020-03-19T18:26:00+00:00</updated><author><name>Georgiana Dolocan</name></author><id>tag:jasongrout.github.io,2020-03-19:/medium-archive/pelican/posts/2020/the-superheros-and-the-magic-wand/</id><summary type="html">&lt;p&gt;In a place far, far away, on a planet called Jupyter, magic happens every day. This land is special because it’s full of magical tools…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/the-superheros-and-the-magic-wand/images/001-1_v9JBClFvN4V-YZOQgwNxig.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the first of a series of posts describing tools in the JupyterHub ecosystem, written by our wonderful &lt;em&gt;Contributor in Residence&lt;/em&gt;, Georgiana. For our first post, we’ll share some lore of JupyterHub, and tell you a story of how it all began…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a place far, far away, on a planet called Jupyter, magic happens every day. This land is special because it’s full of magical tools with all kind of powers, devoted to one common purpose: to help people. Everyone sympathizing with this goal either advocates, uses, or cares for these tools. So, in the blink of an eye, these people with sometimes nothing else in common than the same drive to help others, gathered together and formed a community.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This special group is known in the galaxy as “&lt;em&gt;&lt;strong&gt;&lt;/em&gt;The Jovyans&lt;/strong&gt;&lt;/em&gt;*” and new recruits join every day.* 🚀&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;
&lt;img alt="This image was created by Scriberia for The Turing Way community and is used under a CC-BY licence. Zenodo record." src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/the-superheros-and-the-magic-wand/images/002-1_lz2yH1jyAlILFF9hZNtFPg.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;This image was created by &lt;a href="http://www.scriberia.co.uk/"&gt;Scriberia&lt;/a&gt; for &lt;a href="https://github.com/alan-turing-institute/the-turing-way"&gt;&lt;strong&gt;The Turing Way&lt;/strong&gt;&lt;/a&gt; community and is used under a CC-BY licence. &lt;a href="https://zenodo.org/record/3695300"&gt;Zenodo record&lt;/a&gt;.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;But somebody needed to take care of these magical tools. So, the first Jovyans decided that from then on, they will become tool-keepers. Because their greatest responsibility was to teach new Jovyans how to use the magic, shortly after, they created a set of guiding laws. Some call this &lt;em&gt;“the&lt;/em&gt; &lt;em&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/em&gt;”.&lt;/p&gt;
&lt;h2 id="the-magic-wand"&gt;The magic wand&lt;/h2&gt;
&lt;p&gt;One greatly cherished tool on planet Jupyter is the magical wand. This wand’s very special power is to help people work together as a team and find solutions to important problems.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The wand is called “&lt;em&gt;&lt;strong&gt;&lt;/em&gt;JupyterHub&lt;/strong&gt;&lt;/em&gt;”.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/the-superheros-and-the-magic-wand/images/003-1_3nDwvUQiUfa54OsDPqKNag.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;But the wand is of vast complexity and because of this, so are the guiding laws that control it.&lt;/p&gt;
&lt;p&gt;As a consequence, people trying to benefit from the magic of JupyterHub, spent a lot of time reading and understanding the instructions.&lt;/p&gt;
&lt;h2 id="the-superheros"&gt;The superheros&lt;/h2&gt;
&lt;p&gt;The tool-keepers noticed that most of the people coming to planet Jupyter to become Jovyans were from a big city in the cloud 🌤 called &lt;em&gt;Kubernetes&lt;/em&gt;. So they gathered together and debated what is the best way to help the people of Kubernetes.&lt;/p&gt;
&lt;p&gt;After 3 days and 3 nights of intense discussions (also lots of pizza breaks of course) they decided that one of them needed to get special training, rent a house in Kubernetes and teach the people there the wonders of the JupyterHub magic wand.&lt;/p&gt;
&lt;p&gt;The Chosen One gained the people’s trust, and it got better and better at anticipating and understanding their needs. So the locals started seeing it as a superhero and they even gave it a name, “&lt;strong&gt;Z2JH&lt;/strong&gt;”. 👓&lt;/p&gt;
&lt;p&gt;The news of these events started to spread far and wide, and people living in little towns outside of Kubernetes felt that they deserved the support of a superhero too. They also had great ideas and little time and needed to use the wand’s magic to do good.&lt;/p&gt;
&lt;p&gt;The tool-keepers were inspired by the magnificent accomplishments of these little towns, taking place even without a superhero around. So the littlest of them all, volunteered to go into superhero training to help the people of the little towns do good, faster. Though it was little, in no time, it grew to become the helper these small groups needed.&lt;/p&gt;
&lt;p&gt;The residents called it “&lt;strong&gt;The Littlest JupyterHub” or “TLJH”&lt;/strong&gt; because of its stature. But everybody knew that its stature didn’t reflect its enormous tenacity, speed and ability to do great things. This ended up inspiring people all over to believe that not all superheros wear capes, nor do they need to know how to fly in the cloud to be cool.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/the-superheros-and-the-magic-wand/images/004-1_egrzbBcwyaxm7-793O7uiQ.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nowadays, people from all over join their forces and help the superheros learn new skills. Thanks to them, TLJH and Z2JH get stronger and better and have more special powers than they had when first created by the tool-keepers. Join them, become a Jovyan! ツ&lt;/p&gt;
&lt;/blockquote&gt;
</content><category term="JupyterHub"/><category term="Kubernetes"/></entry><entry><title>Connect to a JupyterHub from Visual Studio Code</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/" rel="alternate"/><published>2019-12-09T17:23:00+00:00</published><updated>2019-12-09T17:23:00+00:00</updated><author><name>yuvipanda</name></author><id>tag:jasongrout.github.io,2019-12-09:/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/</id><summary type="html">&lt;p&gt;Visual Studio Code has pretty good support for running Jupyter Notebooks. But what if your organization has a JupyterHub running remotely…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://code.visualstudio.com"&gt;Visual Studio Code&lt;/a&gt; has pretty good support&lt;br&gt;
for &lt;a href="https://code.visualstudio.com/docs/python/jupyter-support"&gt;running Jupyter Notebooks&lt;/a&gt;. But what if your organization has a &lt;a href="https://jupyter.org/hub"&gt;JupyterHub&lt;/a&gt; running remotely, with more compute resources &amp;amp; access to large amounts of data? How can you access that from Visual Studio Code running on your local machine?&lt;/p&gt;
&lt;p&gt;It’s pretty easy to do, and this blog post will guide you through it.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/001-1_FGIOFXmphgFGab3WOJwSZw_2x.webp" alt="jupyterhub and vscode logos" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="step-1-get-a-jupyterhub-access-token"&gt;Step 1: Get a JupyterHub access token&lt;/h2&gt;
&lt;p&gt;JupyterHub lets you create tokens for yourself for use by third party applications. These tokens can be used anywhere a &lt;a href="https://jupyter-notebook.readthedocs.io/en/stable/security.html"&gt;Jupyter Notebook access token&lt;/a&gt; is needed. Since this is what Visual Studio Code needs, let’s acquire one.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log-in to your JupyterHub&lt;/li&gt;
&lt;li&gt;Access your &lt;em&gt;Control Panel&lt;/em&gt;. In classic notebook, there is a ‘Control Panel’ button on the top right. In JupyterLab, you can access it under ‘File -&amp;gt; Hub Control Panel’&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt="Top Right ‘Control Panel’ button in classic Jupyter Notebook" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/002-1_ZNZ-jbJu8TbhWPZ596difg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Top Right ‘Control Panel’ button in classic Jupyter Notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="File -&amp;gt; Hub Control Panel in JupyterLab" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/003-1_Jl-Ug0_n0Pr22AW7Le6CWg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;File -&amp;gt; Hub Control Panel in JupyterLab&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Go to the ‘Token’ page by clicking ‘Token’ in the top bar&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt="Token link in the top bar to go to the token page" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/004-1_BSKDpkMhSp_l0oAuMVrMRA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Token link in the top bar to go to the token page&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Type in a description for the new token you want, and click ‘Request new API Token’&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt="Type in a description for what this token will be used for" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/005-1_6JY55qIAgIaYQlviKY1xYw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Type in a description for what this token will be used for&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Copy your token and keep it somewhere safe. You should treat this like a password to your JupyterHub. You can (and should!) revoke it (as I have done) from the same page when you are no longer using it.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt="Copy your token" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/006-1_1iCbErLsY3L8p5YFG-ch0Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Copy your token&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is all the information you need from JupyterHub! Now let’s go to vscode.&lt;/p&gt;
&lt;h2 id="step-2-connect-vs-code-to-your-jupyterhub"&gt;Step 2: Connect VS Code to your JupyterHub&lt;/h2&gt;
&lt;p&gt;Visual Studio Code supports connecting to a &lt;a href="https://code.visualstudio.com/docs/python/jupyter-support#_connect-to-a-remote-jupyter-server"&gt;remote notebook server&lt;/a&gt;, and we can use that to connect to our JupyterHub. You must perform these steps &lt;em&gt;before&lt;/em&gt; opening your notebook.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open the command palette&lt;/strong&gt; in Visual Studio Code (‘Cmd+Shift+P’ on MacOS, ‘Ctrl+Shift+P’ elsewhere)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Select ‘Python: Specify local or remote Jupyter server for connections’&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt="vscode command palette" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/007-1_hm9yZinnwlF3EqeXuossBQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;vscode command palette&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;Construct your &lt;em&gt;notebook server URL&lt;/em&gt;&lt;/strong&gt; with the following template: &lt;code&gt;https://&amp;lt;your-hub-url&amp;gt;/user/&amp;lt;your-hub-user-name&amp;gt;/?token=&amp;lt;your-token&amp;gt;&lt;/code&gt;. Note that your hub user name might sometimes be escaped from whatever you used to actually log in, if it has special characters. You can verify this by looking at the URL you get once you log in to your JupyterHub — it should have the right one after ‘user’.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt="Enter your notebook server URL" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/connect-to-a-jupyterhub-from-visual-studio-code/images/008-1__FILTBgRJ76bq5R7LYFGww.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Enter your notebook server URL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ol start="4"&gt;
&lt;li&gt;&lt;strong&gt;Create or open a new notebook.&lt;/strong&gt; The kernel for this should now live on your JupyterHub! You can verify this by running &lt;code&gt;!hostname&lt;/code&gt;, which should return the hostname of your remote JupyterHub server instead of your local hostname. You can also try importing libraries that are in the remote JupyterHub server, but not your local file system.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tada! That wasn’t so hard, was it?&lt;/p&gt;
&lt;h2 id="limitations"&gt;Limitations&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Your JupyterHub notebook server &lt;em&gt;must&lt;/em&gt; be running already when you try to open your notebook — Visual Studio Code will not automatically start it. If it has stopped, you need to log in to your JupyterHub &amp;amp; start it again. You do not need a new token though.&lt;/li&gt;
&lt;li&gt;Watch out for filesystem access. When you are calling &lt;code&gt;open()&lt;/code&gt;(or a helper function that eventually access a file) to read a file, it is going to be read from your &lt;em&gt;remote JupyterHub server’s home directory&lt;/em&gt;, not your local system’s current directory. So if you add a new file next to your Jupyter Notebook locally, that is &lt;em&gt;not&lt;/em&gt; automatically going to be available to the Jupyter Notebook to read.&lt;/li&gt;
&lt;li&gt;Installing pip or conda packages locally will have no effect, since your Python kernel is running on your JupyterHub. Use the &lt;a href="https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-pip"&gt;%pip&lt;/a&gt; or &lt;a href="https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-conda"&gt;%conda&lt;/a&gt; magics to install packages in the correct environment.&lt;/li&gt;
&lt;/ol&gt;
</content><category term="JupyterHub"/></entry></feed>