Getting ModuleNotFoundError: No module named '<project name>' when deploying with gunicorn

This is my gunicorn_start file inside env/bin/, by runnig this I aim to deploy my django application

#!/bin/bash

NAME='yumfed_web'
DJANGODIR=~/webapps/yumfed-web/yumfed_web
SOCKFILE=~/webapps/yumfed-web/run/gunicorn.sock
USER=webappuser
GROUP=webapps
NUM_WORKERS=3
DJANGO_SETTINGS_MODULE=yumfed_web.settingsprod
DJANGO_WSGI_MODULE=yumfed_web.wsgi
TIMEOUT=120

cd $DJANGODIR
echo "Starting $NAME as `whoami`"
source ~/webapps/yumfed-web/env/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH

RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR

ls
exec gunicorn ${DJANGO_WSGI_MODULE}:application \
  --name $NAME \
  --workers $NUM_WORKERS \
  --timeout $TIMEOUT \
  --user=$USER --group=$GROUP \
  --bind=unix:$SOCKFILE \
  --log-level=debug \
  --log-file=-

my folder structure:

wsgi file

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yumfed_web.settings')

application = get_wsgi_application()

settingsprod.py file

"""
Django settings for yumfed_web project.

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

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

For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from dotenv import load_dotenv
import os
from pathlib import Path

load_dotenv()

# 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/5.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY', '')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

ALLOWED_HOSTS = ['*']

INTERNAL_IPS = [
    "127.0.0.1",
]

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'tailwind',
    'django_browser_reload',
    'fontawesomefree',
    'ckeditor',
    'solo',

    'theme',
    'members',
    'news',
    'scholarship',
    'internship',
    'memories',
]

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

    "django_browser_reload.middleware.BrowserReloadMiddleware",
]

ROOT_URLCONF = 'yumfed_web.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            BASE_DIR / 'templates',
        ],
        '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',
            ],
        },
    },
]

# Register the generated 'theme' app
TAILWIND_APP_NAME = 'theme'

WSGI_APPLICATION = 'yumfed_web.wsgi.application'


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

DATABASES = {
    'default': {
        #'ENGINE': 'django.db.backends.sqlite3',
        #'NAME': BASE_DIR / 'db.sqlite3',

        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'yumfed',
        'USER': 'yumfeduser',
        'PASSWORD': os.environ.get('DB_PASSWORD', ''),
        'HOST': 'localhost',
        'PORT': '',
    }
}


# Password validation
# https://docs.djangoproject.com/en/5.0/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',
    },
]

AUTH_USER_MODEL = 'members.CustomUser'

AUTHENTICATION_BACKENDS = ['members.backends.EmailBackend']


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

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


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

STATIC_URL = 'static/'

STATICFILES_DIRS = [
    BASE_DIR / "static",
]

MEDIA_URL = '/media/'

MEDIA_ROOT = BASE_DIR / "media"

# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

and error I get

Starting yumfed_web as root
internship  members   news		scholarship  templates	yumfed_web
manage.py   memories  requirements.txt	static	    theme
[2024-05-17 10:57:21 +0000] [56611] [DEBUG] Current configuration:
  config: ./gunicorn.conf.py
  wsgi_app: None
  bind: ['unix:/root/webapps/yumfed-web/run/gunicorn.sock']
  backlog: 2048
  workers: 3
  worker_class: sync
  threads: 1
  worker_connections: 1000
  max_requests: 0
  max_requests_jitter: 0
  timeout: 120
  graceful_timeout: 30
  keepalive: 2
  limit_request_line: 4094
  limit_request_fields: 100
  limit_request_field_size: 8190
  reload: False
  reload_engine: auto
  reload_extra_files: []
  spew: False
  check_config: False
  print_config: False
  preload_app: False
  sendfile: None
  reuse_port: False
  chdir: /root/webapps/yumfed-web/yumfed_web
  daemon: False
  raw_env: []
  pidfile: None
  worker_tmp_dir: None
  user: 999
  group: 988
  umask: 0
  initgroups: False
  tmp_upload_dir: None
  secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
  forwarded_allow_ips: ['127.0.0.1']
  accesslog: None
  disable_redirect_access_to_syslog: False
  access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
  errorlog: -
  loglevel: debug
  capture_output: False
  logger_class: gunicorn.glogging.Logger
  logconfig: None
  logconfig_dict: {}
  logconfig_json: None
  syslog_addr: udp://localhost:514
  syslog: False
  syslog_prefix: None
  syslog_facility: user
  enable_stdio_inheritance: False
  statsd_host: None
  dogstatsd_tags: 
  statsd_prefix: 
  proc_name: yumfed_web
  default_proc_name: yumfed_web.wsgi:application
  pythonpath: None
  paste: None
  on_starting: <function OnStarting.on_starting at 0x79836f367920>
  on_reload: <function OnReload.on_reload at 0x79836f367a60>
  when_ready: <function WhenReady.when_ready at 0x79836f367ba0>
  pre_fork: <function Prefork.pre_fork at 0x79836f367d80>
  post_fork: <function Postfork.post_fork at 0x79836f367ec0>
  post_worker_init: <function PostWorkerInit.post_worker_init at 0x79836f38c040>
  worker_int: <function WorkerInt.worker_int at 0x79836f38c180>
  worker_abort: <function WorkerAbort.worker_abort at 0x79836f38c2c0>
  pre_exec: <function PreExec.pre_exec at 0x79836f38c400>
  pre_request: <function PreRequest.pre_request at 0x79836f38c540>
  post_request: <function PostRequest.post_request at 0x79836f38c5e0>
  child_exit: <function ChildExit.child_exit at 0x79836f38c720>
  worker_exit: <function WorkerExit.worker_exit at 0x79836f38c860>
  nworkers_changed: <function NumWorkersChanged.nworkers_changed at 0x79836f38c9a0>
  on_exit: <function OnExit.on_exit at 0x79836f38cae0>
  ssl_context: <function NewSSLContext.ssl_context at 0x79836f38ccc0>
  proxy_protocol: False
  proxy_allow_ips: ['127.0.0.1']
  keyfile: None
  certfile: None
  ssl_version: 2
  cert_reqs: 0
  ca_certs: None
  suppress_ragged_eofs: True
  do_handshake_on_connect: False
  ciphers: None
  raw_paste_global_conf: []
  strip_header_spaces: False
  permit_unconventional_http_method: False
  permit_unconventional_http_version: False
  casefold_http_method: False
  header_map: drop
  tolerate_dangerous_framing: False
[2024-05-17 10:57:21 +0000] [56611] [INFO] Starting gunicorn 22.0.0
[2024-05-17 10:57:21 +0000] [56611] [DEBUG] Arbiter booted
[2024-05-17 10:57:21 +0000] [56611] [INFO] Listening at: unix:/root/webapps/yumfed-web/run/gunicorn.sock (56611)
[2024-05-17 10:57:21 +0000] [56611] [INFO] Using worker: sync
[2024-05-17 10:57:21 +0000] [56615] [INFO] Booting worker with pid: 56615
[2024-05-17 10:57:21 +0000] [56615] [ERROR] Exception in worker process
Traceback (most recent call last):
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/arbiter.py", line 609, in spawn_worker
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/workers/base.py", line 134, in init_process
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/workers/base.py", line 146, in load_wsgi
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/app/base.py", line 67, in wsgi
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/util.py", line 371, in import_app
  File "/usr/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1310, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1324, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'yumfed_web'
[2024-05-17 10:57:21 +0000] [56615] [INFO] Worker exiting (pid: 56615)
[2024-05-17 10:57:21 +0000] [56616] [INFO] Booting worker with pid: 56616
[2024-05-17 10:57:21 +0000] [56616] [ERROR] Exception in worker process
Traceback (most recent call last):
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/arbiter.py", line 609, in spawn_worker
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/workers/base.py", line 134, in init_process
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/workers/base.py", line 146, in load_wsgi
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/app/base.py", line 67, in wsgi
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
  File "/root/webapps/yumfed-web/env/lib/python3.12/site-packages/gunicorn/util.py", line 371, in import_app
  File "/usr/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1310, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1324, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'yumfed_web'
[2024-05-17 10:57:21 +0000] [56616] [INFO] Worker exiting (pid: 56616)
[2024-05-17 10:57:21 +0000] [56611] [ERROR] Worker (pid:56615) exited with code 3
[2024-05-17 10:57:21 +0000] [56611] [ERROR] Worker (pid:56616) was sent SIGTERM!
[2024-05-17 10:57:21 +0000] [56611] [ERROR] Shutting down: Master
[2024-05-17 10:57:21 +0000] [56611] [ERROR] Reason: Worker failed to boot.

when I run gunicorn yumfed_web.wsgi:application from ~/webapps/yumfed-web directory it runs without errors

The first possibility that comes to mind is a permissions issue. When you run this manually, what user are you logged in as? Does UID 999, GID 988 have all the necessary permissions into the project directory structure?

1 Like

I am logged in as root when running the script, as you can see in output ā€œwhoamiā€ returns root


directories are owned by webappuser

I changed my user and group form webappuser, webapps to root, root in gunicorn_start file and it run properly. So as @KenWhitesell mentioned it is, indeed, permission issue. But I cannot think of any solution to this issue.

Check through all the files and directories in your project to ensure that everything is owned by webappuser.

1 Like


wsgi.py is not executable, can it be a reason?

No, your python code does not need to be executable.

1 Like

I just realized that your webapps directory is in /root. Iā€™m willing to guarantee that webapps does not have the necessary permissions to /root. You should have all this in something like /home/webappuser/webapps (or /opt/webapps, etc). Your project should not reside in someone elseā€™s home directory, and almost certainly not in /root.

1 Like

as you advised I moved project and venv to /webapps directory and it solved the issue, Thanks very much. So, the lesson I need to learn is not to put projects inside /root folder, as it is unsecure an will not work.