Multiapp bokeh server with docker empty response

I have a bokeh server dashboard with 15 or so endpoints that all reuse the same plotting function with different data. It is currently a flask webpage with the bokeh server embedded into it (though not married to that, could drop flask and just do bokeh maybe if flask is the root of this, but I don’t think it is). I want to put it into a docker container now, but when I do, and navigate in my browser to my endpoints, the page loads, but it doesn’t load the bokeh figures. The developer tools gives me “net::ERR_EMPTY_RESPONSE”. I am using the example from the bokeh docs as my a base, and that dynamically maps the ports to whatever is an open port, since it doesn’t know how many ports you will need, depending on how many endpoints and such. Which I think is my problem. On the outside of the container the browser is looking for, which is empty, since that is on the inside of the container.

Is there an easy fix to this? Should I just map each port manually? That won’t impact multiple users accessing the same endpoints right?

Here are some code examples. I would have made a gist, but I got a new phone and lost my 1 factor auth, and it says it will take 4-5 days for them to get me reauthorized.


    import asyncio
except ImportError:
    raise RuntimeError("This example requries Python3 / asyncio")

from threading import Thread

from flask import Flask, render_template, Blueprint
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
from bokeh.embed import server_document
from bokeh.layouts import column
from bokeh.plotting import figure
from bokeh.server.server import BaseServer
from bokeh.server.tornado import BokehTornado
from bokeh.server.util import bind_sockets

app = Flask(__name__)

def bkapp(doc):
    plot = figure()
    plot.line([1, 2, 3], [3, 2, 1])


@app.route('/', methods=['GET'])
def bkapp_page():
    name = bkapp.__name__
    port = start_bk_worker(bkapp, name)
    script = server_document(f'http://localhost:{port}/{name}')
    return render_template("embed.html", script=script)

def start_bk_worker(bokeh_app, app_name):
    sockets, port, app, = _process_bokeh_app(bokeh_app)
    print(f"sockets {sockets} port {port} app {app}")
    t = Thread(target=_bk_worker, kwargs=dict(sockets=sockets, app=app, app_name=app_name))
    t.daemon = True
    return port

def _process_bokeh_app(bokeh_app):
    app = Application(FunctionHandler(bokeh_app))
    sockets, port = bind_sockets("localhost", 0)
    return sockets, port, app

def _bk_worker(sockets, app, app_name):

    bokeh_tornado = BokehTornado(
        {f"/{app_name}": app}, extra_websocket_origins=["", "localhost:5000"])
    bokeh_http = HTTPServer(bokeh_tornado)

    server = BaseServer(IOLoop.current(), bokeh_tornado, bokeh_http)


<!doctype html>
<html lang="en">
  {{ script|safe }}



from app.main import app

if __name__ == '__main__':


FROM python:3.8-slim-buster

RUN adduser bokeh

# Install dependencies
RUN apt-get update && apt-get -y upgrade
RUN apt-get install -y gcc libpq-dev python3-dev

WORKDIR /home/bokeh

# Setup Python env
COPY requirements.txt requirements.txt
RUN python -m venv venv
RUN pip install -r requirements.txt
RUN pip install gunicorn

# Copy the application over
COPY app app
COPY *.py ./
RUN chmod +x

# Setup the framework and run it

RUN chown -R bokeh ./
USER bokeh


exec gunicorn --bind --access-logfile - --error-logfile - --log-level "INFO" --forwarded-allow-ips="*" main:app

then run on command line
docker build -t test:latest .
docker run -p 5000:5000 test:latest