Deploy dash web app on caddy web server
Every new dashboard or web app requires a means to make it available. Deploying and sharing the app as it is being developed allows us to actively disseminate it to the non developer members of the team enabling them to actively participate in the project. First follow the instructions to set up a caddy web server. Here I will show how to deploy a containerised dash web app onto an ubuntu server.
Summary
- Define environment variables for the
debug,portandhostsettings used forapp.run_server()in dashecho "APP_HOST=0.0.0.0\nAPP_PORT=8090\nAPP_DEBUG=False" > .env - Disable website setting and instead setup reverse proxy for port used by dash app
perl -pi -e "s|root * /var/www/html|# root * /var/www/html" /etc/caddy/Caddyfile perl -pi -e "s|# reverse_proxy localhost:80|reverse_proxy localhost:8090" /etc/caddy/Caddyfile systemctl reload caddy - In app: fetch and use configuration settings in your dash project
constants.py:APP_HOST = os.environ.get("APP_HOST") APP_PORT = int(os.environ.get("APP_PORT")) APP_DEBUG = bool(os.environ.get("APP_DEBUG"))app.py:server = app.server if __name__ == '__main__': app.run_server(port=APP_PORT, debug=APP_DEBUG, host=APP_HOST) - Create a
Dockerfileanddocker-compose.yml.Dockerfile:FROM python:3.11 ENV DASH_DEBUG_MODE $APP_DEBUG WORKDIR /app COPY . . RUN apt-get update && \ apt-get --yes install build-essential python3-dev libmemcached-dev libldap2-dev libsasl2-dev libzbar-dev ldap-utils tox lcov valgrind && \ apt-get clean RUN set -ex && \ pip install -e . EXPOSE $APP_PORT CMD gunicorn -w 4 -b $APP_HOST:$APP_PORT app:server --preloaddocker-compose.yml:version: "3" services: dash: container_name: dash build: dockerfile: Dockerfile args: - APP_DEBUG=${APP_DEBUG} - APP_HOST=${APP_HOST} - APP_PORT=${APP_PORT} restart: unless-stopped # Do not keep starting if stopped by user environment: - APP_DEBUG - APP_PORT - APP_HOST - TZ=Australia/Canberra ports: - ${APP_PORT}:${APP_PORT} expose: - ${APP_PORT} networks: - default volumes: # allows host to get files downloaded in container - "./analytics:/app/analytics:rw" - Sync local repo to remote
rsync -avz --stats --exclude-from='exclude-file.txt' . ade@$(SERVER):~/$(APP)/. - Build on remote
ssh [user]@[remote] cd [project] sudo docker compose up --build --detach dash - Optionally setup a Makefile for streamlined syncing and container management
Procedure
Specify host and port
The dash app uses environment settings in .env which set the host and port used for the app, and turn debug mode off.
$ cat .env
# Production settings
APP_HOST=0.0.0.0
APP_PORT=8090
APP_DEBUG=False
These differ to the development settings of:
# Development settings
APP_HOST=127.0.0.1
APP_PORT=8091
APP_DEBUG=True
Set up reverse proxy
The caddy server setup reverse proxy from the chosen domain name to the app port and reload the caddy server
perl -pi -e "s|root * /var/www/html|# root * /var/www/html" /etc/caddy/Caddyfile
perl -pi -e "s|# reverse_proxy localhost:80|reverse_proxy localhost:8090" /etc/caddy/Caddyfile
systemctl reload caddy
Afterwards /etc/caddy/Caddyfile should look like
[HOST_NAME] {
# Set this path to your site's directory.
# root * /var/www/html
# Enable the static file server.
file_server
# Another common task is to set up a reverse proxy:
reverse_proxy localhost:8090
# Or serve a PHP site through php-fpm:
# php_fastcgi localhost:9000
}
In app: fetch configuration settings
In your constants.py within your app, fetch the configuration settings
APP_HOST = os.environ.get("APP_HOST")
APP_PORT = int(os.environ.get("APP_PORT"))
APP_DEBUG = bool(os.environ.get("APP_DEBUG"))
In app: setup app.py
Gunicorn requires server parameter, app.py should look like:
# Local machine docker: Use debug=True
# Remote machine docker: Use debug=False, host=0.0.0.0
# Both: set server variable
server = app.server
if __name__ == '__main__':
app.run_server(port=APP_PORT,
debug=APP_DEBUG,
host=APP_HOST)
Test start of the app
Test start of the app in development mode on server
cd [project]
python3 -m pip install -e .
python3 ./app.py
Press Ctrl-C to exit app in dev mode
Containerise with Docker
Create a Dockerfile with python which copies the project to /app, installs the package, runs the app on localhost:8090 and exposes that port. Some of these ubuntu packages are only required if you setup your app to support ldap login.
FROM python:3.11
ENV DASH_DEBUG_MODE $APP_DEBUG
WORKDIR /app
COPY . .
# Required for python-ldap
RUN apt-get update && \
apt-get --yes install build-essential python3-dev libmemcached-dev libldap2-dev libsasl2-dev libzbar-dev ldap-utils tox lcov valgrind && \
apt-get clean
RUN set -ex && \
pip install -e .
EXPOSE $APP_PORT
CMD gunicorn -w 4 -b $APP_HOST:$APP_PORT app:server --preload
Create a docker-compose.yml for easy deployment as below. This sets the timezone within the container and retrieves the application settings $APP_DEBUG, $APP_HOST and $APP_PORT from .env and passes these as environment variables into the application container.
# Requires .env file
version: "3"
services:
dash:
container_name: dash
build:
dockerfile: Dockerfile
args:
- APP_DEBUG=${APP_DEBUG}
- APP_HOST=${APP_HOST}
- APP_PORT=${APP_PORT}
restart: unless-stopped # Do not keep starting if stopped by user
environment:
- APP_DEBUG
- APP_PORT
- APP_HOST
- TZ=Australia/Canberra
ports:
- ${APP_PORT}:${APP_PORT}
expose:
- ${APP_PORT}
networks:
- default
volumes:
# allows host to get files downloaded in container
- "./analytics:/app/analytics:rw"
Streamline container rebuild with Makefile
Using a Makefile we can simplify the commands by adding these rules to your Makefile.
app: denv
python ./app.py $(SKIP_LOGIN)
sync:
rsync -avz --stats --exclude-from='exclude-file.txt' . ade@$(SERVER):~/$(APP)/.
penv:
cp -p environment/.env_prod .env
denv:
cp -p environment/.env_dev .env
up:
sudo docker compose up --build --detach dash
logs:
sudo docker compose logs -f dash
stop:
sudo docker compose stop dash
run:
sudo docker compose up --detach dash
restart: stop up
ps:
sudo docker compose ps
open:
sudo docker exec -it dash bash
rmdash:
sudo docker container rm dash
rmprune:
sudo docker system prune
This will simplify deployment and container restart to two commands make sync... and make restart
On local machine in project
$ cd [project]
$ make sync server=[server_name]
On server
$ cd [project]
$ make restart
Troubleshooting
Docker uses considerable space which can fill over time, especially with repeat builds. This excess docker artifacts can be deleted with
sudo docker system prune
shortened to
make rmprune
However docker space usage does continue to increases over time. The docker storage location is found with
sudo docker info | grep 'Docker Root Dir'
and by default is /var/lib/docker. You can manage docker space usage by moving docker to a custom docker storage location with a larger disk space, or removing and reinstalling docker.