<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Python on kistu</title>
  <link rel="alternate" href="/tags/python/" />
  <link rel="self" href="/tags/python/index.xml" />
  <subtitle>Recent content in Python on kistu</subtitle>
  <id>/tags/python/</id>
  <generator uri="http://gohugo.io" version="0.124.0">Hugo</generator>
  <language>en</language>
  <updated>2025-11-11T09:10:59+02:00</updated>
  <author>
    
    
  </author>
  
      <entry>
        <title>Jupyter Notebook on Docker</title>
        <link rel="alternate" href="/posts/001-jupyter-on-docker/" />
        <id>/posts/001-jupyter-on-docker/</id>
        <published>2025-11-11T09:10:59+02:00</published>
        <updated>2026-04-17T09:37:59+02:00</updated>
        <summary type="html">The design described here is for a very specific set of requirements; to create a reproducable and easy to configure environment to use Jupyter notebooks - one that is suitable for quickly experimenting and sharing ideas. Multiple virtual environments will be required for use across different projects.
Install Jupyter in a virtual environment Create and setup the virtual environment:
python3 -m virtualenv env-name source env-name/bin/activate pip3 install jupyter Run Jupyter Notebook:</summary>
          <content type="html"><![CDATA[<p>The design described here is for a very specific set of requirements; to create a reproducable and easy to configure environment to use Jupyter notebooks - one that is suitable for quickly experimenting and sharing ideas. Multiple virtual environments will be required for use across different projects.</p>
<h2 id="install-jupyter-in-a-virtual-environment">Install Jupyter in a virtual environment</h2>
<p>



    
    <input type="checkbox" id="zoomCheck-8a28a" hidden>
    <label for="zoomCheck-8a28a">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="install-jupyter-in-virtualenv.svg" alt="install jupyter in virtualenv" 
             />
    </label>

</p>
<p>Create and setup the virtual environment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">python3 -m virtualenv env-name
</span></span><span class="line"><span class="cl"><span class="nb">source</span> env-name/bin/activate
</span></span><span class="line"><span class="cl">pip3 install jupyter
</span></span></code></pre></div><p>Run Jupyter Notebook:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">jupyter notebook
</span></span></code></pre></div><p>While it is very easy to install modules by sourcing the correct virtualenv and using <code>pip</code>, there are a few drawbacks to this approach. Jupyter Notebook needs to be installed for each new environment created. If used as a server, this would become quite tedious and difficult to maintain - particularly if there is a need to use multiple environemnts at once with different users.</p>
<h2 id="install-jupyter-as-global-system-package">Install Jupyter as global system package</h2>
<p>



    
    <input type="checkbox" id="zoomCheck-f536f" hidden>
    <label for="zoomCheck-f536f">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="install-jupyter-as-system-package.svg" alt="install jupyter as system package" 
             />
    </label>

</p>
<p>The Jupyter Notebook installation can be centralized using the system package manager. Virtual environments are created separately.</p>
<p>To install Jupyter Notebook on Debian based systems, use <code>apt</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt install python3 python3-virtualenv jupyter
</span></span><span class="line"><span class="cl">jupyter notebook
</span></span></code></pre></div><p>This is convenient because there is only one Jupyter Notebook installation - it can be accessed on localhost:8888. Next, virtual environments must created, and modules need to be installed in different virtual environments.</p>
<h3 id="commands">Commands</h3>
<p>Commands can be run from within a notebook. A better idea of how Jupyter Notebook works is needed to understand this. Internally, it has a component called the IPython Kernel. This is responsible for communicating with the Python interpreter on the system. The <code>ipykernel</code> is included by default when Jupyter Notebook is installed, but it can also be installed independently.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-28a4e" hidden>
    <label for="zoomCheck-28a4e">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="how-ipython-kernel-integrates.svg" alt="how ipython kernel integrates" 
             />
    </label>

</p>
<p>Shell commands are provided by the operating system. They can be run by prefixing a line with an exclaimation mark:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="err">!</span><span class="n">ls</span> <span class="p">;</span> <span class="n">cd</span> <span class="n">example_dir</span>
</span></span></code></pre></div><p>This spawns a subprocess that exits upon completion; it is equivalent (on Linux) to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">subprocess</span>
</span></span><span class="line"><span class="cl"><span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">([</span><span class="s2">&#34;bash&#34;</span><span class="p">,</span> <span class="s2">&#34;-c&#34;</span><span class="p">,</span> <span class="s2">&#34;ls ; cd example_dir&#34;</span><span class="p">])</span>
</span></span></code></pre></div><p>In this case, the <code>ls</code> will list the files in the current directory and <code>cd</code> will change the directory. The change in directory will not persist since the process exists after completion.</p>
<p>Magic commands (prefixed with a percentage sign) are provided by the IPython kernel. State changes persist because these commands are run inside the kernel process itself (and apply to the virtual environment in which the kernel was installed):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">%</span><span class="n">cd</span> <span class="n">example_dir</span>
</span></span><span class="line"><span class="cl"><span class="o">%</span><span class="n">page</span> <span class="n">my_file</span><span class="o">.</span><span class="n">txt</span>
</span></span><span class="line"><span class="cl"><span class="o">%</span><span class="n">pip</span> <span class="n">install</span> <span class="n">bs4</span>
</span></span></code></pre></div><p>This prints <code>example_dir/my_file.txt</code> and installs BeautifulSoup (a module for HTML parsing) in the current virtual environment.</p>
<p>For all available magic commands, refer to the <a href="https://ipython.readthedocs.io/en/stable/interactive/magics.html">IPython documentation</a> or run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">%</span><span class="n">lsmagic</span>
</span></span></code></pre></div><p>To install a module in the current virtual environment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">%</span><span class="n">pip</span> <span class="n">install</span> <span class="n">module_name</span>
</span></span></code></pre></div><p>It is not possible to create and use a virtual environment directly within a notebook. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">!python -m virtualenv myvenv
</span></span><span class="line"><span class="cl">!source myvenv/bin/activate
</span></span><span class="line"><span class="cl">!pip install bs4
</span></span></code></pre></div><p>The first two commands succeed but the third fails with an <code>externally-managed-environment</code> error. This is because a new subprocess is created for each command - the sourced environment is not actually activated when pip is used. This can be resolved by chaining the command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">!python -m virtualenv myvenv<span class="p">;</span> <span class="nb">source</span> myvenv/bin/activate<span class="p">;</span> pip install bs4
</span></span></code></pre></div><p>BeautifulSoup now installs in the <code>myvenv</code> virtual environment, but the notebook does not use this environment. The virtual environment that the IPython kernel is running on is different to the one created - virtual environments cannot be nested!</p>
<h3 id="multiple-virtual-environments">Multiple Virtual Environments</h3>
<p>To make a virtual environments available to Jupyter Notebook, the ipykernel module should be installed inside the virtual environment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">python3 -m virtualenv --system-site-packages env-name
</span></span><span class="line"><span class="cl"><span class="nb">source</span> env-name/bin/activate
</span></span><span class="line"><span class="cl">pip install ipykernel
</span></span><span class="line"><span class="cl">python3 -m ipykernel install --user --name<span class="o">=</span>env-name --display-name <span class="s2">&#34;Python (env-name)&#34;</span>
</span></span></code></pre></div><blockquote>
<p>The <code>--system-site-packages</code> flag is particularly important if you depend on globally installed modules. For example, <code>matplotlib</code> has to be specially packaged for ARM platforms like Raspberry Pi and installed using the system package manager. This flag ensures that these modules can be found.</p>
</blockquote>
<p>Since the Jupyter Notebook installation is global, the above commands can be easily run from the interface by creating a new terminal.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-50c75" hidden>
    <label for="zoomCheck-50c75">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="jupyter-new-menu.png" alt="jupyter new menu" 
             />
    </label>

</p>
<p>When creating a new notebook, you can select <code>Python (env-name)</code> to use a particular virtual environment.</p>
<p>To list all kernels and remove a particular kernel:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">jupyter-kernelspec list
</span></span><span class="line"><span class="cl">jupyter-kernelspec uninstall env-name
</span></span></code></pre></div><p>There is still a problem - this is running directly on the server. If something goes wrong, it might affect other services. This is also not very easily portable.</p>
<h2 id="install-jupyter-on-docker">Install Jupyter on Docker</h2>
<p>Using docker, this can be containerized!</p>
<p>



    
    <input type="checkbox" id="zoomCheck-aeca6" hidden>
    <label for="zoomCheck-aeca6">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="install-jupyter-in-docker.svg" alt="install jupyter in docker" 
             />
    </label>

</p>
<p>This Dockerfile uses Alpine linux as the base image and installs <code>python</code>, <code>pip</code>, <code>virtualenv</code> and <code>jupyter-notebook</code> using the Alpine package manager <code>apk</code>.
The last command starts the notebook. This probably isn&rsquo;t very secure - you might want to modify the flags based on your security model.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> alpine:3.22</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apk add python3 py3-pip py3-virtualenv jupyter-notebook<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="s"> 8888</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;jupyter-notebook&#34;</span><span class="p">,</span> <span class="s2">&#34;--ip=0.0.0.0&#34;</span><span class="p">,</span> <span class="s2">&#34;--no-browser&#34;</span><span class="p">,</span> <span class="s2">&#34;--allow-root&#34;</span><span class="p">,</span> <span class="s2">&#34;--NotebookApp.token=&#39;your-very-long-and-secure-passphrase&#39;&#34;</span><span class="p">]</span><span class="err">
</span></span></span></code></pre></div><p>Now, creating the following docker-compose.yaml file and running <code>docker compose up</code> will build the image and expose Jupyter Notebook on localhost:4444. To manage the virtual environments, use the terminal and follow the steps in the previous section.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">services:
</span></span><span class="line"><span class="cl">  jupyter:
</span></span><span class="line"><span class="cl">    container_name: jupyter
</span></span><span class="line"><span class="cl">    build: .
</span></span><span class="line"><span class="cl">    ports:
</span></span><span class="line"><span class="cl">      - 4444:8888
</span></span></code></pre></div><p>To reproduce this cionfiguration on a different service, just the <code>Dockerfile</code> and <code>docker-compose.yaml</code> need to be copied.</p>
]]></content>
      </entry>

</feed>


