All Products
Search
Document Center

Elastic Compute Service:Deploy a Django project with Nginx and uWSGI

Last Updated:Sep 19, 2025

On an Elastic Compute Service (ECS) instance, you can deploy a Django project with Nginx to serve static files and uWSGI to manage application processes. This architecture separates dynamic and static content to improve service performance.

How it works

image
  1. The browser sends an HTTP or HTTPS request to the server.

  2. Nginx, acting as the frontend server, receives the incoming request.

    • For static file requests (such as CSS, JavaScript, and images), Nginx serves the files directly. This improves response speed and reduces the load on the Django application.

    • Dynamic requests are forwarded to the uWSGI server.

  3. uWSGI receives the request from Nginx, passes it to the Django application for processing, and provides load balancing.

  4. The Django application handles the business logic (such as querying a database or rendering a template) and returns a response to uWSGI.

Procedure

Step 1: Prepare resources

  1. Create a Virtual Private Cloud (VPC) and a vSwitch.

  2. Create an ECS instance.

    1. Go to the instance purchase page. Select the Custom Launch tab.

    2. Instance type: Use an instance with at least 2 vCPUs and 4 GiB of memory.

    3. Image: Alibaba Cloud Linux 3.

    4. Network and Security Group:

      • Network: Select the VPC you created.

      • Public IP: Select Assign Public IPv4 Address.

      • Security group: Select New Security Group and select HTTP (TCP: 80).

  3. Configure the runtime environment.

    1. Log on to a Linux instance by using Workbench.

    2. Install Nginx and the Python development tools to compile uWSGI.

      The Nginx version in the default repositories is outdated and may pose security risks. Add the official Nginx repository to install the latest stable version.
      # Add the official Nginx source to the system.
      sudo tee /etc/yum.repos.d/nginx.repo <<-'EOF'
      [nginx-stable]
      name=nginx stable repo
      baseurl=http://nginx.org/packages/centos/8/$basearch/
      gpgcheck=1
      enabled=1
      gpgkey=https://nginx.org/keys/nginx_signing.key
      module_hotfixes=true
      EOF
      sudo yum install -y python3.8 python38-devel pcre-devel gcc make nginx
      # Install Django and uWSGI.
      sudo python3.8 -m pip install --upgrade pip wheel -i https://mirrors.aliyun.com/pypi/simple
      sudo python3.8 -m pip install "Django>=4.2,<5.0" "uwsgi>=2.0.23" -i https://mirrors.aliyun.com/pypi/simple
    3. Create the logs, static, and media directories to store logs, static files, and media files, respectively. Create the /run/uwsgi directory to store the socket file.

      sudo mkdir -p /srv/django-app/{logs,static,media}
      sudo mkdir -p /run/uwsgi

Step 2: Deploy the Django application

  1. Create a sample Django project.

    Create a Python virtual environment for your project to isolate dependencies and avoid package version conflicts between different projects.
    sudo /usr/local/bin/django-admin startproject myproject /srv/django-app/
  2. To meet production security and performance requirements, modify the project's configuration file, /srv/django-app/myproject/settings.py.

    • Set the DEBUG attribute to False. This prevents the exposure of sensitive information and code details when an application error occurs.

    • Set the ALLOWED_HOSTS attribute to the public IP address of your instance. If this list is empty, the application cannot start. This is a built-in security measure in Django.

      The IP address must be included in the list as a string, for example, ['xxx.xxx.xxx.xxx']
    • At the end of the file, add STATIC_ROOT = BASE_DIR / 'static' to specify the directory for static files.

  3. Collect the sample project's static files into the directory specified by STATIC_ROOT so that Nginx can access them directly.

    sudo python3.8 /srv/django-app/manage.py collectstatic --noinput
  4. Run the database migration to create the table schema that stores application data, such as user information.

    By default, Django projects use SQLite3, which is not suitable for production environments. This can be replaced with another database by modifying the DATABASES setting in the myproject/settings.py file.
    sudo python3.8 /srv/django-app/manage.py migrate
  5. To log on to the Django admin panel later, create an administrator account for the project.

    sudo python3.8 /srv/django-app/manage.py createsuperuser

