🚀 Gen 5 of the leading AI app deployment platform launches October 6. Click for the livestream.

Hosting multiple Dash apps (uWSGI Emperor vs multi-page apps vs multi-app projects)

Perhaps others can chime in, as I only have limited experience, but I would imagine that you could achieve similar redundancy running the one app with a single Flask server. Your wsgi server should still be able to restart failed worker processes serving that single app.

Emperor mode, in addition the redundancy you would expect, allows to to monitor various sources for app configurations, eg by adding or removing config files, it will automatically spin up the corresponding app. If you don’t need these features, then using a single app that you make modular will make your deployment simpler. There are also many tutorials for hosting a Flask app with a wsgi server, but fewer for using emperor mode.

Yet another approach that I came across on these forums is to use Werkzeug’s DispatcherMiddlleware to mount additional Flask instances alongside an existing one.

https://stackoverflow.com/a/45849050/77533

This would mean that you could run the one single wsgi server, but would allow for a little more modularity by not having to import the same Dash instance across all your app modules.

Been trying the above but was getting url not available. could you explain a bit more

In that example, it’s assuming the contents is in a wsgi.py file, as WSGI servers default to looking for an ‘application’ attribute of the module within such files.

Let’s say you had something like this in app.py:

from dash import Dash 
from werkzeug.wsgi import DispatcherMiddleware

dash_app1 = Dash(__name__)
dash_app2 = Dash(__name__)
flask_app = Flask(__name__)

app = DispatcherMiddleware(flask_app, {
    '/dash1': dash_app1.server,
    '/dash2': dash_app2.server,
})

