Docker Compose fails to collect static

I am preparing a release in production for a Dockerized Django-admin blog.

settings.py:

"""
Django settings for XXXX project.

Generated by 'django-admin startproject' using Django 3.2.7.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
import os
from pathlib import Path
from django.contrib.messages import constants as messages

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

TEMPLATES_DIRS = os.path.join(BASE_DIR,'templates')

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('SECRET_KEY') or 'XXXXXXX'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
DEBUG = os.getenv('ENVIRONMENT') != 'production'
SESSION_COOKIE_SECURE = True

if DEBUG:
    # ALLOWED_HOSTS = ['127.0.0.1', 'XXX.XX.XX.XX', 'localhost']
    ALLOWED_HOSTS = ['*']
else:
    ALLOWED_HOSTS = ['{ALLOWED_HOST_1}', '{ALLOWED_HOST_2}']
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.contenttypes',
    'django.contrib.auth',
    'django.contrib.messages',
    'django.contrib.sessions',
    'django.contrib.staticfiles',
    'django.contrib.sitemaps',
    'widget_tweaks',
    'blog',
    'ckeditor',
    'taggit',
    'hitcount'
]

DEFAULT_FROM_EMAIL = 'XXXXXXX'

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'admin.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [TEMPLATES_DIRS],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'libraries':{
                'is_current': 'blog.templatetags.is_current'
            }
        }
    },
]

MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'

MESSAGE_TAGS = {
        messages.DEBUG: 'alert-secondary',
        messages.INFO: 'alert-info',
        messages.SUCCESS: 'alert-success',
        messages.WARNING: 'alert-warning',
        messages.ERROR: 'alert-danger',
}

WSGI_APPLICATION = 'admin.wsgi.application'

LANGUAGE_CODE = 'fr-fr'  # or other appropriate code
USE_I18N = True
USE_L10N = True
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': os.getenv('DB_NAME') or 'XXXXXX',
        'USER': os.getenv('DB_USER') or 'XXXX',
        'PASSWORD': os.getenv('DB_PASSWORD') or 'XXXX',
        'HOST': os.getenv('DB_HOST') or 'XXXX'
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
    },
    'handlers': {
        'console': {
            'level': 'NOTSET',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose'
        }
    },
    'loggers': {
        '': {
            'handlers': ['console'],
            'level': 'NOTSET',
        },
        'django.request': {
            'handlers': ['console'],
            'propagate': False,
            'level': 'ERROR'
        }
    }
}

HITCOUNT_KEEP_HIT_ACTIVE = { 'seconds': 1 }

MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/')
MEDIA_URL = '/media/'

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'fr-FR'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]
if not DEBUG:
    # Static files are built with "python manage.py collectstatic" to "dist" directory and then served by nginx
    STATIC_ROOT = os.path.join(BASE_DIR, 'dist')
    
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

CSP_DEFAULT_SRC = ["'none'"]
CSP_SCRIPT_SRC = [
    "https://stackpath.bootstrapcdn.com",
    "https://cdn.jsdelivr.net",
    "https://code.jquery.com",
    "https://fonts.googleapis.com",
    'self'
]
CSP_STYLE_SRC = ["https://fonts.googleapis.com"]
CSP_FONT_SRC = [
    "https://fonts.googleapis.com"
]
CSP_IMG_SRC = ["'self'"]
CSP_FRAME_SRC = [
                "https://app.mailjet.com",
                "https://fonts.googleapis.com"
]
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

# DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

Dockerfile for the app:

FROM python:3

ENV PYTHONUNBUFFERED=1


WORKDIR /code
COPY requirements.txt /code

RUN pip install -r requirements.txt

COPY . /code/

Dockerfile for DB:

FROM postgres:9.6

COPY ./docker/db/init-script.sh /docker-entrypoint-initdb.d/init-script.sh

Init-script.sh for DB:

#!/bin/bash
set -e

if [ ! -z $DB_USER ]; then
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';
    CREATE DATABASE "$DB_NAME";
    GRANT ALL PRIVILEGES ON DATABASE "$DB_NAME" TO $DB_USER;
EOSQL
fi

Dockerfile for Nginx:

FROM nginx:1.21.3-alpine

# Configuration
COPY ./docker/nginx/nginx.conf /etc/nginx/conf.d/default.conf

# Install the packages required for watchman to work properly:
RUN apk add --no-cache libcrypto1.0 libgcc libstdc++

# Copy the watchman executable binary directly from our image:
COPY --from=icalialabs/watchman:4-alpine3.4 /usr/local/bin/watchman* /usr/local/bin/

# Create the watchman STATEDIR directory:
RUN mkdir -p /usr/local/var/run/watchman \
 && touch /usr/local/var/run/watchman/.not-empty

# (Optional) Copy the compiled watchman documentation:
COPY --from=icalialabs/watchman:4-alpine3.4 /usr/local/share/doc/watchman* /usr/local/share/doc/

# Continue with the rest of your Dockerfile...

# Static files
COPY ./dist/ /var/www/XXXX/static

Nginx.conf:

server {
    listen 80;
    server_name XXXXX.fr;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /var/www/XXXXX;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Below my docker-compose.yml:

version: "3"

services:

  db:
    build: 
      dockerfile: ./docker/db/Dockerfile
      context: .
    volumes:
      - ./data/dev:/var/lib/postgresql/data:z
    environment:
      - POSTGRES_DB=XXXX
      - POSTGRES_USER=XX
      - POSTGRES_PASSWORD=XX
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d XXXXX"]
      interval: 10s
      timeout: 10s
      retries: 5      

  app:
    build: 
      dockerfile: ./docker/app/Dockerfile
      context: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    environment:
      - DB_HOST=XXX
      - DB_NAME=XXX
      - DB_USER=XXXX
      - DB_PASSWORD=XXXX
    depends_on:
      db:
        condition: service_healthy

Here my docker-compose.prod.yml:

version: "3"

services:

  app:
    build: 
      dockerfile: ./docker/app/Dockerfile
      context: .
    command: gunicorn --access-logformat  "{'host':'%({host}i)s','remote_ip':'%(h)s','response_code':'%(s)s','request_method':'%(m)s','request_path':'%(U)s','request_querystring':'%(q)s','request_timetaken':'%(D)s','response_length':'%(B)s'}" --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock admin.wsgi:application
    volumes:
      - .:/code
      - ./docker/app/sock:/run
    environment:
      - ENVIRONMENT=production
      - DB_HOST=XX
      - DB_NAME=XX
      - DB_USER=XX
      - DB_PASSWORD=XXX
      - SECRET_KEY=XXXXX
    depends_on:
      db:
        condition: service_healthy

  db:
    volumes:
      - ./data/prod:/var/lib/postgresql/data
    environment:
      - DB_USER=XXXXX
      - DB_PASSWORD=XXXXX
      - DB_NAME=XXXXX
  
  nginx:
    build:
      dockerfile: ./docker/nginx/Dockerfile
      context: .
    volumes:
      - ./docker/app/sock:/run
    ports:
      - "80:80"

On my host machine when I run docker-compose exec app python manage.py collectstatic here is the traceback:

Traceback (most recent call last): 

File "/code/[manage.py](https://manage.py/)", line 22, in <module> main() 

File "/code/[manage.py](https://manage.py/)", line 18, in main execute_from_command_line(sys.argv) 

File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line utility.execute() 

File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 413, in execute self.fetch_command(subcommand).run_from_argv(self.argv) 

File "/usr/local/lib/python3.11/site-packages/django/core/management/[base.py](https://base.py/)", line 354, in run_from_argv self.execute(*args, **cmd_options) File "/usr/local/lib/python3.11/site-packages/django/core/management/[base.py](https://base.py/)", line 398, in execute output = self.handle(*args, **options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

File "/usr/local/lib/python3.11/site-packages/django/contrib/staticfiles/management/commands/[collectstatic.py](https://collectstatic.py/)", line 187, in handle collected = self.collect() ^^^^^^^^^^^^^^ 

File "/usr/local/lib/python3.11/site-packages/django/contrib/staticfiles/management/commands/[collectstatic.py](https://collectstatic.py/)", line 114, in collect handler(path, prefixed_path, storage) 

File "/usr/local/lib/python3.11/site-packages/django/contrib/staticfiles/management/commands/[collectstatic.py](https://collectstatic.py/)", line 338, in copy_file if not self.delete_file(path, prefixed_path, source_storage): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

File "/usr/local/lib/python3.11/site-packages/django/contrib/staticfiles/management/commands/[collectstatic.py](https://collectstatic.py/)", line 248, in delete_file if self.storage.exists(prefixed_path): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

File "/usr/local/lib/python3.11/site-packages/django/core/files/[storage.py](https://storage.py/)", line 318, in exists return os.path.exists(self.path(name)) ^^^^^^^^^^^^^^^ 

File "/usr/local/lib/python3.11/site-packages/django/contrib/staticfiles/[storage.py](https://storage.py/)", line 38, in path raise ImproperlyConfigured("You're using the staticfiles app " django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path.

This seems to be an issue with the STATIC_ROOT config on the settings.py side, I might be able to find a workaround. However, when I try to use docker-compose.yml with docker-compose.prod.yml, it fails on my host machine while the container is of course running. Please find the traceback:

Usage:  docker compose [OPTIONS] COMMAND

Docker Compose

Options:
      --ansi string                Control when to print ANSI control characters
                                   ("never"|"always"|"auto") (default "auto")
      --compatibility              Run compose in backward compatibility mode
      --env-file string            Specify an alternate environment file.
  -f, --file stringArray           Compose configuration files
      --parallel int               Control max parallelism, -1 for unlimited (default -1)
      --profile stringArray        Specify a profile to enable
      --project-directory string   Specify an alternate working directory
                                   (default: the path of the, first specified, Compose file)
  -p, --project-name string        Project name

Commands:
  build       Build or rebuild services
  convert     Converts the compose file to platform's canonical format
  cp          Copy files/folders between a service container and the local filesystem
  create      Creates containers for a service.
  down        Stop and remove containers, networks
  events      Receive real time events from containers.
  exec        Execute a command in a running container.
  images      List images used by the created containers
  kill        Force stop service containers.
  logs        View output from containers
  ls          List running compose projects
  pause       Pause services
  port        Print the public port for a port binding.
  ps          List containers
  pull        Pull service images
  push        Push service images
  restart     Restart service containers
  rm          Removes stopped service containers
  run         Run a one-off command on a service.
  start       Start services
  stop        Stop services
  top         Display the running processes
  unpause     Unpause services
  up          Create and start containers
  version     Show the Docker Compose version information

Run 'docker compose COMMAND --help' for more information on a command.
unknown docker command: "compose python" same with docker compose

And the traceback from the running terminal:

app  | Traceback (most recent call last):
app  |   File "/code/manage.py", line 22, in <module>
app  |     main()
app  |   File "/code/manage.py", line 18, in main
app  |     execute_from_command_line(sys.argv)
app  |   File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
app  |     utility.execute()
app  |   File "/usr/local/lib/python3.11/site-packages/django/core/management/__init__.py", line 363, in execute
app  |     settings.INSTALLED_APPS
app  |   File "/usr/local/lib/python3.11/site-packages/django/conf/__init__.py", line 82, in getattr
app  |     self._setup(name)
app  |   File "/usr/local/lib/python3.11/site-packages/django/conf/__init__.py", line 69, in _setup
app  |     self._wrapped = Settings(settings_module)
app  |                     ^^^^^^^^^^^^^^^^^^^^^^^^^
app  |   File "/usr/local/lib/python3.11/site-packages/django/conf/__init__.py", line 170, in init
app  |     mod = importlib.import_module(self.SETTINGS_MODULE)
app  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app  |   File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
app  |     return _bootstrap._gcd_import(name[level:], package, level)
app  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app  |   File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
app  |   File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
app  |   File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
app  |   File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
app  |   File "<frozen importlib._bootstrap_external>", line 936, in exec_module
app  |   File "<frozen importlib._bootstrap_external>", line 1074, in get_code
app  |   File "<frozen importlib._bootstrap_external>", line 1004, in source_to_code
app  |   File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
app  |   File "/code/admin/settings.py", line 32
app  |     DEBUG = os.getenv('ENVIRONMENT') =! 'production'
app  |             ^^^^^^^^^^^^^^^^^^^^^^^^
app  | SyntaxError: cannot assign to function call

On the droplet/Ubuntu server, the command docker-compose -f docker-compose.yml -f docker-compose.prod.yml python manage.py collectstatic gives me nothing, just an empty output and the dist directory is not created.

Sorry for the long message.
My host machine is on Arch Linux (6.1.8-arch1-1) and the server on Ubuntu 20.04 (LTS) x64.

Thanks a lot!