Open edX Jupyter notebooks in courses: what the Tutor plugin delivers
A course team that wants hands-on notebook exercises inside Open edX should choose the Tutor Jupyter plugin as the supported route. The plugin installs the official jupyter-xblock in both the LMS and Studio, so authors can place notebook components in course units and learners can open them without leaving the course. Behind the component, the plugin runs a Docker-based JupyterHub that uses a Docker spawner. Each learner gets a container with limited CPU and memory, which keeps one heavy notebook from starving the rest of the server but also caps what a learner can compute. Before the team commits to exercises that need large datasets or long-running calculations, it should test those workloads against the limits the plugin actually sets in its configuration.
What the plugin does and does not settle
The plugin gives a course team a working path from authoring to learner access on a single Tutor-managed deployment. It does not settle how the notebook service should run at production scale. The plugin's README states that Kubernetes compatibility was not battle-tested and points to Zero to JupyterHub with Kubernetes for production use. If the Open edX deployment runs on Kubernetes, or will need to scale notebook sessions across nodes, the team should treat the Docker spawner setup as a starting point and plan a separate JupyterHub design for that environment. Confirm the current README and the Tutor version you run before you finalize the architecture, because plugin behavior and supported configurations can change between releases.
Decisions to make before build starts include the expected number of concurrent learners in a notebook exercise, the image contents each course needs (language kernels, libraries and data files), how long containers may persist, and where notebook outputs and any learner data will be stored. Each of these choices changes resource needs on the host. Ask the implementation partner to show how the plugin's CPU and memory limits are set, where they are changed, and what happens to a learner's session when a container hits its limit.
Verification should happen in a staging environment that matches the production Tutor configuration. Run a rehearsal with a realistic group of learners opening the same exercise at once, check container start times, confirm that Studio authors can add and edit notebook components, and confirm that learners cannot reach other learners' containers. Record the results so the team can compare them with the expectations in its course design.
Cost and effort depend on who is paid for what. The plugin itself is open source, so the main costs are the host capacity that runs the Open edX stack and the notebook containers, the engineering time to configure and test the environment, and any later work to move notebook hosting to Kubernetes. Ongoing costs include upgrades of Tutor, the plugin and the notebook images, plus monitoring of host resources. When you request a quote, ask for the written scope to name the Tutor version, the plugin version, the resource limits to be tested, the staging test plan, and whether production hosting on Kubernetes is in or out of scope.
What you need before enabling Jupyter notebooks in Open edX
Enabling notebooks touches your deployment model, your release choice, your hosting and your test environments. Work through these five items before anyone installs the Tutor Jupyter plugin, because each one changes what the plugin needs from the rest of the stack.
Tutor-based Open edX deployment
The Jupyter plugin is a Tutor plugin, so it installs into a deployment that Tutor builds and manages. The edx-platform maintainers recommend Tutor or a service provider for production, noting that bare-metal installation is complex and receives limited community support. Confirm that your current environment runs on Tutor, that you can rebuild images, and that you know who applies plugin upgrades. If you run a hand-built install, plan a migration to Tutor before adding notebooks.
Targeted Open edX release
Open edX supports only the latest named release, and it cannot support more than one release at a time. Verawood is the current named release, published on 30 July 2026, and Willow and Xylon are listed for December 2026 and June 2027. Choose the release your site will run, then confirm the Tutor Jupyter plugin version against that Tutor and Open edX release before installation. This page does not state a compatibility matrix, so check the plugin repository and your Tutor version yourself.
Host capacity for learner containers
With the Docker spawner, JupyterHub starts a container for each learner, and each container receives limited CPU and memory. Capacity depends on how many learners open notebooks at the same time, the per-container limits you set, and the memory and disk the Docker host keeps for images and sessions. Size the host from concurrent use in a load test on your own course content, and ask the implementer to show the container limits and cleanup policy in writing.
Kubernetes production decision
The plugin runs JupyterHub with the Docker spawner, and its README says Kubernetes compatibility was not battle-tested. For production on Kubernetes, the README points to Zero to JupyterHub with Kubernetes, which is a separate deployment path. Decide the hosting model before the build starts, because the spawner, storage, image management and monitoring differ between the two. If your Open edX already runs on Kubernetes, schedule a design review of the notebook layer before you commit.
Studio and LMS test access
The Jupyter plugin installs jupyter-xblock in both the LMS and Studio, so the first tests cover authoring and learner launch. Prepare a non-production Open edX instance or staging course, with accounts for a course author, a learner and an administrator. Without Studio access, the team cannot place the block in a unit, and without LMS test accounts, it cannot confirm that learner containers start and stop correctly. Keep these accounts out of production courses.
How the Jupyter XBlock and JupyterHub run Open edX notebooks
The notebook feature depends on three pieces working together: the Jupyter XBlock, which course authors add to a unit in Studio; the LMS, which renders that unit for learners; and JupyterHub, which starts a separate container for each learner who opens a notebook. The Tutor Jupyter plugin installs the official jupyter-xblock in both the LMS and Studio and runs a Docker-based JupyterHub with a Docker spawner. The request path therefore begins when an author places the XBlock in a unit in Studio. When a learner later opens that unit in the LMS, the XBlock hands the session to JupyterHub, which launches a container for that learner with limited CPU and memory.
This per-learner container model is the main driver of capacity planning. Each open notebook consumes a share of the host's CPU and memory for as long as the session stays running, so the number of learners who open notebooks at the same time, not the total enrollment, sets the load. A cohort starting a graded lab in the same hour can produce a spike that a course with the same enrollment spread over a term never would. Teams should estimate peak concurrent sessions for their largest course, multiply by the per-container limits, and compare the result with the capacity of the Docker host or hosts. Where the total exceeds the hardware, learners will see slow starts, failed spawns or sessions that stop, and the platform itself may slow down because it shares those machines.
The limits are the control to check first. The per-container CPU and memory values, the idle timeout that frees resources from abandoned sessions, and the cap on total spawned containers determine how the platform behaves under a spike. Confirm how the plugin's Docker spawner limits are set in your environment, including whether a learner who exceeds them is stopped, throttled or left unable to start a session, and whether the values differ between the test and production stacks. Ask the team that operates the deployment to show the running configuration rather than rely on defaults.
Be aware that the plugin's own README says Kubernetes compatibility was not battle-tested and points to Zero to JupyterHub with Kubernetes for production use. If your Open edX deployment runs on Kubernetes, the spawner, storage for user data and scheduling of notebook pods need separate design work and a load test before a graded course depends on them. On a single Docker host, plan a test that opens sessions at the planned peak, records how long spawns take, and confirms that the LMS remains responsive while they run.
Enabling the Tutor Jupyter plugin in the right order
This sequence adds the Tutor Jupyter plugin to a working Open edX deployment one change at a time. Each step ends with a check, so any failure traces back to a single change. The result is a notebook unit that a test learner can open in a sandbox course, with the production hosting review still ahead.
Confirm the base deployment works without notebooks
The platform team runs the existing Tutor deployment in the target environment before any notebook change is made. Confirm that Studio and the LMS load, that a sandbox course can be created, and that a test learner account can enroll and view published content. Record the Tutor version and the configuration in version control so the baseline can be restored. This step is done when a text unit published in Studio is visible to the test learner in the LMS.
Install the Tutor Jupyter plugin
On the Tutor host, install the Tutor Jupyter plugin using the command and version given in the current tutor-jupyter README, since command syntax can change between releases. The plugin installs the official jupyter-xblock into the LMS and Studio and sets up a Docker-based JupyterHub with a Docker spawner. Confirm that the Docker engine is present on the host where learner containers will run, because the spawner creates containers there. This step is done when the plugin appears in the list of installed plugins and the install completes without errors.
Enable the plugin and apply the configuration
Enable the plugin in Tutor, save the configuration, and relaunch the stack so the new settings and images take effect. Review the generated values for the per-learner container CPU and memory limits, and treat the defaults as a starting point until the platform owner has agreed sizing for the expected number of concurrent learners. Keep the configuration change in version control with the baseline. This step is done when the Jupyter services show as running and the LMS and Studio restart cleanly after the change.
Confirm the Jupyter XBlock appears in Studio
Open a unit in Studio in a sandbox course and choose the option to add a new component. Look for the Jupyter notebook component in the list of available block types. If it is missing, check that Studio was restarted after the configuration was applied and that the jupyter-xblock is loaded in the Studio container logs. This step is done when the component can be added to a unit in the sandbox course without an error.
Add a test notebook unit in a sandbox course
Create or use a dedicated sandbox course that contains no learner records or production content. Add a unit, insert the Jupyter notebook component, and set its settings according to the XBlock documentation, including the notebook source the test should use. Publish the unit and preview it in Studio. This step is done when the unit publishes successfully and the notebook component renders in the preview, even if the notebook has not yet started.
Open the unit as a learner and watch containers
Sign in as the test learner, enroll in the sandbox course, and open the notebook unit. On the host, confirm that a container for that learner starts and that it stops after the session ends or after the idle period configured for the hub. Check the container's CPU and memory use against the limits set earlier. This step is done when you have observed one container start and stop for the test learner. Production hosting questions, including the Kubernetes path that the plugin README points to through Zero to JupyterHub, belong to a separate review that must finish before any learner uses the feature.
Which learner, course and container identities map to each notebook
This table maps each identity in an Open edX notebook launch: the LMS learner, the course unit holding the Jupyter XBlock, the JupyterHub user and the per-learner Docker container. Read each row to see where the identity is created and what to check before enrolling real learners. The main takeaway is that the mapping is proven only when you test it end to end in a test course with more than one learner.
| Identity | Where it lives and what it maps to | What to verify in a test course |
|---|---|---|
| LMS learner account | Created when a learner registers or is enrolled in the LMS. This is the identity the platform records for the course. | Enroll two test learners and confirm each sees the notebook unit and no one else's session. |
| Course and unit with the Jupyter XBlock | Authored in Studio and placed in a unit. The course key and unit location determine who can open the block. | Confirm the unit is hidden from non-enrolled users and the course key matches your planned production run. |
| JupyterHub user | Created by the hub on first launch, with the username taken from the LMS identity. Confirm which LMS attribute is used. | Check that each learner gets a separate hub user and see what happens to hub records if an LMS username changes. |
| Docker container per learner | The Docker spawner starts one container per hub user, with CPU and memory limits set in the plugin configuration. | Confirm each test learner gets a distinct container, that the limits apply, and how idle containers are stopped. |
| Studio author role | Authors build and edit the block in Studio. Learners launch the block from the LMS, so authoring access is separate from learner sessions. | Confirm which Studio views show learner notebook content, which show only block settings, and whether authors can open learner containers. |
| Staff and preview accounts | Staff and preview launches may create hub users or containers that do not correspond to a learner record. | Check whether staff launches appear in hub records and exclude them from learner counts and capacity planning. |
| Kubernetes production path | The Tutor Jupyter README says Kubernetes compatibility was not battle-tested and points to Zero to JupyterHub for production. | Repeat every mapping check on the Kubernetes design before any production launch. |
Why the Tutor Jupyter plugin is not a Kubernetes production design
The Tutor Jupyter plugin should not be treated as a Kubernetes production design. Its README states that Kubernetes compatibility was not battle-tested and points teams to Zero to JupyterHub with Kubernetes for production deployments. Any team running Open edX on Kubernetes should read that statement as the project's own boundary on what the plugin supports, and plan the notebook service around it.
The reason is architectural. The plugin installs the official jupyter-xblock in the LMS and Studio and runs JupyterHub with a Docker spawner, which starts one container per learner with limited CPU and memory. That model assumes a Docker host the plugin can drive directly. A Kubernetes spawner works differently: the hub asks the cluster to schedule each user pod, and the cluster owns placement, resource requests and limits, storage claims, networking and cleanup. Those responsibilities do not map cleanly onto the Docker spawner, so a Kubernetes cluster cannot be assumed to run the plugin's notebook layer correctly just because Open edX itself runs there.
The practical consequence is a separate piece of work. Plan a JupyterHub deployment designed for Kubernetes, typically built with Zero to JupyterHub with Kubernetes, and then decide how the Open edX side connects to it. The jupyter-xblock must point at that hub, and the hub's authentication must recognize learners who arrive from the LMS. Confirm with the jupyter-xblock and JupyterHub documentation which authentication and URL settings apply to your versions, rather than copying settings from a Docker-based Tutor configuration.
Before committing to a design, put these questions to your team and to any partner: where user pods will run and in which namespace, how CPU, memory and storage quotas will be enforced per learner, how notebook data is backed up and deleted when a course ends, and who on-call owns a hub failure during an exam window. Ask for the expected load profile in writing, because the answers change node sizing, storage classes and the cost of running notebooks alongside the rest of the platform.
Finally, treat the Kubernetes notebook service as its own component with its own test plan. Verify that a learner can open a notebook from a course unit, that grades or outputs reach the systems you expect, that resource limits stop a runaway notebook without affecting other learners, and that an upgrade of either JupyterHub or the Open edX release does not break the embedding. Keeping this boundary explicit prevents the common mistake of assuming a working Tutor plugin on a test cluster proves production readiness.
Securing Open edX Jupyter notebooks and learner data
Running Jupyter notebooks inside Open edX puts learner containers, learner data and the LMS on shared infrastructure. The cards below cover the settings a team should review before learners use notebooks in production, and each one is a design decision to verify in your own environment. Independent security testing is a separate engagement and is not included in this configuration review.
Trust boundary between containers and host
The Tutor Jupyter plugin runs JupyterHub with a Docker spawner, so each learner gets a container on a Docker host. Decide who can reach that host, whether containers run with privileged settings, and whether the Docker socket or host paths are exposed to notebook containers. Confirm these choices in your own environment, because a container that escapes its boundary can reach other learners' sessions and the platform itself.
Resource limits on learner containers
The plugin gives each learner container a limited share of CPU and memory. Review the actual per-container values, the maximum number of concurrent containers, and what happens when a learner runs a long or runaway computation. Without firm limits, one notebook can starve the host and slow the LMS for every course running on it.
Network reach from containers to internal services
Notebook containers should not reach the LMS database, Studio, MongoDB, Memcached or other internal services unless a specific flow requires it. Map which addresses and ports each container can reach, then restrict that path with host or network firewall rules. Confirm the result by testing from inside a running learner container, not only from the configuration files.
Persistence of learner work and data
Decide which notebook files persist between sessions, where they are stored on disk or in volumes, and how long they remain. Check whether learner work is included in backups, how it is removed when a learner or course is deleted, and whether it contains personal data covered by your privacy obligations. Ask your team to document the answers before launch.
Staff access to learner notebooks
Define which course staff roles can open, view or copy learner notebooks, and whether that access is logged. Keep the rule for instructors aligned with your privacy policy and course terms. Verify the permissions in a test course with a staff account and a learner account, because role settings in the LMS do not automatically limit what the notebook service exposes.
Patching of Jupyter components
Track the versions of JupyterHub, the Jupyter XBlock, the Docker images used for learner environments and the host operating system. Decide who receives security advisories, how quickly updates are applied, and how images are rebuilt when a base image changes. The tutor-jupyter README points production deployments toward Zero to JupyterHub with Kubernetes, so confirm which path your team will support before setting the patch schedule.
Common Jupyter failures in Open edX, their causes and fixes
This table lists the failures teams most often see when running Jupyter notebooks in Open edX, with the likely cause and the first fix or check for each. Use it as a triage guide before opening a support ticket or changing sizing. The main takeaway is that most failures trace back to the Docker spawner setup, the notebook XBlock configuration, or host capacity rather than to the course content itself.
| Failure | Likely cause | Fix or check to run |
|---|---|---|
| XBlock missing from Studio after install | The Jupyter XBlock was installed in a different image or environment than the one Studio runs, or the platform was not rebuilt and restarted after the install. | Confirm the package is present in the Studio container, rebuild the image if needed, restart Studio, then check the advanced component list in the course settings. |
| Notebooks not launching for learners | JupyterHub cannot reach the Docker daemon, the spawner image is missing or misnamed, or the LMS and JupyterHub URLs and tokens do not match. | Check the JupyterHub logs for spawn errors, verify the spawner image exists on the host, and confirm the LTI or XBlock URL settings point to the public JupyterHub address. |
| Containers failing under concurrent load | Per-learner containers exceed the CPU or memory limits set for the spawner, or the host runs out of Docker resources while many sessions start at the same time. | Review container exit codes and out-of-memory events, reduce per-container limits only after testing, and load test a representative class size before the course opens. |
| Learners seeing another learner's session | Session or user identity is not isolated across the hub, a shared volume or user directory is mounted into more than one container, or a proxy caches responses between users. | Treat this as a security incident. Confirm each spawned container maps to a single user, review volume mounts and proxy caching headers, and retest with two test accounts before reopening access. |
| Capacity exhaustion on the host | The Docker host has no limits on the number of notebook containers, disk fills with unused user volumes, or other services share the same machine. | Monitor CPU, memory, disk and container count, set idle-container culling and volume cleanup policies, and plan separate hosts for the notebook workload if usage grows. |
| Production limits on Kubernetes | The Tutor Jupyter plugin's Docker spawner was not designed for Kubernetes, and its README says that compatibility was not battle-tested. | Confirm the target platform with the Tutor Jupyter README before planning, and ask whether a Zero to JupyterHub with Kubernetes deployment fits your hosting model. |
What buyers ask before hiring help with Open edX Jupyter notebooks
How much does hiring help with Open edX Jupyter notebooks cost?
The quote follows from a written scope, and DeployOpen does not publish a price list. The main cost drivers are the number of courses that embed notebooks, the complexity of each notebook (kernels, packages, data files, grading or interactivity needs), the host sizing for JupyterHub and learner containers, and whether Kubernetes is in scope. Costs split into one-time work (planning, installation, integration, testing, documentation and cutover) and recurring work (patching, upgrades, monitoring, capacity changes and support). Ask each bidder to itemize these components separately, state the environment assumptions behind them, and list what they expect your team to provide. Compare quotes on the same written scope so the totals reflect the same work.
How long does an Open edX Jupyter notebook project take?
Duration depends on scope and environment, so no timeline applies until the scope is agreed. It grows with the number of courses, the number of distinct notebook environments and kernels, whether the hosts are new or already exist, and how many approval gates your security and change processes add. A single-environment pilot on Docker is generally shorter than a production build that adds Kubernetes, identity integration and load testing. Ask for a schedule with milestones tied to deliverables, a list of dependencies you own (access, sandbox, sign-offs), and the assumptions behind each phase. Any duration given without a stated scope should be treated as a provisional estimate until the scope is written down.
What does our team need to provide for a notebook implementation?
Expect to supply a sandbox or staging environment with access to your cloud account, data center or network, plus named owners who can approve changes. Release decisions come first: Open edX supports only its latest named release, so you must choose the release and your upgrade cadence before the build starts. Course content is also yours, including the notebook files, data sets, instructions and the learner outcomes each notebook should support. Capacity estimates are required as well, covering expected concurrent sessions, the CPU and memory each learner container needs, and any peak periods such as exam windows. Confirm early which of these inputs are missing, because gaps tend to surface during testing.
Can our internal team set up Jupyter notebooks in Open edX alone?
A team with working experience in Open edX, Tutor, Docker and Kubernetes can build a pilot without outside help, but the plugin's documentation sets limits you need to plan around. The Tutor Jupyter plugin is documented for a Docker-based JupyterHub, and its README states that Kubernetes compatibility was not battle-tested and points to Zero to JupyterHub with Kubernetes for production. Production work therefore needs JupyterHub design on Kubernetes, container isolation for learner code, storage, monitoring and backups. Check whether your team has those skills in-house, who will patch and upgrade the stack, and who is on call when a learner session fails during a course run.
Does the Tutor Jupyter plugin work on Kubernetes for production?
The plugin's README does not describe Kubernetes as a tested production path. It says Kubernetes compatibility was not battle-tested and recommends Zero to JupyterHub with Kubernetes for production. The plugin runs JupyterHub with a Docker spawner, so learners get containers with limited CPU and memory on Docker hosts. A production design on Kubernetes usually means a separate JupyterHub deployment with its own spawner, storage, networking and authentication handoff from the LMS. Before committing, confirm that the jupyter-xblock works with that deployment in a test cluster, pin image versions, and verify that network policies isolate learner containers from platform services.
How should we size hosts for learner notebook containers?
Size hosts from expected concurrent sessions, not total enrollment, and then add the per-container CPU and memory limits and the overhead of the platform itself. On a Docker-based setup, the containers share host resources with the LMS, Studio, MySQL, MongoDB, the cache layer and JupyterHub, so host memory must cover all of them plus the containers. Notebook workloads spike during data loads and package installs, which makes peak sessions more important than averages. Provide your enrollment forecast, peak concurrency assumptions, notebook resource profiles and any exam schedules. Ask the implementer to show how sizing was tested under load, and what the results were, before accepting the host plan.
What should a quote for notebook work include?
A quote should state the scope in concrete terms: the number of courses, the notebook types, the environments (pilot, staging, production), the integrations and the platform release. It should list named deliverables, acceptance criteria, exclusions, assumptions and the client dependencies that could move the schedule. Separate one-time costs from recurring ones, and say whether support, upgrades and testing are included or offered as a retainer. Include the upgrade plan for the release you choose and for later named releases, along with the handover documentation and training. DeployOpen's engagements are fixed-scope and agreed in writing before work starts, so ask for the written scope document before signing.
How DeployOpen scopes, delivers and supports Jupyter notebook integrations
DeployOpen plans, implements, integrates and supports Open edX and Jupyter notebook setups inside infrastructure you control, from the Tutor installation to the notebook environment learners use. Notebook work starts with a written scope that states where the JupyterHub runs, how many learners it must serve, and whether production uses Docker or Kubernetes. Engagements are fixed-scope, and ongoing support is available as a retainer.
How we scope
- The written scope names the courses, notebook XBlocks and learner groups that will use Jupyter in Open edX.
- It records where the JupyterHub runs, whether that is a Docker host or a Kubernetes cluster you operate.
- Resource limits per learner container, storage for notebook data and the Kubernetes production path are scoped before build.
How we build
- Install Open edX with Tutor and enable the Jupyter plugin, then confirm the jupyter-xblock loads in LMS and Studio.
- Configure the Docker spawner, container CPU and memory limits, and images that match the notebook content.
- Test with a course staging instance, including concurrent learner sessions, before any course goes live.
How we hand over
- Documentation covers the Tutor configuration, plugin settings, image and volume layout, and how to add a new notebook course.
- Runbooks describe container cleanup, restarts, log locations and the steps to roll back a plugin change.
- Your team receives a walkthrough of the deployment with named owners for the JupyterHub, the LMS and the host.
How we support
- Support retainers cover Tutor and plugin upgrades, reviewed against the current Open edX named release before rollout.
- Monitoring of host capacity and container failures is scoped during handover, then reviewed with your operations team.
- Production move to Kubernetes is scoped as separate work since the plugin README states its Kubernetes compatibility was not battle-tested.

