Managing Django Static Files on Heroku

Deploying and running Django apps on Heroku is really a no-brainer, except for one thing — serving static files via collectstatic.

I run collectstatic as usual, using heroku run command just like how I did it for syncdb. It worked but I’m getting 404 error when serving static files.

It turns out that running collectstatic via heroku run spins up a new dyno and collectstatic is running in an isolated environment. So, all the collected static files are deployed to that location and only for this new dyno and the dyno attached to our Django app can’t access that. — Heroku support staff

Solution

The dead simple solution would be to run collectstatic as part of the Procfile before starting the app. We need to “chain” together our Procfile commands for the web process, like so:

1
web: python my_django_app/manage.py collectstatic --noinput; bin/gunicorn_django --workers=4 --bind=0.0.0.0:$PORT my_django_app/settings.py
view raw Procfile hosted with ❤ by GitHub

OK there you go, no more 404 error when serving static files from your Django app on Heroku. Plus, every time you deploy your app, newly added static files will be collected automatically.

Update

There are a lot of questions about the configurations. Cross check with my settings.py here http://pastebin.com/H43gRKsJ

Important thing here is your STATICFILES_DIRS. Make sure to include your project_name/app_name/static here. In my case, I have project_name/staticfiles for the STATIC_ROOT. Change STATIC_URL = ‘/static/’ if you want to serve from Heroku, same goes to ADMIN_MEDIA_PREFIX = ‘/static/admin/’

Finally add this to your urls.py in order to serve from Heroku.

urlpatterns += patterns(”,
(r’^static/(?P.*)$’, ‘django.views.static.serve’, {‘document_root’: settings.STATIC_ROOT}),
)

Files could be access as such:

/app/project_name/staticfiles/style.css > http://flaming-fire-999.herokuapp.com/static/style.css

/app/lib/python2.7/site-packages/django/contrib/admin/media/css/login.css > http://flaming-fire-999.herokuapp.com/static/admin/css/login.css
Please note that this is not the best way to serve static files but it works. One last thing, you still need to go through all these things if you are using django storages and django compressor to serve static files via Amazon S3 because django compressor needs a temp file system cache on Heroku.

