Nathan Grigg

Persistent IPython notebook server with launchd, virtual host, and proxy

I have been using IPython for interactive Python shells for several years. For most of that time, I have resisted the web-browser-based notebook interface and mainly used the console version. Despite my love of all things texty, I finally gave in, and began using the web version almost exclusively. So much that I got annoyed at constantly needing to start and stop the IPython server and having a terminal dedicated to running it.

Always running server using Launchd

My first step was to always keep the IPython server running. I did this with a KeepAlive launchd job. Here is the plist:

<plist version="1.0">
<dict>
    <key>KeepAlive</key>
    <true/>
    <key>Label</key>
    <string>net.nathangrigg.ipython</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/python3/bin/ipython</string>
        <string>notebook</string>
        <string>--no-browser</string>
        <string>--quiet</string>
        <string>--port=10223</string>
        <string>--notebook-dir=/Users/grigg/Dropbox/Notebooks</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/Users/grigg/Library/Logs/LaunchAgents/ipython-notebook.stderr</string>
    <key>StandardOutPath</key>
    <string>/Users/grigg/Library/Logs/LaunchAgents/ipython-notebook.stdout</string>
</dict>
</plist>

This job runs ipython notebook with the --port flag, so that the port stays the same each time.

I used LaunchControl to create and load this launch agent, but you can also just save it in ~/Library/LaunchAgents and run launchctl load.

If you want, you can be done now. The notebook browser is running at http://localhost:10223.

Virtual host and proxy using Apache

But I was not done, because I already had too many processes on my machine that were serving content at some localhost port. This required me to memorize port numbers, made Safari’s autocorrect not very useful, and felt barbaric. What I needed was a domain name that resolved to http://localhost:10223. To do this, I needed a virtual host and a proxy.

Before reading further, you should know that I am not an Apache expert. In fact, I have never managed an Apache webserver except as a hobby. The best I can promise you is that this works for me, on my OS X computer, for now.

In /etc/hosts, I created a new host called py.

127.0.0.1     py

This resolves py to 127.0.0.1, i.e., localhost.

Now in /etc/apache2/httpd.conf I created a virtual host and a proxy.

<VirtualHost 127.0.0.1:80>
    ServerName py
    ProxyPass /api/kernels/ ws://localhost:10223/api/kernels/
    ProxyPassReverse /api/kernels/ ws://localhost:10223/api/kernels/
    ProxyPass / http://localhost:10223/
    ProxyPassReverse / http://localhost:10223/
    RequestHeader set Origin "http://localhost:10223/"
</VirtualHost>

This forwards all traffic to py on port 80 to localhost on port 10223. Note that the order of the ProxyPass directives is apparently important. Also, if you use * instead of the address in the VirtualHost directive, you might also be forwarding requests originating outside of your machine, which sounds dangerous.

Then I ran sudo apachectl restart, and everything seemed to work.

Note that Safari interprets py as a Google search, so I have to type py/. Chrome does the same thing, except for that after I load py/ once, the trailing slash is optional.