This should be deployable with gunicorn (and then accessible on http://HOSTNAME:5000) like so:

$ gunicorn --bind '0.0.0.0:5000' app:app

(I haven’t actually tried this, but I’m fairly sure this should work)

1 Like

Let me try it later then will give feedback

am failing to deploy it, not sure what am missing. I am missing. Its a single app deployment and this is how I had thought of doing it
import dash
import dash_core_components as dcc
import dash_html_components as html
from flask import Flask

application= Flask(__name__)
app=dash.Dash(__name__)
app.config.supress_callback_exceptions=True
print(dcc.__version__)

@app.route("/")
app.layout=html.Div([
                html.H3('Select location'),
               dcc.Dropdown(
                      id='my-dropdown',
                      options=[{'label': i, 'value': i} for i in zse_stock_list],
                      value=[''],
                      multi=True
                )
            dcc.Graph(id='my-graph')
 ])
@app.callback(Output('my-graph', 'figure'),[Input('my-dropdown', 'value')])
def populate(selected):
       for x in selected:
           dat_x=pd.read_csv("http://www.anything.com/"+x+"=csv")
       return {
               data:[{
                   'x': dat_x.index,
                  'y': dat_x.Population]}
if __name__ == '__main__':
    app.run_server()

Is my hosting code correct or I need to make adjustments to the top part of the app and in what way to host it locally

Tried editing the deployment till getting no errors on the server log files but retained the error page URL not found which is the same as in URL Not Found - Multi-Page Dash App. Then modified the wsgi file and the app to be the same as Deploy Dash on apache server [solved!] following all the steps but failed to get the errors they were getting but ended with the page saying url not found. Am trying to deploy the dash app on a Linux apache webserver. The hello world script for flask and other examples flask applications are running and producing html pages when url is entered. These are the changges made to the dash app.
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
from flask import Flask
application = Flask(__name__)

app = dash.Dash()
#application = app
app.config.update({
     #as the proxy server will remove the prefix
    'routes_pathname_prefix': '/',

     #the front-end will prefix this string to the requests
     #that are made to the proxy server
    'requests_pathname_prefix': '/meatpie/'
})

Without the line application = Flask(__name__)the server returns an error saying target WSGI script '/var/www/html/meatpies/meatpie.py' does not contain WSGI application ‘application’. By inserting the line application = Flask(__name__) we return URL not found.

If we set application =app' whereapp=dash.Dash()’ we get TypeError:'Dash' object is not callable. Can someone assist

You need to set application to the Flask instance that Dash is using. Like this:

app = Dash(__name__)
application = app.server

When you do application = Flask(name), you’re just pointing Apache at an entirely separate Flask instance that is not associated with the Dash app. If you need to create your own Flask instance rather than have Dash create its own, then you can do this:

server = Flask(__name__)
app = Dash(__name__, server=server)
application = app.server

Thank you nedned

Added the line application = app.server and indeed the requested URL not found error disappeared.
Now it attempts to load the dash layout then stops with error message
Error loading layout

What could be the issue now?

I see that you have disabled callback validation. Unless you actually need this (eg because you are registering a callback to an element that is not part of the initial layout), then you might want to remove that line, as it will help find potential errors in your Dash app.

Also you’ll want to remove this line:

@app.route("/")

app is a Dash instance, not a Flask instance, so this doesn’t make any sense, and Dash creates the needed routes for you anyway.

2 Likes

Thanks @nedned. Am now interested in what causes the Error loading layout. Changed the code replacing the lines as described by nedned but now am also getting the same error as @FoT. Whats the platform you are using @FoT. Whats the cause of the error. Is it the render or something else tried to run a ping and discovered that the page loaded 1.2mb yet returned that Error loading layout. Would it be that I somehow missed the wsgi setup

As per the error message, it sounds like there’s some kind of issue in your layout tree. I would try removing subtrees from the layout until you get a layout that works. That should hopefully help you locate the part of the layout with the issue.

I ran the below script as a test and got the same error
import dash
import dash_html_components as html
import inspect

app = dash.Dash()
application=app.server
print('App start')


def serve_layout():
    print('Calling layout')
    return html.Div('hlo world')


app.layout = serve_layout

if __name__ == '__main__':
    app.run_server(debug=False)

given by @chriddyp in a github post but retained the error Error loading layout.

How are you running the app?

It works fine for me when I run it both using the Flask development server. ie

$ python app.py

And also using gunicorn:

$ gunicorn app:app.server

(assuming the snippet is saved in a file app.py)

not running through gunicorn but mod_wsgi for apache, so calling directly from web browser. Does gunicorn work with Apache? If so, how do I go about it? I tried gunicorn with nginx once in the early stages before I stuck with Apache because that is the webserver running other non-python (Dash) apps. Plus when I was doing my reading was biased to mod_wsgi because of http://flask.pocoo.org/docs/0.12/deploying/mod_wsgi/ . Not knowing whether thats the best for Dash though

You can definitely access the app from a browser when running using the Flask dev server and also gunicorn. You just have to provide the port number in the browser. Gunicorn defaults to port 8000, so you would enter http://localhost:8000 in your browser. If you want to make the app available publicly not just on your local machine, then you would want to do this:

$ gunicorn --bind 0.0.0.0:8000 app:app.server

mod_wsgi on Apachi is a good option too, however it can annoying to configure properly to run with your existing Apache service – as I suspect you may have discovered. Fortunately there is the very handy mod_wsgi-express which takes care of everything for you and largely just works.

$ pip install mod_wsgi-express
$ mod_wsgi-express start-server app.py

You can also use it to generate the configuration that you would provide to Apache to hookup mod_wsgi yourself, which you’ll have tolook up the documentation for.

1 Like

Thanks @nedned. It ran simultaneous. Fos single app hosting it worked. Now trying to find out whether the use of a single port to host multiple apps. Having struggled with mod_wsgi, would recommend gunicorn.

In summary this is what i came across, the only modification one needs to make to the dash script is add the line application =app.server below the line app=dash.Dash(). Then run the application on a server with gunicorn configurations. I think we should create a tab called Hosting a Dash app for dummies outlining stage by stage hosting of a Dash app. From configuring Apache and Nginx then mod_wsgi or gunicorn. It would make deployment easier and a one stop place for first timers. This was my first time hosting a python app.

When using Nginx define your location value to load Dash app in nginx.conf file. Then in your Dash app, pass the location url as argument in the app=dash.Dash() section e.g app=dash.Dash(url_base_pathname='/myDashApp') to serve requests on yourdomain.com/myDashApp

Excuse me for reanimating this zombie from 11 months ago.
I feel a need to expand the conversation to meet new challenges.

Currently, I have two separate EC2 T2-Micro-instances each running 4 docker containers, Nginx and three Gunicorn/Flask/Dash single app containers.

I build this as a multi-app container set and it uses almost all the 8gb of memory with the free-tier T2 instance. I have built two of these with 3 apps each.

They are both on https/443 (using letsencrypt) and http/80. Working fine.
The graphs are interactive and can be embedded in a WordPress blog easily.

Now, I see that it would make sense if there were more than three apps per server and I am sure that just adding the small .py Dash-powered apps would not smoke the rest of memory.

Adding additional containers, each with the whole stack, Dash, plot.ly, python, pandas, gunicorn etc. would eat memory.

So it makes much more sense just to try and expand the number of apps rather than the number of containers or servers to scale up in dash apps.

The multi-page app approach could be the answer if each page were separately addressable via a unique port # as a part of a sub-domain. I use sub-domains with an IGW (Internet GateWay) and route53 on AWS to allow me to use just a single domain - tsworker.com to route publicly to all the sub-domain instances. A simple modifier (’‘A’ record) for each subdomain allows something like: 'charts.tsworker.com to reach the other instances.

If it were possible to link to individual pages in the multi-page app approach, that would optimize instance consumption and storage which is where the charges get you eventually. I could merrily create hundreds of dashboard apps using 10’s of instances (or less maybe?) and treat all of them as uniquely addressable endpoints.

What do you think?

See some of the dash apps live on the blog below.
www.thoughtsociety.org