Multi-page app "pages" folder undiscoverable by gunicorn on Linux?

I am working to deploy a Flask application (containing a multi-page Dash application) using nginx and gunicorn using Dash 2.5.1. I’ve run into a wall where the app works as expected when running on the Flask development server, but not when running gunicorn on an Amazon Linux server; I get the following error:

A folder called {pages_folder} does not exist.
If a folder for pages is not required in your application, set `pages_folder=""`
For example, `app = Dash(__name__,  pages_folder="")`

where {page_folder} is the path supplied to the dash constructor:

dash_app = Dash(
    server=False,
    use_pages=True,
    routes_pathname_prefix='/',
    pages_folder="app/dash_app/pages",
    title="TITLE",
)

I’ve tried many different values for the pages_folder param, from absolute paths, to relative paths, yet none seem to work.

Here is a simplified example of my project stucture:

image

The application is created by calling a function create_app() in the application.py file, which creates a Flask instance, links the Flask instance as the Dash server, and returns the Flask instance (which is used by gunicorn).

When configuring gunicorn, I’m running synchronous workers, have tried preloading the app, and I’ve messed around with all kinds of different values for chdir and pythonpath, yet nothing seems to work, and Dash continues to throw the same error, defined by this function in the Dash _configs.py file:

def pages_folder_config(name, pages_folder, use_pages):
    if not use_pages:
        return None

    pages_folder = pages_folder.lstrip("\\").lstrip("/")
    pages_folder = None if pages_folder == "" else pages_folder
    pages_folder_path = None
    error_msg = f"""
    A folder called {pages_folder} does not exist.
    If a folder for pages is not required in your application, set `pages_folder=""`
    For example, `app = Dash(__name__,  pages_folder="")`
    """

    if pages_folder:
        pages_folder_path = os.path.join(
            flask.helpers.get_root_path(name), pages_folder
        )
    if pages_folder and not os.path.isdir(pages_folder_path):
        raise Exception(error_msg)
    return pages_folder_path

I’ve debugged the path for the working directory using pathlib, directly before the Dash declaration, and it lines up exactly with what I’d expect, yet running through gunicorn still doesn’t work. I suspect the issue is more related to how gunicorn forks workers, but I’d appreciate any guidance.

Hi @3d65! I’m not entirely sure the right answer - attaching Dash to an already existing Flask app is always a little more finicky than having Dash create the Flask app.

One missing piece though is the name parameter to the Dash constructor (where we normally pass in __name__) - since you didn’t provide that, it’ll fall back on "__main__" which I can certainly see taking different values for the dev server and for gunicorn. Even though that parameter isn’t needed for creating the Flask app, we still use it as the name in flask.helpers.get_root_path(name).

So what I’d try is: pass in __name__ there in your Dash constructor, figure out the pages_folder value you need at that point to get it working with the dev server, and with any luck that will work correctly via gunicorn as well.

3 Likes

Great eye! Marked as the solution, thanks!

For documentation sake, this is the constructor, given the project structure I showed above, I needed to use.

dash_app = Dash(
    name=__name__,
    server=False,
    use_pages=True,
    routes_pathname_prefix='/',
    pages_folder="pages",
    title="TITLE"
)

This constructor is defined in the __init__.py file in the dash_app subpackage.

1 Like