✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

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

So I’ve been trying to host my apps through AWS using nginx and uWSGI. The process for doing this with Flask apps is fairly well-documented, so I figured I’d just host some Dash apps that way. I can host a single app without much problem, but I have been really struggling to host multiple apps. In particular, I’ve been trying to mount the apps at different locations with uWSGI and have Emperor manage them, but I’m really not having a huge amount of luck with it.

Before I sort of start asking for tech support for this stuff, 1) is there a reason I shouldn’t expect that approach to work, and 2) what is the recommended way of running multiple apps on the same server? I noticed the post about multiple dashboards, and also this page about multi-page apps. Could anyone give me a quick run-down about what the difference is here and why I should use one over the other?

I believe that the approach in chriddyp’s post you link to is essentially the same as the Multi Page app approach described in the Dash docs. They both use Dash’s support for single page app by associating a callback to the current URL, which injects the appropriate layout for the URL that has been navigated to. chriddy’s post showed a way of extending this idea to have the URLs associated with distinct modules. This is really just a way of organising the one app into different modules. While they look like different apps, they all share the same single Dash instance (and therefore Flask server).

The difference between a multi-page app such as that, and going the emperor mode option is that in the former you have a single app running on a single Python instance (ignoring multiple workers etc for now) and in the latter, each is an entirely separate Dash app running on a separate process. Which is better probably just depends on your needs. The emperor mode option will make it easier to keep things fully modularised, with your separate apps being able to be maintained and deployed independently of the emperor mode scenario. But maybe you actually want everything to run in the same Flask server/on the same process, in which case the multi-page app might be the way to go (if performance is a concern you can also use multiple workers (optionally threaded) for whichever wsgi server you’re using).

I’d imagine that if you thought about it a bit, you could also enhance chriddyp’s approach to either use a parent Dash instance or spin up a new Dash instance depending on some deployment configuration, giving the multi-page approach the flexibility of deploying the modules independently. Actually, in which case, you could probably use the same setup to run those modules/apps on emperor mode.

1 Like

That’s great, cheers. Under what circumstances would you want everything to run on the same process? You lose redundancy - if the Flask server goes down, all of your apps go down. So what, if anything, do you gain?

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.