Run Dash+Poetry+Gunicorn as your own Docker container on Heroku
Tags: dash, python, docker, beginners
Published: 2022-06-15 21:54:25

I am totally a noob, and this should be seen only as a working example, and never be used for something serious. Feel free to suggest some improvements and comments! 

## Dash
The open source [Dash](https://dash.plotly.com/) platform is my main reason for my desire  to learn to program. I am starting with simple data analytics and visualization, and hoping to get into AI and more complex matters later. 

## Poetry
Python comes in different versions, and all my packages do as well. [Poetry](https://python-poetry.org/) makes life easier for me when learning to program. It both handles dependencies for my project, as well as providing me with a virtual environment with all my packages present! 

This is a example configuration in my `pyproject.toml`. 

```toml
[tool.poetry]
name = "dashes"
version = "0.1.0"
description = "Data analytics"
license = "GPL3"

[tool.poetry.dependencies]
python = "^3.10"
dash = "^2.4.1"
pandas = "^1.4.2"
numpy = "^1.22.4"
pyjstat = "^2.2.1"
dash-bootstrap-components = "^1.1.0"
dash-labs = "^1.0.8"
gunicorn = "^20.1.0"
whitenoise = "^6.2.0"

[tool.poetry.dev-dependencies]
flake8 = "^4.0.1"
isort = "^5.10.1"
autopep8 = "^1.6.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
```


## Docker
What [Docker](https://www.docker.com/) does, is a bit above my imagination. Still: it makes it possible to run pretty much software with a few Google searches. 

There seems to be a trend towards slim Docker images for your containers, so I tried a multi stage build. First we are exporting the dependencies to a `requirements.txt`, and then in a new container we install dependencis and run the Gunicorn web server. 

This is my `Dockerfile`: 

```docker
################
# Stage: Build #
################

FROM python:3.10-slim AS build

ENV POETRY_VERSION=1.1.13

WORKDIR /app

# Export poetry dependencies to file
RUN pip install "poetry==$POETRY_VERSION"
COPY poetry.lock pyproject.toml ./
RUN python -m venv /app/venv
RUN poetry export --without-hashes --format requirements.txt --output /app/requirements.txt

#####################
# Stage: Production #
#####################
FROM python:3.10-slim AS prod

ENV PYTHONPATH=/app

WORKDIR /app

# Copy requirements from build stage, and install them
COPY --from=build /app/requirements.txt . 
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# Create a non-root user to run the web server
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser

# Run server
EXPOSE ${PORT:-8000}
CMD gunicorn --bind 0.0.0.0:${PORT:-8000} index:server

```


## Heroku
Heroku is a super solid cloud platform suitable for running your enterprise apps. However, they do also provide a free tier. It takes some time to warm up for some request, howeve super cool to test your apps with. 

You need to tell Heroku that you have your own image to run, just download their CLI first. Use the Heroku CLI, and make sure to set your run stack to container: `heroku stack:set container`.

Heroku also loves to decide which port the app should be available at, so make sure to get their PORT from environment variables. See above for how I managed to read the port when present, and default to port 8000 if not provided – `${PORT:-8000}`.  

Needed to add a file to my repo, `heroku.yml`, as well


```yml
build:
  docker:
    web: Dockerfile
run:
  web: gunicorn index:server --bind 0.0.0.0:$PORT
```

And then: finally my little data analytics dashboard is running on Heroku! Again - feel free to suggest improvements in the comments section.