113

I want to run my django project under gunicorn on localhost. I installed and integrated gunicorn. When I run:

python manage.py run_gunicorn

It works but there are no any static files (css and js)

I disabled debug and template_debug in settings.py (made them false), but it is still same. Am I missing something?

I call statics like:

{{ STATIC_URL }}css/etc....
alioguzhan
  • 7,657
  • 10
  • 46
  • 67

7 Answers7

215

When in development mode and when you are using some other server for local development add this to your urls.py

from django.contrib.staticfiles.urls import staticfiles_urlpatterns

# ... the rest of your URLconf goes here ...

urlpatterns += staticfiles_urlpatterns()

Note that staticfiles_urlpatterns() will only work when DEBUG = True is set in your settings.py.

More info here

When in production you never, ever put gunicorn in front. Instead you use a server like nginx which dispatches requests to a pool of gunicorn workers and also serves the static files.

See here

phoenix
  • 7,988
  • 6
  • 39
  • 45
rantanplan
  • 7,283
  • 1
  • 24
  • 45
  • when i re-enable debug and template_debug, it started to work correctly. it didnt matter to put those lines into urls.py. but now i cant see my custom 404 and 500 error pages. in the production ; should i make False Debug and template_debug ? – alioguzhan Oct 09 '12 at 13:41
  • @drTerminal I updated my answer and included the specific wording of the django link I cited. The custom 404/500 pages are only displayed when DEBUG=False. Please read the specific links, I think it will answer most of your questions. – rantanplan Oct 09 '12 at 13:47
  • 1
    This worked for me although I use Django 1.6. In the docs https://docs.djangoproject.com/en/1.4/howto/static-files/#serving-static-files-in-development, there is a different usage of urlpattern for 1.6., but with no result for me. So I used the 1.4 solution with success. – Timo Aug 20 '14 at 20:15
  • So are you saying that gunicorn will never serve static files itself—it will only serve whatever the WSGI app serves? – binki Jun 29 '16 at 14:43
  • @binki I am not sure what you mean. It *can* serve whatever you want it to serve. And in development, it makes sense to do it. But when in production you don't let gunicorn serve static files, because it's too slow and it's not its specialty. – rantanplan Jun 29 '16 at 15:19
  • What I mean is: gunicorn has no way to serve static files itself. It only serves what the WSGI application serves. I.e., there is no way to use core gunicorn, without extending it or adding modules, to bypass the WSGI app and serve static files. Is that correct? (Your answer seems to indirectly suggest that, but not say so straight out). – binki Jun 29 '16 at 15:25
  • @binki The links in my answer are dead it seems and I haven't worked with Django since.... 2013, so I don't remember what point I was trying to make :( *But* this link https://vxlabs.com/2015/12/08/gunicorn-as-your-django-development-server/ seems to suggest that it's not impossible. – rantanplan Jul 01 '16 at 13:53
  • @rantanplan That’s just another example (like the [`whitenoise` suggestion](http://stackoverflow.com/a/37220346/429091)) of adding additional WSGI apps to serve static files. The fact that people keep giving like examples suggests that gunicorn does not have a core, built-in ability to serve static files. These may be valid approaches, but django itself clearly documents that its development support for serving static files itself “[grossly inefficient and probably insecure, so it is unsuitable for production](https://docs.djangoproject.com/en/1.9/howto/static-files/#staticfiles-in-templates)” – binki Jul 01 '16 at 14:05
  • So I guess the clear answer is that with just django and gunicorn you can’t serve static files—you need an additional third party component registered with either django’s routing system or gunicorn’s. – binki Jul 01 '16 at 14:06
  • 1
    @binki Argh! You made me search :P So... since I had vague memories that you can actually do that, I searched a bit. If you download this pdf https://media.readthedocs.org/pdf/django/1.4.X/django.pdf, which is the obsolete version my answer was referring to, you'll see that it didn't matter if your app server could or couldn't, because Django did it for you. Of course it was grossly inefficient, but doable nonetheless. Don't know if it's possible anymore, and if yes, how it can be done. – rantanplan Jul 01 '16 at 14:23
  • 1
    Ah, it looks like maybe that would be [`django.views.static.serve`](https://docs.djangoproject.com/en/1.9/ref/views/#serving-files-in-development) which still exists in modern documentation. But basically with the same warning which hasn’t changed from the 1.4 docs “This view is not hardened for production use and should be used only as a development aid”. Not as strongly worded. So it’s doable with pure django and even documented, just it’s an “unsupported” use of it I guess. Just the `serve` function is always available but the `static` helper function automatically disables in production. – binki Jul 01 '16 at 14:35
  • Has anybody told you that you are AWESOME before ! – Yasser Sinjab Apr 24 '17 at 14:13
  • @YasserSinjab Heh. Glad you found it useful :) – rantanplan Apr 24 '17 at 21:52
  • Is there any way to do it without adding python code. It should be simpler like setting a environment variable to 1. – Shiplu Mokaddim Dec 01 '20 at 17:12
  • 1
  • works on 4.1, no need to add any config on nginx – Miebaka Jun 09 '23 at 23:04
38

Whitenoise

Post v4.0

http://whitenoise.evans.io/en/stable/changelog.html#v4-0

The WSGI integration option for Django (which involved editing wsgi.py) has been removed. Instead, you should add WhiteNoise to your middleware list in settings.py and remove any reference to WhiteNoise from wsgi.py. See the documentation for more details. (The pure WSGI integration is still available for non-Django apps.)

Pre v4.0

Heroku recommends this method at: https://devcenter.heroku.com/articles/django-assets:

Your application will now serve static assets directly from Gunicorn in production. This will be perfectly adequate for most applications, but top-tier applications may want to explore using a CDN with Django-Storages.

Install with:

pip install whitenoise
pip freeze > requirements.txt

wsgi.py:

import os
from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "free_books.settings")
application = get_wsgi_application()
application = DjangoWhiteNoise(application)

Tested on Django 1.9.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 2
    So, again, this means that the WSGI app is serving static files. Not gunicorn. – binki Jun 29 '16 at 15:32
  • 1
    `pip freeze > requirements.txt` will overwrite previous requirements, i would recommend `pip freeze | grep whitenoise >> requirements.txt` – Marco Lavagnino Oct 25 '17 at 17:48
  • 3
    Instructions above are deprecated as of whitenoise version 4.0 . http://whitenoise.evans.io/en/stable/changelog.html#v4-0 – Fuchida Jan 29 '19 at 20:11
25

I've used this for my development environment (which uses gunicorn):

from django.conf import settings
from django.contrib.staticfiles.handlers import StaticFilesHandler
from django.core.wsgi import get_wsgi_application


if settings.DEBUG:
    application = StaticFilesHandler(get_wsgi_application())
else:
    application = get_wsgi_application()

And then run gunicorn myapp.wsgi. This works similar to @rantanplan's answer, however, it does not run any middleware when running static files.

WhyNotHugo
  • 9,423
  • 6
  • 62
  • 70
23

The gunicorn should be used to serve the python "application" itself, while the static files are served by a static file server ( such as Nginx ).

This is an excerpt from one of my configurations:

upstream app_server_djangoapp {
    server localhost:8000 fail_timeout=0;
}

server {
    listen < server port goes here >;
    server_name < server name goes here >;

    access_log  /var/log/nginx/guni-access.log;
    error_log  /var/log/nginx/guni-error.log info;

    keepalive_timeout 5;

    root < application root directory goes here >;

    location /static {    
        autoindex on;    
        alias < static folder directory goes here >;    
    }

    location /media {
       autoindex on;
       alias < user uploaded media file directory goes here >;
    }

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;

        if (!-f $request_filename) {
            proxy_pass http://app_server_djangoapp;
            break;
        }
    }
}

Some notes:

  • The static root, media root, static files path prefix and media file path prefix are set up in your settings.py
  • Once you have nginx set up to serve from the static content directory, you need to run "python manage.py collectstatic" in your project root so that the static files in the various apps can be copied to the static folder

In closing: while it is possible to serve static files from gunicorn ( by enabling a debug-only static file serving view ), that is considered bad practice in production.

Lorenzo Lerate
  • 3,552
  • 3
  • 26
  • 25
Ngure Nyaga
  • 2,989
  • 1
  • 20
  • 30
  • 1
    yes i know. i m gonna use it under nginx in production. i am just makin a test in my localhost before production. – alioguzhan Oct 09 '12 at 13:42
  • 1
    When you say “while it is possible to serve static files from gunicorn”, are you actually meaning to say “gunicorn will **indirectly** serve static files if the WSGI app it delegates requests to serves static files”? – binki Jun 29 '16 at 15:31
13

While you're able to use Django to serve static files in development mode with the app django.contrib.staticfiles, that is not suitable for production use.

In order to serve static files, as Jamie Hewland says, normally one routes all the requests to /static/ using Nginx

location /static/ {

    alias /path/to/static/files;

}

Nginx + Gunicorn + Django

Also, and as coreyward says about Gunicorn / Unicorn

was not designed to solve the suite of problems involved in serving files to clients

Same line of reasoning applies if you consider other WSGI server like uWSGI instead of Gunicorn. In uWSGI documentation

it’s inefficient to serve static files via uWSGI. Instead, serve them directly from Nginx and completely bypass uWSGI

So, serving static files in production is something to be done by NGINX, not by Django or other WSGI servers.


Another way is to serve your static files in production is using WhiteNoise library which is very easy to setup (you might want to use a CDN so that most requests won't reach the Python app). As Miguel de Matos says, you just have to

  1. Collect static

     python manage.py collectstatic
    
  2. Installing whitenoise

     pip install whitenoise
    
  3. Add the following STATICFILES_STORAGE in settings.py

     STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
    
  4. Add the following to your MIDDLEWARE in settings.py (as mracette notes, "According to the whitenoise docs you should place the middleware after django.middleware.security.SecurityMiddleware")

     `MIDDLEWARE = [
       'django.middleware.security.SecurityMiddleware',
       'whitenoise.middleware.WhiteNoiseMiddleware',
       ...
     ]
    
Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
  • 1
    According to the whitenoise docs you should place the middleware *after* 'django.middleware.security.SecurityMiddleware' http://whitenoise.evans.io/en/stable/ – zestyrx Apr 17 '21 at 19:11
  • 1
    Big thumbs up for `whitenoise`, spent days trying to get docker + gunicorn to serve static files but it refused, I assumed due to Docker's file structure. Following this cleared it right up. – Michael Davidson Jan 16 '23 at 15:51
1

Since Django 1.3 there is django/conf/urls/static.py that handle static files in the DEBUG mode:

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... the rest of your URLconf goes here ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Read more https://docs.djangoproject.com/en/2.0/howto/static-files/#serving-static-files-during-development

frost-nzcr4
  • 1,540
  • 11
  • 16
0

If you are using Apache/Gunicorn then here is how I set mine up.

  1. In your Django root dir (with manage.py), create dirs mkdir -p django_static/static

  2. In your project settings.py set the following:

DEBUG = False
INSTALLED_APPS = [..., 'django.contrib.staticfiles', ...]
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, "django_static", "static")
  1. Run python manage.py collectstatic. This will output static content to django_static/static

  2. Start your gunicorn server with gunicorn your_project_name.wsgi (plus options)

  3. Assuming you have default global Apache settings, you'll need to create a soft link from /var/www to your static dir: sudo ln -s /path/to/your_django_project/django_static /var/www/your_django_project_static

  4. For your domain www.example.com that you wish to point to your Django app, configure the following virtual host in apache in order to proxy all requests submitted to https://www.example.com onto 127.0.0.1:8000 except for www.example.com/static/ routes (in which case, serve files to such requests from django_static):

<VirtualHost *:443>
    ServerName www.example.com
    DocumentRoot /var/www/your_django_project_static
    <Location "/">
        ProxyPreserveHost On
        ProxyPass http://127.0.0.1:8000/
        ProxyPassReverse http://127.0.0.1:8000/
    </Location>
    <Location "/static/">
        ProxyPass "!"
    </Location>
</VirtualHost>

Voila!

Magnus
  • 3,086
  • 2
  • 29
  • 51