Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/1-getting-started/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ BREVO_API_KEY BREVO_API_KEY n/a
BREVO_API_URL n/a n/a "https://api.brevo.com/v3/"
SPARKPOST_API_KEY SPARKPOST_API_KEY n/a raises error
SPARKPOST_API_URL n/a n/a "https://api.sparkpost.com/api/v1"
n/a PRODUCTION_PROCESSES n/a a configured dictionary depending on your choices - check settings/production.py
======================================= =========================== ============================================== ======================================================================

--------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/2-local-development/developing-locally.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Make sure to have the following on your host:

#. If you're running synchronously, see the application being served through Django development server: ::

uv run python manage.py runserver 0.0.0.0:8000
uv run python manage.py devserver 0.0.0.0:8000

or if you're running asynchronously: ::

Expand Down
10 changes: 3 additions & 7 deletions {{cookiecutter.project_slug}}/Procfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
release: python manage.py migrate
{%- if cookiecutter.use_async == "y" %}
web: gunicorn config.asgi:application -k uvicorn_worker.UvicornWorker
{%- else %}
web: gunicorn config.wsgi:application
{%- endif %}
web: python manage.py prodserver web
{%- if cookiecutter.use_celery == "y" %}
worker: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app worker --loglevel=info
beat: REMAP_SIGTERM=SIGQUIT celery -A config.celery_app beat --loglevel=info
worker: REMAP_SIGTERM=SIGQUIT python manage.py prodserver worker
beat: REMAP_SIGTERM=SIGQUIT python manage.py prodserver beat
Comment on lines +4 to +5
Copy link
Member

@browniebroke browniebroke Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to deploy that to heroku and I'm getting some errors (Sentry public link)

Logs

Worker process:

heroku/worker.1 Starting process with command `REMAP_SIGTERM=SIGQUIT python manage.py prodserver worker`
heroku/worker.1 State changed from starting to up
app/worker.1 Starting server named worker
app/worker.1 Traceback (most recent call last):
app/worker.1   File "/app/manage.py", line 30, in <module>
app/worker.1     main()
app/worker.1     ~~~~^^
app/worker.1   File "/app/manage.py", line 26, in main
app/worker.1     execute_from_command_line(sys.argv)
app/worker.1     ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
app/worker.1   File "/app/.heroku/python/lib/python3.13/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
app/worker.1     utility.execute()
app/worker.1     ~~~~~~~~~~~~~~~^^
app/worker.1   File "/app/.heroku/python/lib/python3.13/site-packages/django/core/management/__init__.py", line 436, in execute
app/worker.1     self.fetch_command(subcommand).run_from_argv(self.argv)
app/worker.1     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
app/worker.1   File "/app/.heroku/python/lib/python3.13/site-packages/django_prodserver/management/commands/prodserver.py", line 64, in run_from_argv
app/worker.1     self.start_server(*args, **cmd_options)
app/worker.1     ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
app/worker.1   File "/app/.heroku/python/lib/python3.13/site-packages/django_prodserver/management/commands/prodserver.py", line 102, in start_server
app/worker.1     backend.start_server(*backend.prep_server_args())
app/worker.1     ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/worker.1   File "/app/.heroku/python/lib/python3.13/site-packages/django_prodserver/backends/celery.py", line 18, in start_server
app/worker.1     self.app.Worker(*args).start()
app/worker.1     ~~~~~~~~~~~~~~~^^^^^^^
app/worker.1   File "/app/.heroku/python/lib/python3.13/site-packages/celery/worker/worker.py", line 94, in __init__
app/worker.1     self.app.loader.init_worker()
app/worker.1     ^^^^^^^^^^^^^^^
app/worker.1 AttributeError: 'str' object has no attribute 'loader'. Did you mean: 'lower'?
app/worker.1 Sentry is attempting to send 2 pending events
app/worker.1 Waiting up to 2 seconds
app/worker.1 Press Ctrl-C to quit
heroku/worker.1 State changed from up to crashed
heroku/worker.1 Process exited with status 1

Beat process:

app/beat.1 Starting server named beat
app/beat.1 Traceback (most recent call last):
app/beat.1   File "/app/manage.py", line 30, in <module>
app/beat.1     main()
app/beat.1     ~~~~^^
app/beat.1   File "/app/manage.py", line 26, in main
app/beat.1     execute_from_command_line(sys.argv)
app/beat.1     ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
app/beat.1   File "/app/.heroku/python/lib/python3.13/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
app/beat.1     utility.execute()
app/beat.1     ~~~~~~~~~~~~~~~^^
app/beat.1   File "/app/.heroku/python/lib/python3.13/site-packages/django/core/management/__init__.py", line 436, in execute
app/beat.1     self.fetch_command(subcommand).run_from_argv(self.argv)
app/beat.1     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
app/beat.1   File "/app/.heroku/python/lib/python3.13/site-packages/django_prodserver/management/commands/prodserver.py", line 64, in run_from_argv
app/beat.1     self.start_server(*args, **cmd_options)
app/beat.1     ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
app/beat.1   File "/app/.heroku/python/lib/python3.13/site-packages/django_prodserver/management/commands/prodserver.py", line 102, in start_server
app/beat.1     backend.start_server(*backend.prep_server_args())
app/beat.1     ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
app/beat.1   File "/app/.heroku/python/lib/python3.13/site-packages/django_prodserver/backends/celery.py", line 26, in start_server
app/beat.1     self.app.Beat(*args).start()
app/beat.1     ^^^^^^^^^^^^^^^^^^^^^^^^^^
app/beat.1 AttributeError: 'Beat' object has no attribute 'start'
app/beat.1 Sentry is attempting to send 2 pending events
app/beat.1 Waiting up to 2 seconds
app/beat.1 Press Ctrl-C to quit
heroku/beat.1 State changed from up to crashed
heroku/beat.1 Process exited with status 1

