Plotly 5.19.0 - Dash 2.16.0 - python 3.10.11 - debian 12
I am having a nightmare getting to run this in production. I want the app to respond to the mematest.mydomain.it address and nginx should route it to the localhost:4200 port where the app is listening.
The current config you see below is more or less running the app under the werkzeug dev server but my attempts at running under gunicorn have all failed. Can you spot what I’m doing wrong? Thanks a lot in advance.
nginx with the following configuration:
server {
server_name mematest.mydomain.it;
client_max_body_size 25m;
access_log /var/log/nginx/memaapp.access.log;
error_log /var/log/nginx/memaapp.error.log;
location / {
proxy_pass http://127.0.0.1:4200; # Ensure this points to your Dash app
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Prefix /;
#proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
#proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/mematest.mydomain.it/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/mematest.mydomain.it/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
server {
listen 80;
server_name mematest.mydomain.it;
return 301 https://$host$request_uri; # Direct HTTPS redirection without using 'if'
}
server {
listen 80 default_server;
server_name _;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://127.0.0.1:4100;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
while the Dockerfile is as follows:
# ---- Base Layer ----
FROM python:3.10.13-slim-bullseye as base
# Environment settings and maintainer label
LABEL maintainer="bob@mydomain.com"
ENV TZ=Europe/Rome \
POETRY_VERSION=1.8.0 \
POETRY_HOME="/usr/local/bin"
# Set timezone and package preferences
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \
echo 'APT::Install-Suggests "0";' > /etc/apt/apt.conf.d/00-docker && \
echo 'APT::Install-Recommends "0";' > /etc/apt/apt.conf.d/00-docker
# Install necessary Debian packages
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
apt-get install -y nvi net-tools tzdata curl git wget tar iputils-ping gunicorn && \
rm -rf /var/lib/apt/lists/*
RUN pip install poetry==1.8.0
ENV POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=1 \
POETRY_VIRTUALENVS_CREATE=1 \
POETRY_CACHE_DIR=/tmp/poetry_cache
WORKDIR /app
COPY pyproject.toml poetry.lock README.md ./
RUN poetry install --no-root --no-interaction --no-ansi --no-dev -vvv
# ---- Application Layer ----
FROM base as application
COPY config /app/config
COPY resources /app/resources
COPY src /app/src
RUN poetry install --only-root --no-interaction --no-ansi --no-dev -vvv
EXPOSE 4200
WORKDIR /app
#CMD ["poetry", "run", "gunicorn", "-w", "4", "-b", "0.0.0.0:4200", "src.app.mema_app:server"]
CMD poetry run python src/app/mema_app.py
which does launch the app with Flask’s default werkzeug server.
The code itself is as follows:
server = Flask(__name__)
server.secret_key = secrets.token_urlsafe(16)
server.wsgi_app = ProxyFix(server.wsgi_app, x_for=1, x_host=1)
app = dash.Dash(__name__,
server=server,
external_stylesheets=theme["stylesheets"],
url_base_pathname='/'
)
....
if __name__ == '__main__':
app.run(debug=False, host="0.0.0.0", port=4200)