# Bokeh server embedded in gunicorn/Flask hosted to cloud

**URL:** <https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199>\
**Category:** Community Support\
**Tags:** server\
**Created:** [August 22, 2020, 4:11pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199 "2020-08-22T16:11:13Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 22, 2020, 4:11pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/1 "2020-08-22T16:11:13Z")

</div>

This topic is cross-referenced to [Holoviz Panel discourse topic 1124](https://discourse.holoviz.org/t/panel-server-embedded-in-gunicorn-flask-hosted-to-cloud/1124) because the panel server is the bokeh server, and the issue is really one of low-level server setup and communication.

> [@\_jm](#):
>
> I have a panel server embedded in a Flask app which is run via gunicorn with multiple worker processes. This works well when served on a either a Linux or Mac OSX system on an intranet, and I verified that I can also make it available externally for quick testing purposes.
> 
> I want to take the next step to deploy it to a cloud service like Heroku or the Google App Engine, but have been unsuccessful thus far. The application builds and deploys but the panel (or bokeh model) content does not render and the JavaScript console ultimately throws a connection-timeout-error.
> 
> The panel server deployment documentation gives sufficient detail to deploy a panel server application to Heroku, but the next piece is when this needs to be run through / embedded in a web framework like Flask that is ultimately serving additional content, managing user access to certain routes, etc.
> 
> As such, the panel(bokeh) server needs to be called via `server_document()` accessors as illustrated in the “official” bokeh Github repo example here: [bokeh Flask gunicorn embed example](https://github.com/bokeh/bokeh/blob/master/examples/howto/server_embed/flask_gunicorn_embed.py).
> 
> Is there an example of such an implementation being successfully deployed to Heroku?
> 
> Thanks!

---

<div class="post-metadata">

**Author:** ![Bryan](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/bryan/32/3625_2.png) [@Bryan](https://discourse.bokeh.org/u/Bryan)\
**Post date:** [August 22, 2020, 4:17pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/2 "2020-08-22T16:17:40Z")

</div>

I don’t have any experience with Heroku (maybe others do and can chime in) so all I can offer is speculation based on reported information.

> [@\_jm](#):
>
> ultimately throws a connection-timeout-error.

What is the exact error? Is the timeout on the initial HTTP request, or on the webscocket upgrade? Is there any more information if you raise `BOKEH_LOG_LEVEL` to `debug`? What about the Flask/Bokeh server console logs, any messages there?

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 22, 2020, 4:45pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/3 "2020-08-22T16:45:39Z")

</div>

Hey @Bryan

Thanks for the quick reply.

The following info is all from a simple example very much like the bokeh Github flask\_gunicorn\_embed.py example.

The error that shows up in the JavaScript console approximately 5 min after the boilerplate bokeh example div/banner as in all the server embedding examples is …

 ![Screen Shot 2020-08-22 at 12.35.51 PM](https://discourse-uploads.bokeh.org/original/2X/3/3979100d1a4309c15558bb9431813846dd601d3b.png)

And the info under that link is (with my actual application name removed/masked out) …

`http://<masked-app-name>.herokuapp.com:43096/bkapp/autoload.js?bokeh-autoload-element=1001&bokeh-app-path=/bkapp&bokeh-absolute-url=http://<masked-app-name>.herokuapp.com:43096/bkapp`

I need to figure out how to get the bokeh server logs on Heroku. The environment variable is set in my app workspace but I don’t see anything in the Heroku logs from bokeh. (This did work on when I tried on Google App Engine, but there the bokeh server just kept periodically reporting that it had zero clients connected.)

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 24, 2020, 5:40pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/4 "2020-08-24T17:40:58Z")

</div>

@Bryan

There are no JavaScript bokeh logs generated at the `DEBUG` log level. There are Python bokeh logs generated at the `DEBUG` log level.

The following are the logs from the Heroku app with my application name masked out from the URL. gunicorn has 3 worker processes in my configuration file.

```
2020-08-24T16:12:05.749354+00:00 heroku[web.1]: Restarting
2020-08-24T16:12:05.751681+00:00 heroku[web.1]: State changed from up to starting
2020-08-24T16:12:06.485783+00:00 heroku[web.1]: Stopping all processes with SIGTERM
2020-08-24T16:12:06.509684+00:00 app[web.1]: [2020-08-24 16:12:06 +0000] [10] [INFO] Worker exiting (pid: 10)
2020-08-24T16:12:06.509724+00:00 app[web.1]: [2020-08-24 16:12:06 +0000] [11] [INFO] Worker exiting (pid: 11)
2020-08-24T16:12:06.509731+00:00 app[web.1]: [2020-08-24 16:12:06 +0000] [4] [INFO] Handling signal: term
2020-08-24T16:12:06.511342+00:00 app[web.1]: [2020-08-24 16:12:06 +0000] [9] [INFO] Worker exiting (pid: 9)
2020-08-24T16:12:06.810778+00:00 app[web.1]: [2020-08-24 16:12:06 +0000] [4] [INFO] Shutting down: Master
2020-08-24T16:12:06.958100+00:00 heroku[web.1]: Process exited with status 0
2020-08-24T16:12:18.519919+00:00 heroku[web.1]: Starting process with command `gunicorn --config gunicorn_config.py main:app`
2020-08-24T16:12:21.520369+00:00 app[web.1]: [2020-08-24 16:12:21 +0000] [4] [INFO] Starting gunicorn 20.0.4
2020-08-24T16:12:21.520922+00:00 app[web.1]: [2020-08-24 16:12:21 +0000] [4] [INFO] Listening at: http://0.0.0.0:19015 (4)
2020-08-24T16:12:21.521041+00:00 app[web.1]: [2020-08-24 16:12:21 +0000] [4] [INFO] Using worker: sync
2020-08-24T16:12:21.525772+00:00 app[web.1]: [2020-08-24 16:12:21 +0000] [9] [INFO] Booting worker with pid: 9
2020-08-24T16:12:21.545715+00:00 app[web.1]: [2020-08-24 16:12:21 +0000] [10] [INFO] Booting worker with pid: 10
2020-08-24T16:12:21.623643+00:00 app[web.1]: [2020-08-24 16:12:21 +0000] [11] [INFO] Booting worker with pid: 11
2020-08-24T16:12:22.098875+00:00 heroku[web.1]: State changed from starting to up
2020-08-24T16:12:26.420319+00:00 app[web.1]: INFO:bokeh.server.tornado:User authentication hooks NOT provided (default user enabled)
2020-08-24T16:12:26.420488+00:00 app[web.1]: INFO:bokeh.server.tornado:User authentication hooks NOT provided (default user enabled)
2020-08-24T16:12:26.420490+00:00 app[web.1]: DEBUG:bokeh.server.tornado:These host origins can connect to the websocket: ['<masked-app-name>.herokuapp.com', '0.0.0.0']
2020-08-24T16:12:26.420613+00:00 app[web.1]: DEBUG:bokeh.server.tornado:These host origins can connect to the websocket: ['<masked-app-name>.herokuapp.com', '0.0.0.0']
2020-08-24T16:12:26.420700+00:00 app[web.1]: DEBUG:bokeh.server.tornado:Patterns are:
2020-08-24T16:12:26.420782+00:00 app[web.1]: DEBUG:bokeh.server.tornado:Patterns are:
2020-08-24T16:12:26.422155+00:00 app[web.1]: DEBUG:bokeh.server.tornado: [('/bkapp/?',
2020-08-24T16:12:26.422251+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.doc_handler.DocHandler'>,
2020-08-24T16:12:26.422341+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e5b0e5b0>,
2020-08-24T16:12:26.422436+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.422546+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/ws',
2020-08-24T16:12:26.422645+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.ws.WSHandler'>,
2020-08-24T16:12:26.422645+00:00 app[web.1]: DEBUG:bokeh.server.tornado: [('/bkapp/?',
2020-08-24T16:12:26.422732+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e5b0e5b0>,
2020-08-24T16:12:26.422736+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.doc_handler.DocHandler'>,
2020-08-24T16:12:26.422821+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.422910+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/metadata',
2020-08-24T16:12:26.422959+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3a42280>,
2020-08-24T16:12:26.423024+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.metadata_handler.MetadataHandler'>,
2020-08-24T16:12:26.423079+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e5b0e5b0>,
2020-08-24T16:12:26.423152+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.423226+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/autoload.js',
2020-08-24T16:12:26.423301+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.autoload_js_handler.AutoloadJsHandler'>,
2020-08-24T16:12:26.423329+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.423370+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e5b0e5b0>,
2020-08-24T16:12:26.423444+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.423501+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/ws',
2020-08-24T16:12:26.423502+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/?',
2020-08-24T16:12:26.423564+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.root_handler.RootHandler'>,
2020-08-24T16:12:26.423633+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.ws.WSHandler'>,
2020-08-24T16:12:26.423679+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'applications': {'/bkapp': <bokeh.server.contexts.ApplicationContext object at 0x7f30e5b0e5b0>},
2020-08-24T16:12:26.423735+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3a42280>,
2020-08-24T16:12:26.423793+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'index': None,
2020-08-24T16:12:26.423832+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.423904+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'prefix': '',
2020-08-24T16:12:26.423931+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/metadata',
2020-08-24T16:12:26.424013+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'use_redirect': True}),
2020-08-24T16:12:26.424017+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.metadata_handler.MetadataHandler'>,
2020-08-24T16:12:26.424102+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/static/extensions/(.*)',
2020-08-24T16:12:26.424105+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3a42280>,
2020-08-24T16:12:26.424192+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.multi_root_static_handler.MultiRootStaticHandler'>,
2020-08-24T16:12:26.424196+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.424300+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/autoload.js',
2020-08-24T16:12:26.424316+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'root': {}}),
2020-08-24T16:12:26.424401+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.autoload_js_handler.AutoloadJsHandler'>,
2020-08-24T16:12:26.424451+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/static/(.*)',
2020-08-24T16:12:26.424501+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3a42280>,
2020-08-24T16:12:26.424584+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.static_handler.StaticHandler'>)]
2020-08-24T16:12:26.424639+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.424742+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/?',
2020-08-24T16:12:26.424840+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.root_handler.RootHandler'>,
2020-08-24T16:12:26.424941+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'applications': {'/bkapp': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3a42280>},
2020-08-24T16:12:26.425046+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'index': None,
2020-08-24T16:12:26.425139+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'prefix': '',
2020-08-24T16:12:26.425272+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'use_redirect': True}),
2020-08-24T16:12:26.425375+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/static/extensions/(.*)',
2020-08-24T16:12:26.425474+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.multi_root_static_handler.MultiRootStaticHandler'>,
2020-08-24T16:12:26.425571+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'root': {}}),
2020-08-24T16:12:26.425672+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/static/(.*)',
2020-08-24T16:12:26.425775+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.static_handler.StaticHandler'>)]
2020-08-24T16:12:26.428273+00:00 app[web.1]: INFO:bokeh.server.tornado:User authentication hooks NOT provided (default user enabled)
2020-08-24T16:12:26.428388+00:00 app[web.1]: DEBUG:bokeh.server.tornado:These host origins can connect to the websocket: ['<masked-app-name>.herokuapp.com', '0.0.0.0']
2020-08-24T16:12:26.428520+00:00 app[web.1]: DEBUG:bokeh.server.tornado:Patterns are:
2020-08-24T16:12:26.429827+00:00 app[web.1]: DEBUG:bokeh.server.tornado: [('/bkapp/?',
2020-08-24T16:12:26.429923+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.doc_handler.DocHandler'>,
2020-08-24T16:12:26.430018+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3bfac10>,
2020-08-24T16:12:26.430100+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.430204+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/ws',
2020-08-24T16:12:26.430306+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.ws.WSHandler'>,
2020-08-24T16:12:26.430407+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3bfac10>,
2020-08-24T16:12:26.430508+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.430615+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/metadata',
2020-08-24T16:12:26.430718+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.metadata_handler.MetadataHandler'>,
2020-08-24T16:12:26.430817+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3bfac10>,
2020-08-24T16:12:26.430916+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.431014+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/bkapp/autoload.js',
2020-08-24T16:12:26.431114+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.autoload_js_handler.AutoloadJsHandler'>,
2020-08-24T16:12:26.431209+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'application_context': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3bfac10>,
2020-08-24T16:12:26.431304+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'bokeh_websocket_path': '/bkapp/ws'}),
2020-08-24T16:12:26.431402+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/?',
2020-08-24T16:12:26.431497+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.root_handler.RootHandler'>,
2020-08-24T16:12:26.431591+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'applications': {'/bkapp': <bokeh.server.contexts.ApplicationContext object at 0x7f30e3bfac10>},
2020-08-24T16:12:26.431685+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'index': None,
2020-08-24T16:12:26.431778+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'prefix': '',
2020-08-24T16:12:26.431872+00:00 app[web.1]: DEBUG:bokeh.server.tornado: 'use_redirect': True}),
2020-08-24T16:12:26.431964+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/static/extensions/(.*)',
2020-08-24T16:12:26.432070+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.multi_root_static_handler.MultiRootStaticHandler'>,
2020-08-24T16:12:26.432164+00:00 app[web.1]: DEBUG:bokeh.server.tornado: {'root': {}}),
2020-08-24T16:12:26.432260+00:00 app[web.1]: DEBUG:bokeh.server.tornado: ('/static/(.*)',
2020-08-24T16:12:26.432355+00:00 app[web.1]: DEBUG:bokeh.server.tornado: <class 'bokeh.server.views.static_handler.StaticHandler'>)]
2020-08-24T16:12:41.445517+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] 0 clients connected
2020-08-24T16:12:41.445585+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] /bkapp has 0 sessions with 0 unused
2020-08-24T16:12:41.449455+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] 0 clients connected
2020-08-24T16:12:41.449456+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] 0 clients connected
2020-08-24T16:12:41.449542+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] /bkapp has 0 sessions with 0 unused
2020-08-24T16:12:41.449546+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] /bkapp has 0 sessions with 0 unused
2020-08-24T16:12:55.085552+00:00 heroku[router]: at=info method=GET path="/" host=<masked-app-name>.herokuapp.com request_id=1ba438cf-e580-4a93-a175-1dff1a81d79f fwd="71.206.168.180" dyno=web.1 connect=1ms service=13ms status=200 bytes=1185 protocol=http
2020-08-24T16:12:55.087722+00:00 app[web.1]: 10.30.61.229 - - [24/Aug/2020:16:12:55 +0000] "GET / HTTP/1.1" 200 1023 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"
2020-08-24T16:12:56.445503+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] 0 clients connected
2020-08-24T16:12:56.445514+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] 0 clients connected
2020-08-24T16:12:56.445566+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] /bkapp has 0 sessions with 0 unused
2020-08-24T16:12:56.445593+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] /bkapp has 0 sessions with 0 unused
2020-08-24T16:12:56.449434+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] 0 clients connected
2020-08-24T16:12:56.449506+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] /bkapp has 0 sessions with 0 unused
2020-08-24T16:13:11.441119+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] 0 clients connected
2020-08-24T16:13:11.441206+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] /bkapp has 0 sessions with 0 unused
2020-08-24T16:13:11.441568+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] 0 clients connected
2020-08-24T16:13:11.441661+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] /bkapp has 0 sessions with 0 unused
2020-08-24T16:13:11.443266+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] 0 clients connected
2020-08-24T16:13:11.443399+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] /bkapp has 0 sessions with 0 unused
2020-08-24T16:13:26.441512+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] 0 clients connected
2020-08-24T16:13:26.441529+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] 0 clients connected
2020-08-24T16:13:26.441576+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 11] /bkapp has 0 sessions with 0 unused
2020-08-24T16:13:26.441620+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 10] /bkapp has 0 sessions with 0 unused
2020-08-24T16:13:26.442057+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] 0 clients connected
2020-08-24T16:13:26.442185+00:00 app[web.1]: DEBUG:bokeh.server.tornado:[pid 9] /bkapp has 0 sessions with 0 unused
```

---

<div class="post-metadata">

**Author:** ![Bryan](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/bryan/32/3625_2.png) [@Bryan](https://discourse.bokeh.org/u/Bryan)\
**Post date:** [August 24, 2020, 5:50pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/5 "2020-08-24T17:50:56Z")

</div>

Looking at that there does not ever seem to be any Websocket connection hitting the server. Perhaps there is some configuration necessary on Heroku to allow WS connections to pass? (There definitely is on AWS Elastic Beanstalk, for instance.)

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 25, 2020, 2:16am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/6 "2020-08-25T02:16:06Z")

</div>

@Bryan

Thanks.

I know that Heroku supports websocket connections and in some circumstances there don’t seem to be any special configuration steps, although I’ve only seen there documentation referencing use of the Flask-sockets extension.

The panel server documentation also shows specific steps to deploy a server on Heroku and by extension this should transfer directly to deploying the bokeh server. The challenge is when this is combined with a Flask web infrastructure b/c Flask is being used to serve additional content, manage user authenticated roles for specific routes, etc.

I have a support ticket open with Heroku to understand what is and what is not possible.

Perhaps it is just my recent attention to this topic, but it seems to me like a number of users are at the stage of being able to deploy to self-managed servers running on Linux or OSX but hosting to the cloud platforms as a service like Heroku, Google App Engine, or Azure is the next frontier.

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 27, 2020, 9:32pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/7 "2020-08-27T21:32:27Z")

</div>

@Bryan

My understanding is that current Heroku versions enable websockets by default although they dictate which port you’re allowed to use.

I have been able to extend my debugging app based on Bokeh’s Flask gunicorn embed example to use nginx via the heroku-community/nginx buildpack. And I can hit the index route just fine, but I get errors when it goes to retrieve the server document. Specifically, the JavaScript console shows a `net:ERR_CONNECTION_REFUSED` error.

 ![Screen Shot 2020-08-27 at 5.14.56 PM](https://discourse-uploads.bokeh.org/original/2X/b/b076db542d4449b57439df001b037984d1fb4b9c.png)

As before, there are Python messages with `BOKEH_PY_LOG_LEVEL` equals `debug`, basically say there are no clients connected. And there are no JavaScript messages with `BOKEH_LOG_LEVEL` equals debug, which makes sense b/c the connection is being refused preemptively.

The following is the NGINX config file, which is simply the default for the Heroku-community buildpack. It uses a Unix socket, and my gunicorn configuration file properly binds to that, so I get to the boilerplate banner familiar in the bokeh GitHub examples.

 ![Screen Shot 2020-08-27 at 5.27.05 PM](https://discourse-uploads.bokeh.org/original/2X/0/0efd9ef75a006e809c6a86aaa74fa7de5723cc49.png)

Any ideas on what needs to be done with the nginx configuration and/or setup of the Bokeh server to get at the websocket? (In the screenshot above, I am using the default localhost address 127.0.0.1, but I’ve tried other things here even briefly looking at Tornado’s bind\_unix\_socket methods, but to no avail.)

**NGINX configuration**

```
daemon off;
#Heroku dynos have at least 4 cores.
worker_processes <%= ENV['NGINX_WORKERS'] || 4 %>;

events {
	use epoll;
	accept_mutex on;
	worker_connections <%= ENV['NGINX_WORKER_CONNECTIONS'] || 1024 %>;
}

http {
        gzip on;
        gzip_comp_level 2;
        gzip_min_length 512;

	server_tokens off;

	log_format l2met 'measure#nginx.service=$request_time request_id=$http_x_request_id';
	access_log <%= ENV['NGINX_ACCESS_LOG_PATH'] || 'logs/nginx/access.log' %> l2met;
	error_log <%= ENV['NGINX_ERROR_LOG_PATH'] || 'logs/nginx/error.log' %>;

	include mime.types;
	default_type application/octet-stream;
	sendfile on;

	#Must read the body in 5 seconds.
	client_body_timeout 5;

	upstream app_server {
		server unix:/tmp/nginx.socket fail_timeout=0;
 	}

	server {
		listen <%= ENV["PORT"] %>;
		server_name _;
		keepalive_timeout 5;

		location / {
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header Host $http_host;
			proxy_redirect off;
			proxy_pass http://app_server;
		}
	}
}
```

---

<div class="post-metadata">

**Author:** ![p-himik](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/p-himik/32/4010_2.png) [@p-himik](https://discourse.bokeh.org/u/p-himik)\
**Post date:** [August 28, 2020, 8:19am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/8 "2020-08-28T08:19:28Z")

</div>

It’s been a long time since I’ve written this config for NGINX + Tornado, but it works.

```auto
http {
    upstream app_server {
        server 127.0.0.1:55775;
    }

    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    server {
        location / {
            proxy_pass http://app_server;
            proxy_pass_header Server;
            proxy_http_version 1.1;
            proxy_read_timeout 60h;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;

            proxy_redirect off;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;
        }
    }
}

```

I’ve removed everything that doesn’t seem to be related to WebSockets. Hopefully I didn’t remove something vital.

As far as I recall, the main points of interest are that `map` directive and those `proxy_set_header` directives for the `Upgrade` and `Connection` headers. I think `proxy_read_timeout` is also required because WebSocket connections are usually long-living.

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 28, 2020, 10:15am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/9 "2020-08-28T10:15:34Z")

</div>

@p-himik

Thanks for the help. I sincerely appreciate it.

Everything works well with nginx+gunicorn+Flask+bokeh in my scenario on a self-managed computer but not a platform as a service like Heroku. I think fundamentally it comes down to my not fully appreciating the differences / restrictions with what they refer to as dynos for hosting an app and something more concrete to me like a computer running Linux.

Perhaps the difference is that I am trying to run bokeh server as a separate process on localhost and this simply is not compatible with the concept of a dyno.

I’ve seen a suggestion you made helping others in the past on similar topics to wrap the Flask app in Tornado. Coupled with your response above, I think this will allow me to [embed Bokeh server as a library](https://docs.bokeh.org/en/latest/docs/user_guide/server.html#userguide-server) and attach to the existing Tornado IOLoop?

Let me know if I understand correctly, and I will explore that route in the meantime.

Thanks again so much for the help.

---

<div class="post-metadata">

**Author:** ![p-himik](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/p-himik/32/4010_2.png) [@p-himik](https://discourse.bokeh.org/u/p-himik)\
**Post date:** [August 28, 2020, 11:59am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/10 "2020-08-28T11:59:26Z")

</div>

Hmm, I think you’re right. At some point, the config above was used to run a Django app on Heroku. But both the Django and the Tornado apps were using a WebSocket connection via the same port as the main app. If you embed Bokeh as a library, you will be able to use the same port as well. After all, Heroku doesn’t allow you to use any port but the one it provides.

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 28, 2020, 8:48pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/11 "2020-08-28T20:48:03Z")

</div>

@p-himik @Bryan

I am trying a different architecture currently using separate Heroku dynos for the Flask app and the bokeh server.

In the following description of the problem currently faced, `<masked-app-name>` is a placeholder for my actual application’s name.

I can navigate to the server just fine via a web browser, e.g.

`https://<masked-app-name>.herokuapp.com/`

However, when I try to embed it from Flask via pull\_session() within a client context manager, it fails.

Here’s the Flask app code.

```
from flask import Flask, render_template

from bokeh.client import pull_session
from bokeh.embed import server_session

PANEL_URL = "https://<masked-app-name>.herokuapp.com"

app = Flask( __name__ )

@app.route('/', methods=['GET'])
def bkapp_page():
    with pull_session(url=PANEL_URL) as session:
        script = server_session(session_id=session.id, url=PANEL_URL)
        return render_template("embed.html", script=script, template="Flask")

```

The web browser for the Flask app shows `500 Server Internal Error`, which is also reflected in the JavaScript console.

And the Heroku log excerpt shows the following error stack trace from Bokeh client session logic …

```
2020-08-28T20:07:30.436217+00:00 app[web.1]: File "/app/main.py", line 12, in bkapp_page

2020-08-28T20:07:30.436217+00:00 app[web.1]: with pull_session(url=PANEL_URL) as session:

2020-08-28T20:07:30.436217+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/bokeh/client/session.py", line 120, in pull_session

2020-08-28T20:07:30.436218+00:00 app[web.1]: session.pull()

2020-08-28T20:07:30.436218+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/bokeh/client/session.py", line 381, in pull

2020-08-28T20:07:30.436218+00:00 app[web.1]: self.check_connection_errors()

2020-08-28T20:07:30.436220+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.8/site-packages/bokeh/client/session.py", line 367, in check_connection_errors

2020-08-28T20:07:30.436220+00:00 app[web.1]: raise OSError(f"Check your application path! The given Path is not valid: {self.url}")

2020-08-28T20:07:30.436220+00:00 app[web.1]: OSError: Check your application path! The given Path is not valid: ws://<masked-app-name>.herokuapp.com/ws

```

Is there a way to work around the ws:// protocol substitution in the URL to make it behave as if a user is navigating directly to the server via https?

---

<div class="post-metadata">

**Author:** ![p-himik](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/p-himik/32/4010_2.png) [@p-himik](https://discourse.bokeh.org/u/p-himik)\
**Post date:** [August 28, 2020, 9:14pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/12 "2020-08-28T21:14:39Z")

</div>

It’s not about the protocol, it’s about the URL as a whole:

```python
if self.error_code == 404:
    raise OSError(f"Check your application path! The given Path is not valid: {self.url}")

```

Note that `404`.

Alas, I cannot add anything else without an MRE.

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 28, 2020, 9:49pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/13 "2020-08-28T21:49:49Z")

</div>

I see. It appeared to be related to the protocol and address substitution, b/c when I enter that as the URL manually in a browser, Google Chrome shows the following.

 ![Screen Shot 2020-08-28 at 5.47.38 PM](https://discourse-uploads.bokeh.org/original/2X/0/010c165d9ccb7004e8a32ed7fdf8462e19506a5d.jpeg)

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [August 29, 2020, 11:59am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/14 "2020-08-29T11:59:13Z")

</div>

For others that have application architectures similar that embed a **bokeh** server in a **gunicorn/Flask** framework and want to deploy it to a platform as a service / cloud service, this can be done in **Heroku** using two _dynos_. After exhaustively exploring different mechanisms and helpful exchanges with the **Heroku** support team, getting everything to work in one _dyno_ is not possible.

**DETAILS**

_Flask-app_ which embeds the bokeh server on one _dyno_:

Create a python Flask app, e.g. a simple one as follows, and use the Heroku Procfile to run it via **gunicorn** , which is well documented on Heroku’s site.

```
from flask import Flask, render_template

from bokeh.client import pull_session
from bokeh.embed import server_session

BOKEH_URL = "https://<bokeh-app-name>.herokuapp.com/<bokeh-server-name>"

app = Flask( __name__ )

@app.route('/', methods=['GET'])
def bkapp_page():
    with pull_session(url=PANEL_URL) as session:
        script = server_session(session_id=session.id, url=BOKEH_URL)
        return render_template("embed.html", script=script, template="Flask")

```

_Bokeh server app_ which runs the bokeh server on second _dyno_:

Implement this as usual and create a Heroku Procfile that runs the server, e.g.

`web: bokeh serve --address="0.0.0.0" --port=$PORT <bokeh-server-name> --allow-websocket-origin=<flask-app-name>.herokuapp.com`

NB: The port argument is important b/c Heroku assigns the port that is used; you cannot choose it yourself.

NB: The quantities in \<…\> above should reflect the app names for your Heroku apps and the name of your bokeh server following the bokeh conventions if it is in single-file or directory format. Caveat, that only the single module `bokeh-server-name.py` case has been tested in determining the viability of the approach.

Thanks to @p-himik and @Bryan for the valuable help getting this to work!

---

<div class="post-metadata">

**Author:** ![edesz](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/e/d78d45/32.png) [@edesz](https://discourse.bokeh.org/u/edesz)\
**Post date:** [February 20, 2021, 8:35pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/15 "2021-02-20T20:35:05Z")

</div>

Hi @_jm, thanks for posting this answer. It is really very helpful.

I tried to follow all your instructions above, but I am running into an **Internal Server error** for my Flask app

```auto
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

```

Here are the full details of my setup based on your instructions

**Flask App**

In my Flask app’s `main.py`, I followed [your earlier post in this thread](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/11) to construct `PANEL_URL`

```auto
# main.py

# <bokeh-server-name> = mybserverapp # name of Bokeh server standalone script
# <bokeh-app-name> = mybokehserverapp # name of Heroku app for Bokeh Server
# <masked-app-name> = mydemoflaskapp # name of Heroku app for Flask+Gunicorn

PANEL_URL = "https://mydemoflaskapp.herokuapp.com"
BOKEH_URL = "https://mybokehserverapp.herokuapp.com/mybserverapp"

```

In my Flask app’s `Procfile`, I have

```auto
web: gunicorn main:app

```

In my `templates/embed.html`, I have [this content](https://github.com/bokeh/bokeh/blob/main/examples/howto/server_embed/templates/embed.html)

```auto
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Embedding a Bokeh Server</title>
</head>
<body>
    <div>
        This Bokeh app.
    </div>
    {{ script|safe }}
</body>
</html>

```

For the Flask app, my `requirements.txt` contains

```auto
bokeh
flask
gunicorn

```

This app on heroku is named `mydemoflaskapp`.

The directory structure for this Flask app is as follows

```auto
├── main.py
├── Procfile
├── requirements.txt
└── templates
    └── embed.html

```

**Bokeh App**

In my Bokeh Server app I have [the following contents](https://docs.bokeh.org/en/latest/docs/user_guide/categorical.html#basic)

```auto
# mybserverapp.py

from bokeh.layouts import column
from bokeh.plotting import curdoc, figure
from bokeh.themes import Theme

fruits = ["Apples", "Pears", "Nectarines", "Plums", "Grapes", "Strawberries"]
counts = [5, 3, 4, 2, 4, 6]

p = figure(
    x_range=fruits,
    plot_height=250,
    title="Fruit Counts",
    toolbar_location=None,
    tools="",
)
p.vbar(x=fruits, top=counts, width=0.9)
p.xgrid.grid_line_color = None
p.y_range.start = 0

curdoc().add_root(column(p))
curdoc().theme = Theme(filename="theme.yaml")

```

My Bokeh app’s `theme.yaml` [contains](https://github.com/bokeh/bokeh/blob/main/examples/howto/server_embed/theme.yaml)

```auto
attrs:
    Figure:
        background_fill_color: "#ffffff"
        outline_line_color: white
        toolbar_location: above
        height: 500
        width: 800
    Grid:
        grid_line_dash: [6, 4]
        grid_line_color: white

```

Per [your recommendation](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/14), in my bokeh app’s `Procfile` I have replaced `<flask-app-name>` by the name of my Flask app on Heroku (`mydemoflaskapp`) and I have replaced `<bokeh-server-name>` by the name of my Bokeh server script (`mybserverapp`)

```auto
web: bokeh serve --address="0.0.0.0" --port=$PORT mybserverapp.py --allow-websocket-origin=mydemoflaskapp.herokuapp.com

```

For this Bokeh app, my `requirements.txt` contains

```auto
bokeh

```

This app on heroku is named `mybokehserverapp`.

The directory structure for my Bokeh server app is as follows

```auto
├── mybserverapp.py
├── Procfile
├── requirements.txt
└── theme.yaml

```

The logs on my Flask app shows the following

```auto
2021-02-20T19:15:12.747347+00:00 heroku[web.1]: Starting process with command `gunicorn main:app`
2021-02-20T19:15:15.476758+00:00 app[web.1]: [2021-02-20 19:15:15 +0000] [4] [INFO] Starting gunicorn 20.0.4
2021-02-20T19:15:15.477519+00:00 app[web.1]: [2021-02-20 19:15:15 +0000] [4] [INFO] Listening at: http://0.0.0.0:12639 (4)
2021-02-20T19:15:15.477646+00:00 app[web.1]: [2021-02-20 19:15:15 +0000] [4] [INFO] Using worker: sync
2021-02-20T19:15:15.482372+00:00 app[web.1]: [2021-02-20 19:15:15 +0000] [9] [INFO] Booting worker with pid: 9
2021-02-20T19:15:15.553524+00:00 app[web.1]: [2021-02-20 19:15:15 +0000] [10] [INFO] Booting worker with pid: 10
2021-02-20T19:15:16.142507+00:00 heroku[web.1]: State changed from starting to up
2021-02-20T19:15:21.000000+00:00 app[api]: Build succeeded
2021-02-20T19:29:31.568283+00:00 heroku[router]: at=info method=GET path="/" host=mydemoflaskapp.herokuapp.com request_id=<removed> fwd="<ip-address-removed>" dyno=web.1 connect=1ms service=230ms status=500 bytes=470 protocol=https
2021-02-20T19:29:31.559310+00:00 app[web.1]: <internal-ip-address-removed-10.33.....> - - [20/Feb/2021:19:29:31 +0000] "GET /ws HTTP/1.1" 404 232 "-" "Tornado/6.1"
2021-02-20T19:29:31.566731+00:00 app[web.1]: ERROR:main:Exception on / [GET]
2021-02-20T19:29:31.566732+00:00 app[web.1]: Traceback (most recent call last):
2021-02-20T19:29:31.566732+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 2447, in wsgi_app
2021-02-20T19:29:31.566733+00:00 app[web.1]: response = self.full_dispatch_request()
2021-02-20T19:29:31.566733+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1952, in full_dispatch_request
2021-02-20T19:29:31.566733+00:00 app[web.1]: rv = self.handle_user_exception(e)
2021-02-20T19:29:31.566734+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1821, in handle_user_exception
2021-02-20T19:29:31.566734+00:00 app[web.1]: reraise(exc_type, exc_value, tb)
2021-02-20T19:29:31.566734+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
2021-02-20T19:29:31.566735+00:00 app[web.1]: raise value
2021-02-20T19:29:31.566735+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1950, in full_dispatch_request
2021-02-20T19:29:31.566735+00:00 app[web.1]: rv = self.dispatch_request()
2021-02-20T19:29:31.566736+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1936, in dispatch_request
2021-02-20T19:29:31.566737+00:00 app[web.1]: return self.view_functions[rule.endpoint](**req.view_args)
2021-02-20T19:29:31.566737+00:00 app[web.1]: File "/app/main.py", line 22, in bkapp_page
2021-02-20T19:29:31.566738+00:00 app[web.1]: with pull_session(url=PANEL_URL) as session:
2021-02-20T19:29:31.566738+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/bokeh/client/session.py", line 120, in pull_session
2021-02-20T19:29:31.566738+00:00 app[web.1]: session.pull()
2021-02-20T19:29:31.566738+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/bokeh/client/session.py", line 381, in pull
2021-02-20T19:29:31.566738+00:00 app[web.1]: self.check_connection_errors()
2021-02-20T19:29:31.566739+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/bokeh/client/session.py", line 367, in check_connection_errors
2021-02-20T19:29:31.566740+00:00 app[web.1]: raise OSError(f"Check your application path! The given Path is not valid: {self.url}")
2021-02-20T19:29:31.566743+00:00 app[web.1]: OSError: Check your application path! The given Path is not valid: wss://mydemoflaskapp.herokuapp.com/ws

```

This seems to be point to a problem with `PANEL_URL`, although I am not sure if this is the only problem.

When I try to load this app at `https://mydemoflaskapp.herokuapp.com`, I get the following Internal Server Error message

```auto
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

```

For my Bokeh app, the logs are

```auto
2021-02-20T19:11:05.371641+00:00 heroku[web.1]: Starting process with command `bokeh serve --address="0.0.0.0" --port=8750 mybserverapp.py --allow-websocket-origin=myflaskapp.herokuapp.com`
2021-02-20T19:11:11.000000+00:00 app[api]: Build succeeded
2021-02-20T19:11:12.388893+00:00 app[web.1]: 2021-02-20 19:11:12,388 Starting Bokeh server version 2.2.3 (running on Tornado 6.1)
2021-02-20T19:11:12.429012+00:00 app[web.1]: 2021-02-20 19:11:12,428 User authentication hooks NOT provided (default user enabled)
2021-02-20T19:11:12.491265+00:00 app[web.1]: 2021-02-20 19:11:12,491 Bokeh app running at: http://0.0.0.0:8750/mybserverapp
2021-02-20T19:11:12.497738+00:00 app[web.1]: 2021-02-20 19:11:12,497 Starting Bokeh server with process id: 4
2021-02-20T19:11:12.646429+00:00 heroku[web.1]: State changed from starting to up
2021-02-20T19:45:23.401735+00:00 heroku[router]: at=info method=GET path="/" host=mybokehserverapp.herokuapp.com request_id=e500b6b1-6796-41b2-9f48-81640b839e33 fwd="<ip-address-removed>" dyno=web.1 connect=1ms service=4ms status=302 bytes=189 protocol=https
2021-02-20T19:45:23.809813+00:00 heroku[router]: at=info method=GET path="/mybserverapp" host=mybokehserverapp.herokuapp.com request_id=<removed> fwd="<ip-address-removed>" dyno=web.1 connect=1ms service=373ms status=200 bytes=4089 protocol=https
2021-02-20T19:45:23.928461+00:00 heroku[router]: at=info method=GET path="/static/js/bokeh.min.js?v=<removed>" host=mybokehserverapp.herokuapp.com request_id=e5d9dc98-a17c-4f8e-af0b-5ad257ffda87 fwd="<ip-address-removed>" dyno=web.1 connect=1ms service=56ms status=200 bytes=771696 protocol=https
2021-02-20T19:45:23.943559+00:00 heroku[router]: at=info method=GET path="/static/js/bokeh-widgets.min.js?v=<removed>" host=mybokehserverapp.herokuapp.com request_id=8d98210a-8679-4f4b-8ca1-cce4645aff53 fwd="<ip-address-removed>" dyno=web.1 connect=0ms service=19ms status=200 bytes=261012 protocol=https
2021-02-20T19:45:23.949794+00:00 heroku[router]: at=info method=GET path="/static/js/bokeh-tables.min.js?v=<removed>" host=mybokehserverapp.herokuapp.com request_id=9fdcb970-b335-4872-a315-85df30044ebd fwd="<ip-address-removed>" dyno=web.1 connect=1ms service=19ms status=200 bytes=296650 protocol=https
2021-02-20T19:45:24.198892+00:00 app[web.1]: 2021-02-20 19:45:24,198 404 GET /favicon.ico (10.99.209.116) 3.73ms
2021-02-20T19:45:24.199452+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=mybokehserverapp.herokuapp.com request_id=af5a01ec-2ce3-4fb0-b86f-0c5f777f8e58 fwd="<ip-address-removed>" dyno=web.1 connect=1ms service=6ms status=404 bytes=238 protocol=https
2021-02-20T19:45:24.289261+00:00 app[web.1]: 2021-02-20 19:45:24,289 Refusing websocket connection from Origin 'https://mybokehserverapp.herokuapp.com'; use --allow-websocket-origin=mybokehserverapp.herokuapp.com or set BOKEH_ALLOW_WS_ORIGIN=mybokehserverapp.herokuapp.com to permit this; currently we allow origins {'myflaskapp.herokuapp.com:80'}
2021-02-20T19:45:24.294237+00:00 app[web.1]: 2021-02-20 19:45:24,294 403 GET /mybserverapp/ws (<internal-ip-address-removed-10.97.....>) 5.91ms

```

When I try to load this app at `https://mybokehserverapp.herokuapp.com`, I get a blank page.

**Questions**

1. Could you confirm that the following were correctly assigned, based on your recommendations in the above answer

```auto
# main.py

# <bokeh-server-name> = mybserverapp # name of Bokeh server standalone script
# <bokeh-app-name> = mybokehserverapp # name of Heroku app for Bokeh Server
# <masked-app-name> = mydemoflaskapp # name of Heroku app for Flask+Gunicorn

```

2. Per your above answer, do you detect any other problems with my implementation of your suggestion?

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [February 20, 2021, 10:04pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/16 "2021-02-20T22:04:49Z")

</div>

Hi @edesz

A few things from the error logs.

For your bokeh app you state, …

> [@edesz](#):
>
> When I try to load this app at `https://mybokehserverapp.herokuapp.com`, I get a blank page.

If this is what results in the error log, that is expected behavior given the current configuration of the app. Specifically, see the lines

> [@edesz](#):
>
> ```auto
> 2021-02-20T19:45:24.289261+00:00 app[web.1]: 2021-02-20 19:45:24,289 Refusing websocket connection from Origin 'https://mybokehserverapp.herokuapp.com'; use --allow-websocket-origin=mybokehserverapp.herokuapp.com or set BOKEH_ALLOW_WS_ORIGIN=mybokehserverapp.herokuapp.com to permit this; currently we allow origins {'myflaskapp.herokuapp.com:80'}
> 
> ```

This line is letting you know that you get a blank page because a client is attempting to access the bokeh server app directly presumably by putting its URL in a web browser’s address bar. However, the app is configured so that only connections from the user-facing Heroku app are allowed.

That is exactly the use-case in my Heroku setup. The lower-level bokeh server that handles calculations and renders graphics for a bokeh document. The only thing that is allowed to access it is the separately running flask app, which pulls it in. (In my setup this model is used for conditionally accessing the document in a broader Flask app that has user registration and login capabilities as well as loading large datasets that can get passed to the bokeh app for analysis of data of interest to a specific user.)

So, if you need to access the bokeh app directly – say for testing purposes – you can allow other origins. Then, if you want to restrict things in a final deployment revert to the settings as you currently have.

Regarding the Flask app, I’m not sure what exactly is going on there. However, the Flask app logs show issues with the URLs being used, e.g. the {self.url} path-not-valid error in this snippet.

Are you using that syntax anywhere in your flask app? Generally in my Flask apps I use its `url_for( )` syntax if I need to refer to a route in a redirect, etc.

> [@edesz](#):
>
> ```auto
> 2021-02-20T19:29:31.566740+00:00 app[web.1]: raise OSError(f"Check your application path! The given Path is not valid: {self.url}")
> 
> ```

---

<div class="post-metadata">

**Author:** ![edesz](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/e/d78d45/32.png) [@edesz](https://discourse.bokeh.org/u/edesz)\
**Post date:** [February 20, 2021, 10:25pm UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/17 "2021-02-20T22:25:41Z")

</div>

@_jm

Many thanks for that explanation about the connections - I _think_ that has cleared it up for me.

Regarding the URL error, no, I’m not using that syntax in the flask app - here is the entire flask app (same as yours actually)

```auto
# main.py

from bokeh.client import pull_session
from bokeh.embed import server_session
from flask import Flask, render_template

PANEL_URL = "https://mydemoflaskapp.herokuapp.com"
BOKEH_URL = "https://mybokehserverapp.herokuapp.com/mybserverapp"

app = Flask( __name__ )

@app.route("/", methods=["GET"])
def bkapp_page():
    with pull_session(url=PANEL_URL) as session:
        script = server_session(session_id=session.id, url=BOKEH_URL)
        return render_template("embed.html", script=script, template="Flask")

```

I’ve tried to keep this minimal setup as close to your answer as possible just to eliminate possible causes of errors.

Just re-reading your earlier answer, I think there’s three lines that may be different in my implementation compared to yours - (in the Flask app) `PANEL_URL` and `BOKEH_URL` and (in the `Procfile` for the Bokeh app) `<flask-app-name>` and `<bokeh-server-name>`. IMO, my problem lies in one (or more) of these variables because it looks like the two apps are being built successfully (from the two logs).

My feeling is that this line is pointing to the (full or partial) cause of the problem

```auto
2021-02-20T19:29:31.566738+00:00 app[web.1]: with pull_session(url=PANEL_URL) as session:

```

**Questions**

1. I think there’s something wrong with `PANEL_URL` (and maybe `BOKEH_URL` as well) - I put the name of my Heroku Flask app (`mydemoflaskapp`) in `PANEL_URL`. Is this what you used as well in [your earlier answer](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/11) when you constructed the string for `PANEL_URL`?
2. Also, in `--allow-websocket-origin=<flask-app-name>.herokuapp.com`, did you use `<flask-app-name>` or did you have to change this to something else?

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [February 21, 2021, 1:04am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/18 "2021-02-21T01:04:08Z")

</div>

Hi @edesz

Nothing stands out as incorrect in your setup.

The only thing I do differently are (i) specify versions of the modules explicitly in my requirements.txt files, (ii) include a runtime.txt file to specify a specific version of Python to use on Heroku deployments, and (iii) include a gunicorn.config file to specify options for the gunicorn layer.

I will try to setup a few free dynos next week so I can provide a minimal example similar to yours to help out. (My actual apps are fairly involved and would be inefficient to post details of to help resolve things.)

---

<div class="post-metadata">

**Author:** ![\_jm](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/_/ac91a4/32.png) [@\_jm](https://discourse.bokeh.org/u/_jm)\
**Post date:** [February 21, 2021, 3:25am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/19 "2021-02-21T03:25:00Z")

</div>

@edesz

I think the issue is in the Flask app in the section you identified as the likely problem. I suspect too this explains why the error log mentions {self.url}, although I did not independently confirm with an example.

You have the following block of code …

```
@app.route("/", methods=["GET"])
def bkapp_page():
    with pull_session(url=PANEL_URL) as session:
        script = server_session(session_id=session.id, url=BOKEH_URL)
        return render_template("embed.html", script=script, template="Flask")

```

I believe for your setup you want it to be the following, i.e. referencing BOKEH\_URL in both places.

```
@app.route("/", methods=["GET"])
def bkapp_page():
    with pull_session(url=BOKEH_URL) as session:
        script = server_session(session_id=session.id, url=BOKEH_URL)
        return render_template("embed.html", script=script, template="Flask")

```

For a bit of additional context, in my setup the backend application is a panel server (which is an extension of a bokeh server). That is why the examples you cited have references to “panel”. In that application that the Flask app pulls, I have alot of code originally written in “pure” bokeh, but at the topmost level I wrap it in panel so that I can do a few extra things with formatting that allow me to pop/add graphics elements in a list-like way, e.g. add another plot, get rid of a plot, etc. cleanly.

---

<div class="post-metadata">

**Author:** ![edesz](https://discourse.bokeh.org/letter_avatar_proxy/v4/letter/e/d78d45/32.png) [@edesz](https://discourse.bokeh.org/u/edesz)\
**Post date:** [February 21, 2021, 4:44am UTC](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199/20 "2021-02-21T04:44:02Z")

</div>

@_jm, that did it! 😀 It worked. I replaced `PANEL_URL` by `BOKEH_URL`. That was the only change needed. [https://mydemoflaskapp.herokuapp.com](https://mydemoflaskapp.herokuapp.com) is online - well, for now anyway.

**Unrelated**  
BTW, interesting note about `runtime.txt` - I thought this file was only needed if Heroku could not auto-detect the language. I know I had to use it when [deploying a sub-directory](https://medium.com/@shalandy/deploy-git-subdirectory-to-heroku-ea05e95fce1f) **and** both my `Procfile` and (importantly) my `requirements.txt` were not in the parent dir but in the sub-dir - Heroku couldn’t auto-detect the programming language because it couldn’t find a `requirements.txt` in the root directory. I got around that by placing `runtime.txt` in the root dir with the Python version. I haven’t had to use it otherwise.

Anyway, I think my question is now solved. Thanks for your help…really appreciate it.

[Next page](https://discourse.bokeh.org/t/bokeh-server-embedded-in-gunicorn-flask-hosted-to-cloud/6199.md?page=2)