That might have been due to a change in Celery... ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a project that was possibly erroring like this when trying to add celery with prodserver. Hopefully I'll have time over Christmas to look at this!

{%- endif %}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ set -o errexit
set -o pipefail
set -o nounset


exec celery -A config.celery_app beat -l INFO
# https://django-prodserver.readthedocs.io/en/latest/
exec ./manage.py prodserver beat
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ set -o errexit
set -o pipefail
set -o nounset


exec celery -A config.celery_app worker -l INFO
# https://django-prodserver.readthedocs.io/en/latest/
exec ./manage.py prodserver worker
7 changes: 2 additions & 5 deletions {{cookiecutter.project_slug}}/compose/production/django/start
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,5 @@ if compress_enabled; then
python /app/manage.py compress
fi
{%- endif %}
{%- if cookiecutter.use_async == 'y' %}
exec gunicorn config.asgi --bind 0.0.0.0:5000 --chdir=/app -k uvicorn_worker.UvicornWorker
{%- else %}
exec gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
{%- endif %}
# https://django-prodserver.readthedocs.io/en/latest/
exec python /app/manage.py prodserver web
44 changes: 44 additions & 0 deletions {{cookiecutter.project_slug}}/config/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,5 +446,49 @@
]

{%- endif %}

# django-prodserver
# https://django-prodserver.readthedocs.io/en/latest/
INSTALLED_APPS += ["django_prodserver"]

PRODUCTION_PROCESSES = {
{%- if cookiecutter.use_docker == 'y' %}
{%- if cookiecutter.use_async == 'y' %}
"web": {
"BACKEND": "django_prodserver.backends.gunicorn.GunicornServer",
"ARGS": {"bind": "0.0.0.0:5000", "chdir": "/app", "worker-class": "uvicorn_worker.UvicornWorker"},
},
{%- else %}
"web": {
"BACKEND": "django_prodserver.backends.gunicorn.GunicornServer",
"ARGS": {"bind": "0.0.0.0:5000", "chdir": "/app"},
},
{%- endif %}
{%- else %}
{%- if cookiecutter.use_async == 'y' %}
"web": {
"BACKEND": "django_prodserver.backends.gunicorn.GunicornServer",
"ARGS": {"worker-class": "uvicorn_worker.UvicornWorker"},
},
{%- else %}
"web": {
"BACKEND": "django_prodserver.backends.gunicorn.GunicornServer",
"ARGS": {},
},
{%- endif %}
{%- endif %}
{%- if cookiecutter.use_celery == 'y' %}
"worker": {
"BACKEND": "django_prodserver.backends.celery.CeleryWorker",
"APP": "config.celery_app",
"ARGS": {"loglevel": "info"},
},
"beat": {
"BACKEND": "django_prodserver.backends.celery.CeleryBeat",
"APP": "config.celery_app",
"ARGS": {"loglevel": "info"},
},
{%- endif %}
}
# Your stuff...
# ------------------------------------------------------------------------------
2 changes: 1 addition & 1 deletion {{cookiecutter.project_slug}}/gulpfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function asyncRunServer() {
{%- else %}
// Run django server
function runServer(cb) {
const cmd = spawn('python', ['manage.py', 'runserver'], { stdio: 'inherit' });
const cmd = spawn('python', ['manage.py', 'devserver'], { stdio: 'inherit' });
cmd.on('close', function (code) {
console.log('runServer exited with code ' + code);
cb(code);
Expand Down
1 change: 1 addition & 0 deletions {{cookiecutter.project_slug}}/requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ django-environ==0.12.0 # https://github.com/joke2k/django-environ
django-model-utils==5.0.0 # https://github.com/jazzband/django-model-utils
django-allauth[mfa]==65.13.1 # https://github.com/pennersr/django-allauth
django-crispy-forms==2.5 # https://github.com/django-crispy-forms/django-crispy-forms
django-prodserver==2.3.0 # https://github.com/nanorepublica/django-prodserver
crispy-bootstrap5==2025.6 # https://github.com/django-crispy-forms/crispy-bootstrap5
{%- if cookiecutter.frontend_pipeline == 'Django Compressor' %}
django-compressor==4.6.0 # https://github.com/django-compressor/django-compressor
Expand Down