Step 3: Configure the uWSGI service

  1. Create the uWSGI configuration file at /etc/django-app.ini. Specify the project path, process model, socket file location, and log file.

    Adjust the processes parameter based on the number of CPU cores in your instance. We recommend setting it to Number of Cores * 2.
    Run the service as a non-privileged user. This ensures that even if the application is compromised, an attacker cannot gain root access.
    [uwsgi]
    # Project configuration
    chdir = /srv/django-app
    module = myproject.wsgi:application
    
    # Process configuration
    master = true
    processes = 4
    threads = 2
    
    # Network and permission configuration
    socket = /run/uwsgi/django-app.sock
    chmod-socket = 666
    chown-socket = root:root
    vacuum = true
    
    # Security and runtime configuration
    uid = root
    gid = root
    die-on-term = true
    
    # Log configuration
    logto = /srv/django-app/logs/uwsgi.log
  2. Start the uWSGI service in the background so it continues to run after you close the terminal.

    Check the uWSGI process status by running the ps aux | grep uwsgi command.
    nohup /usr/local/bin/uwsgi --ini /etc/django-app.ini &
  3. (Optional) To enable the uWSGI service on boot and ensure more reliable service management, manage it with systemd.

    1. Create a startup file: /etc/systemd/system/uwsgi-django-app.service, with the following script.

      [Unit]
      Description=uWSGI service for Django App
      After=network.target
      
      [Service]
      User=root
      Group=root
      RuntimeDirectory=uwsgi
      ExecStart=/usr/local/bin/uwsgi --ini /etc/django-app.ini
      Restart=always
      KillSignal=SIGQUIT
      Type=notify
      NotifyAccess=all
      
      [Install]
      WantedBy=multi-user.target
    2. Start the uWSGI service and enable it on boot.

      sudo systemctl daemon-reload
      sudo systemctl start uwsgi-django-app
      sudo systemctl enable uwsgi-django-app

      Check the service status.

      sudo systemctl status uwsgi-django-app

Step 4: Configure Nginx as a frontend server

  1. In the configuration below, replace <server_ip> with your instance's public IP address and save it as the Nginx configuration file: /etc/nginx/conf.d/django-app.conf.

    # Define an upstream that points to the uWSGI socket file.
    upstream django_backend {
        server unix:/run/uwsgi/django-app.sock;
    }
    
    server {
        listen 80;
        server_name <server_ip>; # Replace with the public IP address.
        charset utf-8;
        client_max_body_size 20M;
    
        # Static file service
        location /static/ {
            alias /srv/django-app/static/;
        }
    
        # Media file service
        location /media/ {
            alias /srv/django-app/media/;
        }
    
        # Proxy all other requests to the Django application.
        location / {
            include /etc/nginx/uwsgi_params;
            uwsgi_pass django_backend;
        }
    }
  2. Run the nginx -t command to verify the Nginx configuration syntax.

    Output containing syntax is ok and test is successful indicates a valid configuration.

  3. Restart Nginx to apply the new configuration and enable it on boot.

    sudo systemctl restart nginx
    sudo systemctl enable nginx
  4. Access the sample project's login page at http://<your_ECS_instance_public_IP_address>/admin.

Apply in production

  • Run the service as a non-privileged user: Create a system user with no login permissions to run the application. This isolates permissions and enhances server security. Even if a vulnerability in the application code is exploited, an attacker cannot gain root access.

    # Create a system user (django-app) with no logon permissions.
    sudo useradd --system --shell /bin/false --home /srv/django-app django-app
    # Change the ownership of the application directory to the django-app user.
    sudo chown -R django-app:django-app /srv/django-app
  • Use a Python virtual environment: Create a separate Python virtual environment for each project to isolate dependencies and avoid package version conflicts. This makes the deployment environment cleaner, more predictable, and easier to reproduce.

    # Create a virtual environment.
    python3.8 -m venv venv
    # Activate the virtual environment.
    source venv/bin/activate

Troubleshooting

502 Bad Gateway error

This error typically indicates that Nginx cannot communicate with uWSGI.

  • Check the uWSGI service status: systemctl status uwsgi-django-app.

  • View the uWSGI logs: tail -f /srv/django-app/logs/uwsgi.log.

  • Check if the socket file exists and verify its permissions: ls -l /run/uwsgi/django-app.sock.

403 Forbidden error when accessing static files

This issue occurs because the Nginx process lacks permission to read the static files directory.

  • Ensure that the user running Nginx (typically nginx on Alibaba Cloud Linux) has read (r) and execute (x) permissions for the /srv/django-app/static/ directory and all its subdirectories and files.

500 Internal server error

This error indicates that an internal error occurred in the Django application.

  1. Enable debug mode to identify the problem: Temporarily set DEBUG to True in the project's settings.py file, then restart the uWSGI service and refresh the page. The browser displays a detailed stack trace that pinpoints the issue.

    Important

    This should only be done for debugging. After resolving the issue, change DEBUG back to False to prevent the exposure of sensitive information.

  2. Check the uWSGI logs: The error is also recorded in the uWSGI log file. View the logs to find the specific error thrown by Django.