Run collectstatic when deploying Django app to Heroku w/ Docker: heroku run python manage.py collectstatic

Issue: Collecting static files with Django fails in Dockerfile, but works in Heroku release command.

When deploying a Django app to Heroku using Docker, the RUN manage.py collectstatic --noinput command in the Dockerfile fails because there is no value set for the environment variable DJANGO_SECRET_KEY.

However, when the collectstatic command is run as a release command, it works without error, but the app returns a 500 error when the URL is hit because the copied files are not found on the ephemeral filesystem.

Solution:

To solve this issue, the collectstatic command needs to be run in the Heroku release command instead of the Dockerfile. The settings for collectstatic should be included in settings.py, as shown in the following example:

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'

The Dockerfile should create a staticfiles directory and copy the static files to it.

# 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/

## create staticfiles directory
RUN mkdir backend/staticfiles

The Heroku release command should run collectstatic to copy the files to the directory created earlier:

heroku.yml

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

Issue: Collecting static files with Django fails in Dockerfile, but works in Heroku release command.

Solution:

To solve this issue, the collectstatic command needs to be run in the Heroku release command instead of the Dockerfile. The settings for collectstatic should be included in settings.py, as shown in the following example:

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'

The Dockerfile should create a staticfiles directory and copy the static files to it.

# 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/

## create staticfiles directory
RUN mkdir backend/staticfiles

The Heroku release command should run collectstatic to copy the files to the directory created earlier:

heroku.yml

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