Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to run collectstatic when deploying django app to heroku using docker?

I am deploying a Django app to Heroku using Docker. When I put RUN manage.py collectstatic --noinput in the Dockerfile, it fails because there is no value set for the environment variable DJANGO_SECRET_KEY. My understanding is that this is because config vars aren't available during build time.

When I run collectstatic as a release command, it works without error, and successfully copies the static files. However, when I hit the app url, it returns a 500 error because the static files can't be found. I believe this is because the release command is run as a dyno on an ephemeral filesystem, and the copied files are therefore not found.

It seems to be a catch-22. Putting collectstatic in the Dockerfile fails because there are no config variables available, but putting it as a release command fails because only file changes from the build phase are saved?

What to do?

Here are my collectstatic settings in settings.py


MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...
]
...
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
STATICFILES_STORAGE = 'backend.storage.WhiteNoiseStaticFilesStorage'

Dockerfile

# Pull base image
FROM python:3.7-slim

# Set environment varibles
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set work directory
RUN mkdir /code
WORKDIR /code

# Install dependencies
RUN pip install pipenv
COPY Pipfile Pipfile.lock /code/
RUN pipenv install --system

# Copy project
COPY . /code/

## collect static files
RUN mkdir backend/staticfiles

# This fails because DJANGO_SECRET_KEY can't be empty
RUN python manage.py --noinput

heroku.yml

build:
  docker:
    web: Dockerfile
run:
  web: gunicorn backend.config.wsgi:application --bind 0.0.0.0:$PORT
like image 858
Ryan Knight Avatar asked Jan 13 '20 15:01

Ryan Knight


People also ask

Can we deploy Docker container in Heroku?

Heroku Container Registry allows you to deploy your Docker images to Heroku. Both Common Runtime and Private Spaces are supported. If you would like Heroku to build your Docker images, as well as take advantage of Review Apps, check out building Docker images with heroku. yml.

Can we deploy Django project in Heroku?

Hopefully, by the end of this guide, you'll be able to host your Django project on Heroku, which is a great cloud service, especially because Heroku is a cloud PaaS (Platform as a Service) and also offers a free plan with which you can deploy your project with a full database provision.


2 Answers

After confirming with Heroku support, this does indeed appear to be a bit of a catch-22.

The solution was to put collectstatic in the Dockerfile so that it runs during build time and the files persist.

We got around not having a secret key config var by setting a default secret key using the get_random_secret_key function from Django.

The run phase uses the secret key from the Heroku config vars, so we aren't actually changing the secret key every time -- the default only applies to the build process. collectstatic doesn't index on the secret key, so this is fine.

In settings.py

from django.core.management.utils import get_random_secret_key ... SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', default=get_random_secret_key())  
like image 170
Ryan Knight Avatar answered Sep 20 '22 19:09

Ryan Knight


I don't use heroku so can't test, but you should be able to run collect static before you run the app;

Dockerfile

# Pull base image FROM python:3.7-slim  # Set environment varibles ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1  # Set work directory WORKDIR /code/  # Install dependencies RUN pip install pipenv COPY Pipfile Pipfile.lock . RUN pipenv install --system  # Copy project COPY . .  # Collect static files RUN python manage.py collectstatic --noinput  # run gunicorn CMD gunicorn hello_django.wsgi:application --bind 0.0.0.0:$PORT 

You could also not run collectstatic in your dockerfile, or event run the application because these can be ran by heroku.yml, for example;

build:   docker:     web: Dockerfile   config:     DJANGO_SETTINGS_MODULE: project.settings run:   web: gunicorn backend.config.wsgi:application --bind 0.0.0.0:$PORT release:   image: web   command:     - python manage.py collectstatic --noinput 

You also shouldn't need to mkdir for your working directory. Just set WORKDIR /code/ early in your dockerfile and after that things will run based on that directory.

There's a decent article on this here; https://testdriven.io/blog/deploying-django-to-heroku-with-docker/

like image 45
markwalker_ Avatar answered Sep 23 '22 19:09

markwalker_