39 thoughts on “Managing Django Static Files on Heroku

  1. Pingback: Django Static Files on Heroku — elweb

  2. Pingback: Managing Django Static Files on Heroku | Програмиране

  3. I’ve just been looking into Heroku for hosting myself… I had read that the Cedar filesystem was ephemeral, i.e. wiped whenever the dyno is restarted. I thought that would preclude serving Django’s static files.

    But do your commands above automatically run collectstatic when the new dyno is spun up, to reinstate them?

    So it would just be user-uploaded files I’d need to run from S3 instead?

    • Sorry for the late, late reply.

      It will run every time you do git push heroku master.

      FYI, I’m using django storages & django compressor to serve static files via Amazon S3. In order for compressor to work, you’ll need a temp file system cache on Heroku, thus the solution above.

  4. Hi there, am running into the same problem.

    I wonder how you’ve setup your STATIC_URL, STATIC_ROOT and STATICFILES_DIR as I’m almost hitting my head on the wall now :)

  5. Thanks :) It’s working now. Founded solution.

    So I have:
    In Debug:

    STATIC_ROOT = ‘static/’
    STATIC_URL = ‘/static/’
    and in urls.py
    urlpatterns = patterns(”,
    …####
    #### urls urls urls… ;)
    )
    + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

    And it’s working great :)

      • Even with Kamil’s writeup, I’m not sure what you’re proposing here. Sure, running collectstatic gets all the files in one place.

        But have you configured gunicorn to serve that directory separate from the Django staticfiles app?

        (If you’re willing to run the staticfiles app with DEBUG on, there’s not even a need to collectstatic; it seems to serve things fine from wherever they are. But that’s “probably insecure”, says the Django docs. So I’m trying to figure out if what you Kamil is describing somehow gets heroku-nginx or gunicorn to serve the collectedstatic directory…)

        • With more research I’m guessing you’ve each settled on some variant like this in urls.py:

          if not settings.DEBUG:
          # screw the vague FUD and django doctrine against non-DEBUG static-serving
          urlpatterns += patterns(”,
          (r’^static/(?P.*)$’, ‘django.views.static.serve’, {‘document_root’ :settings.STATIC_ROOT}),
          )

          The comments for ‘django.views.static.serve’ include more of the usual admonitions about how one ‘SHOULD NOT’ do this in a ‘production setting’. But these similarly-worded-but-never-explained warnings have echoed across Django comments/docs so much they’re beginning to look like superstitions to me rather than actual best practices.

  6. Thanks for this post! It was very helpful in getting django static files on heroku running.

    However, I’ve noticed that every time I deploy a change, even without adding any new static files, collectstatic has to run every time and causes the app restart to be longer. This is sort of annoying because any users on the site would experience a long response if they made a request while this process was happening. Do you have any advice for this problem?

    • This is because the collectstatic command is part of the Procfile. Quick solution will be creating two versions of Procfile, one with the collectstatic command.

      I can’t think of other cleaner solution.

  7. Nice post but I have a problem. I can’t get this done. I’m getting:

    unknown specifier: ?P.

    My urls.py looks like:

    admin.autodiscover()

    urlpatterns = patterns(”,


    )

    if not settings.DEBUG:
    urlpatterns += patterns”,
    (r’^static/(?P.*)$’, ‘django.views.static.serve’, {‘document_root’: settings.STATIC_ROOT}),
    )

  8. Pingback: Heroku – Django app static files – Stack Overflow | Programmer Solution

  9. Pingback: Heroku – Handling static files in Django app

  10. Pingback: Your first Heroku Django app « Fear and Loathing on the Learning Curve » Observations on life, tech and web design from a slightly misanthropic mind.

  11. Pingback: Heroku Django: Handling static files. Could not import settings ‘my_django_app/settings.py’

  12. Hi! Thank you for the blog post. However, I encountered this issue when running ‘foreman start’

    13:59:40 web.1 | started with pid 2060
    13:59:40 web.1 | Usage: manage.py collectstatic [options]
    13:59:40 web.1 |
    13:59:40 web.1 | Collect static files in a single location.
    13:59:40 web.1 |
    13:59:40 web.1 | manage.py: error: no such option: –noinput;
    13:59:40 web.1 | process terminated
    13:59:40 system | sending SIGTERM to all processes

    I used this line in my Profile:
    web: python manage.py collectstatic –noinput; gunicorn_django –workers=4 –bind=0.0.0.0:$PORT

    It seems to be an issue with chaining the commands together on one line…any idea?

  13. Pingback: Heroku – Handling static files in Django app | PHP Developer Resource

  14. Thanks for the post!! Question– how would I handle serving just a handful of static files on Heroku, and the majority via my main backend on Amazon S3? There are just a few files that I need on Heroku (to be on the same domain name… it’s related to browser compatibility issues), but I still want the rest of my files to be served via S3.
    Any suggestions on how to go about this?

  15. So what’s stopping you from committing the changed static files after running a collectstatic locally? Seems to work for me so far.

    In my settings.py I have:

    PROJECT_DIR = os.path.dirname(__file__)

    Then I use that in settings.py and urls.py to set the static directory… (Rather than a path starting in / as the manual insists on using…)

    STATIC_ROOT = os.path.join(PROJECT_DIR, ‘static’)

    for example…

  16. Also, the procfile didn’t work. I got it working with:

    web: python manage.py collectstatic –noinput; gunicorn -b 0.0.0.0:$PORT -w 4 [project name].wsgi:application

    [project name] is the folder containing wsgi.py

    The following was enough if I committed the static files:
    web: gunicorn -b 0.0.0.0:$PORT -w 4 [project name].wsgi:application

  17. Pingback: Django non-rel on Heroku with less and coffee-script compilation « Be Amity

  18. Pingback: Deployment of static files to Heroku | Web App (B)Log

  19. Sorry but after updating your urls.py to serve the static files doesn’t that mean that every static file requested will have to be served by both the Dyno and the Django app instead of just the Dyno.
    This will push extra load on your app and that is the reason why it is marked as not good for production in the docs.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>