Search code examples
pythondjangoreactjswebpackpassenger

How to deploy Django/React/Webpack app on Digital Ocean through Passenger/Nginx


I'm trying to deploy a web app built with Django/Redux/React/Webpack on a Digital Ocean droplet. I'm using Phusion Passenger and Nginx on the deployment server.

I used create-react-app to build a Django app which has a frontend that uses React/Redux, and a backend api that uses django-rest-framework. I built the frontend using npm run build.

The Django app is configured to look in the frontend/build folder for its files and everything works as expected, including authentication. It's based on this tutorial: http://v1k45.com/blog/modern-django-part-1-setting-up-django-and-react/

In settings.py:

ALLOWED_HOSTS = ['*']

TEMPLATES = [
... 
       'DIRS': [
            os.path.join(BASE_DIR, 'frontend/build'),
        ],
...
]

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend/build/static'),
]

On my development machine, I activate a Python 3.6 virtual environment and run ./manage.py runserver, and the app is displayed at localhost:3000.

On the deployment server, I've cloned the files into a folder in var/www/ and built the frontend.

I've set up Passenger according to the docs with a file passenger_wsgi.py:

import myapp.wsgi
application = myapp.wsgi.application

And the wsgi.py file is in the djangoapp folder below:

import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myapp.settings')
application = get_wsgi_application()

The Passenger docs only cover a single-part app:

https://www.phusionpassenger.com/library/walkthroughs/start/python.html https://www.phusionpassenger.com/library/walkthroughs/deploy/python/digital_ocean/nginx/oss/xenial/deploy_app.html https://www.phusionpassenger.com/library/deploy/wsgi_spec.html

I've tried cloning the tutorial part 1 code directly onto my server and following the instructions to run it. I got this to work on the server by adding "proxy": "http://localhost:8000" to frontend/package.json. If I run the Django server with ./manage.py runserver --settings=ponynote.production_settings xxx.x.x.x:8000 then the app is correctly served up at myserver:8000. However Passenger is still not serving up the right files.

I have changed wsgi.py to say this:

import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.production_settings")
application = get_wsgi_application()

The page served by Passenger at URL root now appears to have the right links to the js files such as "text/javascript" src="/static/bundles/js/main.a416835a.js, but the links don't work: the expected js is not present. Passenger is failing to serve the js files from static/bundles/js, even though the Django server can find them.

Very grateful for any help or ideas.


Solution

  • The key missing setting was the 'location' setting in the Passenger config file.

    Although the Django server serves up the static files, including the build files for your React app, Nginx doesn't see any static files except those in a 'public' directory.

    So to deploy a Django app built with Webpack to production, you need to tell Nginx about those files. If you're using Passenger, these settings are probably in a separate Passenger config file. 'alias' is the command to use in this case where the folder has a different name from 'static' (which is where the web page links point).

    If you use a virtual environment for your app, you need to specify where Passenger can find the right Python executable.

    /etc/nginx/sites-enabled/myapp.conf

    server {
        listen 80;
        server_name xx.xx.xx.xx;
    
        # Tell Passenger where the Python executable is
        passenger_python /var/www/myapp/venv36/bin/python3.6;
    
        # Tell Nginx and Passenger where your app's 'public' directory is
        # And where to find wsgi.py file
        root /var/www/myapp/myapp/myapp;
    
        # Tell Nginx where Webpack puts the bundle folder 
        location /static/ {
           autoindex on;
           alias /var/www/myapp/myapp/assets/;
        }
    
        # Turn on Passenger
        passenger_enabled on;
    }
    

    Passenger uses the wsgi.py file as an entry point to your app. You need a passenger_wsgi.py file one level above the wsgi.py file. This tells Passenger where to find the wsgi.py file.

    /var/www/myapp/myapp/passenger_wsgi.py

    import myapp.wsgi
    application = myapp.wsgi.application
    

    /var/www/myapp/myapp/myapp/wsgi.py

    If you are using a separate production_settings.py file, make sure this is specified here.

    import os
    from django.core.wsgi import get_wsgi_application
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.production_settings")
    application = get_wsgi_application()