├── .gitignore ├── Procfile ├── environments ├── kernel │ ├── python3 │ │ └── python3.yml │ └── python2 │ │ └── python2.yml ├── latest.yml ├── default.yml └── multi_kernel.yml ├── notebooks └── readme.txt ├── start_jupyter ├── manifest.yml ├── environment.yml ├── app.json ├── LICENSE ├── .jupyter └── jupyter_notebook_config.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: ./start_jupyter 2 | -------------------------------------------------------------------------------- /environments/kernel/python3/python3.yml: -------------------------------------------------------------------------------- 1 | name: Python3 2 | dependencies: 3 | - python=3 -------------------------------------------------------------------------------- /environments/kernel/python2/python2.yml: -------------------------------------------------------------------------------- 1 | name: Python2 2 | dependencies: 3 | - python=2.7 4 | - ipykernel 5 | -------------------------------------------------------------------------------- /notebooks/readme.txt: -------------------------------------------------------------------------------- 1 | Files in this folder will not be stored. Restarting the app may result in data loss. 2 | 3 | Is your ContentsManager configured correctly? 4 | -------------------------------------------------------------------------------- /start_jupyter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | pgcontents init -l $DATABASE_URL --no-prompt 4 | 5 | jupyter contrib nbextension install --user 6 | jupyter nbextensions_configurator enable --user 7 | 8 | jupyter notebook \ 9 | --no-browser --no-mathjax --ip=* --port $PORT \ 10 | $JUPYTER_NOTEBOOK_ARGS \ 11 | notebooks 12 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | # Manifest for use in CloudFoundry 2 | --- 3 | applications: 4 | - name: jupyter 5 | memory: 512M 6 | instances: 1 7 | buildpack: https://github.com/pl31/heroku-buildpack-conda.git 8 | random-route: true 9 | command: ~/start_jupyter 10 | services: 11 | # enforce a postgres database 12 | - jupyter-db 13 | -------------------------------------------------------------------------------- /environments/latest.yml: -------------------------------------------------------------------------------- 1 | name: root 2 | dependencies: 3 | - pip 4 | - notebook 5 | - ipywidgets 6 | - requests 7 | - ipython 8 | - tornado 9 | # get dependencies for pgcontents 10 | - sqlalchemy 11 | - alembic 12 | - click 13 | - cryptography 14 | - six 15 | - pip: 16 | - pgcontents 17 | - psycopg2-binary 18 | - git+git://github.com/ipython-contrib/jupyter_contrib_nbextensions.git 19 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: heroku-jupyter 2 | dependencies: 3 | # bind notebook version (compatibility to pgcontents) 4 | - notebook = 4 5 | - ipywidgets 6 | - requests 7 | # bind ipython version (compatibility to pgcontents) 8 | - ipython = 5 9 | # bind tornado version (websocket problems) 10 | - tornado = 4.5 11 | # get dependencies for pgcontents 12 | - certifi 13 | - click 14 | - cryptography 15 | - mako 16 | - psycopg2 17 | - six 18 | - sqlalchemy 19 | - nose 20 | - pip: 21 | - pgcontents 22 | - git+git://github.com/ipython-contrib/jupyter_contrib_nbextensions.git 23 | -------------------------------------------------------------------------------- /environments/default.yml: -------------------------------------------------------------------------------- 1 | name: root 2 | dependencies: 3 | - pip 4 | # bind notebook version (compatibility to pgcontents) 5 | - notebook = 4 6 | - ipywidgets 7 | - requests 8 | # bind ipython version (compatibility to pgcontents) 9 | - ipython = 5 10 | # bind tornado version (websocket problems) 11 | - tornado = 4.5 12 | # get dependencies for pgcontents 13 | - certifi 14 | - click 15 | - cryptography 16 | - mako 17 | - psycopg2 18 | - six 19 | - sqlalchemy 20 | - nose 21 | - pip: 22 | - pgcontents==0.5.3 23 | - git+git://github.com/ipython-contrib/jupyter_contrib_nbextensions.git 24 | -------------------------------------------------------------------------------- /environments/multi_kernel.yml: -------------------------------------------------------------------------------- 1 | name: root 2 | dependencies: 3 | # bind notebook version (compatibility to pgcontents) 4 | - notebook = 4 5 | - nb_conda_kernels 6 | - ipywidgets 7 | - requests 8 | # bind ipython version (compatibility to pgcontents) 9 | - ipython = 5 10 | # bind tornado version (websocket problems) 11 | - tornado = 4.5 12 | # get dependencies for pgcontents 13 | - certifi 14 | - click 15 | - cryptography 16 | - mako 17 | - psycopg2 18 | - six 19 | - sqlalchemy 20 | - nose 21 | - pip: 22 | - pgcontents 23 | - git+git://github.com/ipython-contrib/jupyter_contrib_nbextensions.git 24 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyter", 3 | "buildpacks": [ 4 | { "url": "https://github.com/pl31/heroku-buildpack-conda.git" } 5 | ], 6 | "env": { 7 | "JUPYTER_NOTEBOOK_PASSWORD": { 8 | "description": "IMPORTANT! Set a secure password." 9 | }, 10 | "JUPYTER_NOTEBOOK_ARGS": { 11 | "description": "Additional command line arguments.", 12 | "required": false, 13 | "value": "" 14 | }, 15 | "ENVIRONMENT_YML": { 16 | "description": "Alternative environment.yml path.", 17 | "required": false, 18 | "value": "" 19 | }, 20 | "ADDITIONAL_ENVIRONMENT_YML": { 21 | "description": "Additional environment.yml path.", 22 | "required": false, 23 | "value": "" 24 | } 25 | }, 26 | "addons": [ 27 | "heroku-postgresql:hobby-dev" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 p-a-c-o 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.jupyter/jupyter_notebook_config.py: -------------------------------------------------------------------------------- 1 | try: 2 | import os 3 | import json 4 | import traceback 5 | import IPython.lib 6 | import pgcontents 7 | 8 | c = get_config() 9 | 10 | ### Password protection ### 11 | # http://jupyter-notebook.readthedocs.io/en/latest/security.html 12 | if os.environ.get('JUPYTER_NOTEBOOK_PASSWORD_DISABLED') != 'DangerZone!': 13 | passwd = os.environ['JUPYTER_NOTEBOOK_PASSWORD'] 14 | c.NotebookApp.password = IPython.lib.passwd(passwd) 15 | else: 16 | c.NotebookApp.token = '' 17 | c.NotebookApp.password = '' 18 | 19 | ### PostresContentsManager ### 20 | database_url = os.getenv('DATABASE_URL', None) 21 | if database_url: 22 | # Tell IPython to use PostgresContentsManager for all storage. 23 | c.NotebookApp.contents_manager_class = pgcontents.PostgresContentsManager 24 | 25 | # Set the url for the database used to store files. See 26 | # http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html#postgresql 27 | # for more info on db url formatting. 28 | c.PostgresContentsManager.db_url = database_url 29 | 30 | # PGContents associates each running notebook server with a user, allowing 31 | # multiple users to connect to the same database without trampling each other's 32 | # notebooks. By default, we use the result of result of getpass.getuser(), but 33 | # a username can be specified manually like so: 34 | c.PostgresContentsManager.user_id = 'heroku' 35 | 36 | # Set a maximum file size, if desired. 37 | #c.PostgresContentsManager.max_file_size_bytes = 1000000 # 1MB File cap 38 | 39 | ### CloudFoundry specific settings 40 | vcap_application_json = os.getenv('VCAP_APPLICATION', None) 41 | if vcap_application_json: 42 | vcap_application = json.loads(vcap_application_json) 43 | uri = vcap_application['uris'][0] 44 | c.NotebookApp.allow_origin = 'https://{}'.format(uri) 45 | c.NotebookApp.websocket_url = 'wss://{}:4443'.format(uri) 46 | 47 | except Exception: 48 | traceback.print_exc() 49 | # if an exception occues, notebook normally would get started 50 | # without password set. For security reasons, execution is stopped. 51 | exit(-1) 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # heroku-jupyter 2 | 3 | *Currently none of the configurations work properly. 4 | I am not able to figure out a working environment.yml. 5 | Sad enough older configurations do not work, as deprecated 6 | packages have been removed from package repositories.* 7 | 8 | *Instead of using anaconda, it might be more stable to create 9 | a docker container to run in heroku. See as an example 10 | [heroku-debian-jupyter](https://github.com/pl31/heroku-debian-jupyter), 11 | but there is still no support for a heroku deploy button from heroku* 12 | 13 | Use this application to deploy [Jupyter Notebook](https://jupyter.org/) to 14 | heroku or CloudFoundry. If a postgres database is available, 15 | [pgcontents](https://github.com/quantopian/pgcontents) is used as notebook 16 | storage. 17 | 18 | ## Quick start 19 | 20 | Jupyter will not start, if the environment variable `JUPYTER_NOTEBOOK_PASSWORD` 21 | was not set. 22 | 23 | If you want to customize your app, easiest is to fork this repository. 24 | 25 | ## Installation instructions 26 | 27 | ### heroku - automatic deployment 28 | 29 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 30 | 31 | If you forked this repository, you can link it to your heroku app afterwards. 32 | 33 | ### heroku - manual deployment 34 | 35 | Push this repository to your app or fork this repository on github and link your 36 | repository to your heroku app. 37 | 38 | Use the [heroku-buildpack-conda](https://github.com/pl31/heroku-buildpack-conda): 39 | ``` 40 | $ heroku buildpacks:set https://github.com/pl31/heroku-buildpack-conda.git -a 41 | ``` 42 | 43 | Jupyter notebook will not start until the environment variable 44 | `JUPYTER_NOTEBOOK_PASSWORD` is set. Use a good password: 45 | ``` 46 | $ heroku config:set JUPYTER_NOTEBOOK_PASSWORD= -a 47 | ``` 48 | 49 | If you are really sure, that you do not want a password protected notebook 50 | server, you can set `JUPYTER_NOTEBOOK_PASSWORD_DISABLED` to `DangerZone!`. 51 | 52 | ### CloudFoundry 53 | 54 | - Clone this repository 55 | - Create a postgres database service with name `jupyter-db` 56 | - Deploy using `cf push` 57 | - Set `JUPYTER_NOTEBOOK_PASSWORD` using `cf set-env`. Do not forget to restart application. 58 | 59 | ## Environment variables 60 | 61 | - `JUPYTER_NOTEBOOK_PASSWORD`: Set password for notebooks 62 | - `JUPYTER_NOTEBOOK_PASSWORD_DISABLED`: Set to `DangerZone!` to disable password 63 | protection 64 | - `JUPYTER_NOTEBOOK_ARGS`: Additional command line args passed to 65 | `jupyter notebook`; e.g. get a more verbose logging using `--debug` 66 | 67 | ## Python version 68 | 69 | If you want to use a special python version, you should set it in your environment.yml: 70 | 71 | ``` 72 | name: root 73 | dependencies: 74 | - python=2.7 75 | - ... 76 | ``` 77 | 78 | ## Environments 79 | 80 | *Experimental feature - in work* 81 | 82 | - Parametrize default environment using ENVIRONMENT_YML 83 | - Add additional kernel(s) to jupyter installation (Python2 and Python3 in parallel) 84 | - Allow changes and experimental features without damaging defult configuration 85 | 86 | | Deployment | Features | Description | 87 | | ---------- | -------- | ----------- | 88 | | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?env[ENVIRONMENT_YML]=environments/default.yml) | Python3, IPython5 | Default Environment 89 | | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?env[ENVIRONMENT_YML]=environments/latest.yml) | Python3 | Latest, no version binding 90 | | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?env[ENVIRONMENT_YML]=environments/multi_kernel.yml&env[ADDITIONAL_ENVIRONMENT_YML]=environments/kernel/python2/python2.yml) | Python3, IPython5 + Python2 | Default Environment + Python2 91 | --------------------------------------------------------------------------------