How to serve static files to AWS when deploying Django app (`python manage.py collectstatic` didn't work)?

11,564

Solution 1

There is definitive guide about deploying a django app to AWS Elastic Beanstalk from RealPython - here it is. It has whole section about static files and how to configure it with eb and you don't need to know anything about nginx/apache etc.

Basically you should define container_commands in your eb config, these commands will be executed after application deploy is finished. For example migrate and collectstatic, so this is an example of such section in eb config file:

container_commands:
  01_migrate:
    command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py migrate --noinput"
    leader_only: true
  02_collectstatic:
    command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py collectstatic --noinput"

option_settings:
  "aws:elasticbeanstalk:application:environment":
    DJANGO_SETTINGS_MODULE: "iotd.settings"
    "PYTHONPATH": "/opt/python/current/app/iotd:$PYTHONPATH"
    "ALLOWED_HOSTS": ".elasticbeanstalk.com"
  "aws:elasticbeanstalk:container:python":
    WSGIPath: iotd/iotd/wsgi.py
    NumProcesses: 3
    NumThreads: 20
  "aws:elasticbeanstalk:container:python:staticfiles":
    "/static/": "www/static/"

Pay attention to aws:elasticbeanstalk:container:python:staticfiles part. And also you should define this part in your django settings file:

STATIC_ROOT = os.path.join(BASE_DIR, "..", "www", "static")
STATIC_URL = '/static/'

I copied this example almost entirely from article above, you should really check it, it's awesome.

UPD: how to debug missing staticfiles. I usually do this (it involves sshing to your eb instance):

  1. Make sure that django.contrib.staticfiles is included in my INSTALLED_APPS.
  2. Check in browser console url to missing file e.g. /static/js/somefile.js
  3. Make sure in my django settings STATIC_URL is the same e.g. /static/.
  4. Check actual value in STATIC_ROOT and check that this folder actually contains your static files in production server.
  5. Check that my eb config is pointing to correct folder (under your option_settings section in config)

Also you can try to collect static into /static dir on your production server (it's where eb looks for them by default). If all of a sudden it starts working - it means that your setting failed to override default one and you should check where else it was defined.

I hope these steps will help you to find right direction.

Solution 2

This is what worked for me.

Remove any staticfiles directives from .config files. Find Static Files section under the Software Configuration. The left column is each of your /static/ url. The right is your static folder relative to your parent directory.

enter image description here

Make sure your STATIC_ROOT setting matches the value on the right. Don't forget the trailing /.

Pasting my settings anyway.

 STATIC_URL = '/static/'
 STATICFILES_DIRS = ('assets',)
 STATIC_ROOT = os.path.join(BASE_DIR, '..', 'www', 'static')

And this is what my folder structure looks like relative to the settings files

project/
├── __init__.py
├── settings
│   ├── base.py
│   ├── __init__.py
│   ├── local.py
│   ├── production.py

wsgi.py, urls.py are located in project folder. www folder is one level above project folder.

Hope this saves your Sunday at least.

Share:
11,564
SilentDev
Author by

SilentDev

Updated on June 07, 2022

Comments

  • SilentDev
    SilentDev almost 2 years

    Yesterday, I created this post: DjangoRestFramework browsable api looks different locally vs when deployed on server?

    Basically, when I did python manage.py runserver, this showed up: local

    But after I deployed it to AWS (eb deploy), this is what I see when I access the site: on deploy

    The answer to the post above mentioned that it is because my static files were missing. So I searched how to deploy static files on AWS and came across this tutorial: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-django.html#python-django-update-app

    Under the "Create a Site Administrator" section, it mentions that in order to serve static files, I must first define STATIC_ROOT in settings.py (so I did: STATIC_ROOT = os.path.join(BASE_DIR, "ebdjangoapp/static/")) and then I did eb deploy. However, the site still looks the same as the 2nd image (without static files). I then tried doing python manage.py collectstatic (this created the static folder with the rest_framework directory inside it, containing the css files etc.) and then did eb deploy again but the site stil looks the same as the 2nd image.

    How come the static files still aren't showing up?

    Note, I searched around and came across this post: Django app deployment not loading static files and the answer says:

    "You then need to serve settings.STATIC_ROOT at settings.STATIC_URL via your web server of choice, very commonly nginx as a reverse proxy behind your Apache-mod_wsgi app server."

    But I have no idea how web servers (nginx, reverse proxy, Apache-mod_wsgi) works. I have a Django app I run locally with python manage.py runserver, and I have AWS elastic beanstalk. I deploy my Django app to AWS by doing eb deploy. What steps do I need to take in order for the static files to appear on deployment (assuming I don't know how to configure nginx, reverse proxy etc.).?

  • SilentDev
    SilentDev about 7 years
    Thanks for the link. So I edited settings.py so that STATIC_ROOT and STATIC_URL is what you mentioned. I then created a file called 02_python.config inside the .ebextensions folder and copy pasted exactly what you have posted here. I change iotd to ebdjango (name of my project.. my app name is ebdjangoapp). I then changed python ebjango/manage.py collectstatic --noinput to python manage.py collectstatic --noinput (since the python path leads to ebdjango already). But it still doesn't work (server still looks like the 2nd image I posted). Any idea how to debug this issue?
  • valignatev
    valignatev about 7 years
    Answer updated with possible debugging steps, hope it'll help.
  • SilentDev
    SilentDev about 7 years
    The update helped a lot! Now, when I let STATIC_ROOT = os.path.join(BASE_DIR, "static") and not mention staticfiles in my option_settings config file, it works (default location, as you mentioned)! When I give an absolute or relative path to "aws:elasticbeanstalk:container:python:staticfiles" in option_settings, it works as well. But when I change STATIC_ROOT to os.path.join(BASE_DIR, "..", "www", "static") and do "/static/": "www/static/" in my option_settings, it gives a 404 for static files. When I let it equal "/static/": "../www/static/", it gives a 403 for static.
  • SilentDev
    SilentDev about 7 years
    I'll mark you're answer as correct when the bounty is about to finish, but for now, any idea why it gives a 403 when I let STATIC_ROOT equal os.path.join(BASE_DIR, "..", "www", "static")? (it does create a ebdjango/../www/static/ folder which has the rest_framework staticfiles).
  • valignatev
    valignatev about 7 years
    403 means that some internal web server doesn't have access to this folder. Easiest way to fix it is give this access, but you have to know actual username of this server and it's not convenient. By the way, I consider you use eb instance for static file only as the matter of initial tutorial. Next step would be use S3 bucket for this :)
  • abautista
    abautista over 4 years
    Hello Manu, I am trying to implement your solution but I am unable to find the Static files section under the software configuration. How did you enable the virtual paths?
  • w_hile
    w_hile almost 4 years
    Note: When using Amazon Linux 2, the namespace is aws:elasticbeanstalk:environment:proxy:staticfiles. See the ELB docs.
  • Devin Venable
    Devin Venable over 3 years
    Your answer is good but may not be clear enough. 1. STATIC_ROOT is your target directory, when collectstatic will pull found static files. I recommend giving a name like 'static_build' for this value. 2. Run collect static locally and commit all files in static_build to your source tree. 3. Set STATIC FILES value per Virtual Path in AWS or via .ebextensions/django.config. This requires less configuration than trying to configure the server to run collectstatic each time it deploys.