Plotly Dash on Ubuntu 24.04 on Azure User Guide
Overview
Plotly Dash is the popular open source Python framework for building interactive, data-driven web applications and analytical dashboards without writing any JavaScript. You describe a dashboard's layout and its interactive behaviour entirely in Python — Plotly charts, controls such as dropdowns, sliders and date pickers, and callbacks that recompute the charts and tables whenever a user changes an input — and Dash serves it as a browsable analytics application that updates live in the browser.
The cloudimg image ships Dash as a finished, ready-to-run dashboard server, not a bare framework. It installs Dash 4.4.0 with Plotly 6.9.0 and pandas 2.2.3 in a Python virtual environment at /opt/plotly-dash/venv on Python 3.12, run by an unprivileged plotlydash system account. A substantive sample dashboard — the Global Development Explorer — is bundled and live on the first boot of every VM: a Plotly bubble chart, a ranked bar chart and a data table driven by a metric dropdown, a region dropdown and a year slider over the Gapminder development-indicators dataset. It is served in production by gunicorn (three workers, the WSGI app.server) on the loopback address 127.0.0.1:5000 — not the Flask development server — and fronted by nginx on TCP 80 with HTTP Basic Authentication. A unique admin password is generated on the first boot of every VM. Backed by 24/7 cloudimg support.
The dashboard listens only on loopback and is never exposed directly. nginx fronts it on port 80; the unauthenticated /health probe stays open for load balancers, and everything else requires the admin password.
What is included:
- Dash 4.4.0 + Plotly 6.9.0 + pandas 2.2.3 in a Python 3.12 venv at
/opt/plotly-dash, run by an unprivilegedplotlydashaccount - A bundled, genuinely interactive sample dashboard at
/opt/plotly-dash/app/dashboard.py(the Global Development Explorer) - gunicorn serving the Dash WSGI app in production on loopback
:5000, managed byplotly-dash.service - nginx reverse proxy on
:80with HTTP Basic Auth in front of the dashboard; open/healthprobe - Per-VM admin password generated at first boot, in a root-only file
plotly-dash.service+nginx.serviceas systemd units, enabled and active- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point; scale up for heavier dashboards or more concurrent users. NSG inbound: allow 22/tcp from your management network and 80/tcp from the browsers that reach the dashboard (front port 80 with TLS for public exposure — see Enabling HTTPS).
Step 1 — Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Plotly Dash by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create → Create.
Step 2 — Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name plotly-dash \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name plotly-dash --port 80 --priority 1010
Step 3 — Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 — Confirm the services are running
systemctl is-active plotly-dash.service nginx.service
curl -s http://127.0.0.1/health
Both services report active and the /health endpoint returns ok. The dashboard is served by gunicorn on loopback 127.0.0.1:5000 and reached only through nginx on port 80.

Step 5 — Retrieve your admin password
The password is generated uniquely on the first boot of your VM and written to a root-only file:
sudo cat /root/plotly-dash-credentials.txt
The DASH_ADMIN_PASSWORD value is the password; sign in as admin with it. Without credentials the dashboard returns 401; with them it returns 200.

Step 6 — Open the dashboard
Browse to http://<vm-public-ip>/ and sign in as admin with the password from Step 5. The bundled Global Development Explorer loads immediately: a Plotly bubble chart of life expectancy versus GDP per capita, with summary tiles above it.

Step 7 — Explore interactively
The dashboard is genuinely interactive — every control fires a Dash callback that recomputes the charts server-side and updates them live. Change the Region dropdown to a single continent and the bubble chart filters to that region and its title updates:

Drag the Year slider back to 1952 and the same chart redraws with the historical data — life expectancy and GDP per capita were far lower:

Step 8 — The ranked view
Scroll down for the ranked bar chart and the top-10 data table, both driven by the same Metric, Region and Year controls:

Step 9 — Confirm the runtime and interactivity
Confirm the pinned versions and that the app is served by gunicorn in production:
for p in dash plotly pandas gunicorn; do printf '%-10s %s\n' "$p" "$(/opt/plotly-dash/venv/bin/pip show $p | awk '/^Version:/{print $2}')"; done
This prints dash 4.4.0, plotly 6.9.0, pandas 2.2.3 and gunicorn 23.0.0. You can also exercise the interactive callback endpoint directly with the admin credentials — two different inputs return two different figures, which is the proof the dashboard reacts to input:
PASS=$(sudo grep '^DASH_ADMIN_PASSWORD=' /root/plotly-dash-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'authenticated dashboard: HTTP %{http_code}\n' -u "admin:$PASS" http://127.0.0.1/
curl -s -u "admin:$PASS" http://127.0.0.1/_dash-layout | head -c 80; echo

Ship your own dashboard
The image is a production Dash host — replace the bundled sample with your own application. Edit /opt/plotly-dash/app/dashboard.py (or drop in your own module and point gunicorn at it), keeping a module-level server = app.server so gunicorn can serve it, then restart:
sudo -u plotlydash /opt/plotly-dash/venv/bin/pip install <your-extra-packages>
sudo systemctl restart plotly-dash.service
The systemd unit runs gunicorn --workers 3 --bind 127.0.0.1:5000 dashboard:server from /opt/plotly-dash/app; adjust the worker count or module name in /etc/systemd/system/plotly-dash.service to match your app, then sudo systemctl daemon-reload && sudo systemctl restart plotly-dash.service. Pass environment variables to your app via /etc/plotly-dash/plotly-dash.env.

Enabling HTTPS
For production, terminate TLS at nginx with a real domain pointed at the VM's public IP. Install certbot and request a certificate (replace the domain):
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
certbot edits the nginx site at /etc/nginx/sites-available/cloudimg-plotly-dash to add the TLS listener and arranges automatic renewal.
Backup and maintenance
The admin password lives in the nginx htpasswd file /etc/nginx/.plotly-dash.htpasswd; rotate it with sudo htpasswd /etc/nginx/.plotly-dash.htpasswd admin. Your dashboard code and any data live under /opt/plotly-dash/app; back that up if you customise it. Keep the OS patched with sudo apt update && sudo apt upgrade (unattended security upgrades are enabled by default). The server restarts cleanly with sudo systemctl restart plotly-dash.service; logs: sudo journalctl -u plotly-dash.service.
Support
This image is backed by 24/7 cloudimg support. Contact us by email and chat for help with building and serving Dash dashboards, production tuning, authentication, TLS and maintenance.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.