Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Lapicher/c6d686b121a4a71eb5a024eb49d6a72f to your computer and use it in GitHub Desktop.
Save Lapicher/c6d686b121a4a71eb5a024eb49d6a72f to your computer and use it in GitHub Desktop.

Revisions

  1. @kasappeal kasappeal revised this gist Jan 24, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion django-postgresql-gunincorn-nginx-pyenv-ubuntu-16.04.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ This guide shows how to setup a production environment for a Django application
    Install PostgreSQL and Nginx using:

    ```bash
    sudo apt-get install -y postgresql-9.5 postgresql-contrib-9.5 postgresql-server-dev-9.5 nginx git circus make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils
    sudo apt-get install -y postgresql-9.5 postgresql-contrib-9.5 postgresql-server-dev-9.5 nginx git circus make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils python-setuptools
    ```

    ## Creating a user to run the application
  2. @kasappeal kasappeal revised this gist Nov 3, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion django-postgresql-gunincorn-nginx-pyenv-ubuntu-16.04.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ This guide shows how to setup a production environment for a Django application
    Install PostgreSQL and Nginx using:

    ```bash
    sudo apt-get install -y postgresql-9.5 postgresql-contrib-9.5 postgresql-server-dev-9.5 nginx git circus build-essential
    sudo apt-get install -y postgresql-9.5 postgresql-contrib-9.5 postgresql-server-dev-9.5 nginx git circus make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils
    ```

    ## Creating a user to run the application
  3. @kasappeal kasappeal revised this gist Nov 3, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion django-postgresql-gunincorn-nginx-pyenv-ubuntu-16.04.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ This guide shows how to setup a production environment for a Django application
    Install PostgreSQL and Nginx using:

    ```bash
    sudo apt-get install -y postgresql-9.5 postgresql-contrib-9.5 postgresql-server-dev-9.5 nginx git circus
    sudo apt-get install -y postgresql-9.5 postgresql-contrib-9.5 postgresql-server-dev-9.5 nginx git circus build-essential
    ```

    ## Creating a user to run the application
  4. @kasappeal kasappeal created this gist Oct 25, 2016.
    389 changes: 389 additions & 0 deletions django-postgresql-gunincorn-nginx-pyenv-ubuntu-16.04.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,389 @@
    # How to deploy a Django app with PostgreSQL + Gunicorn + Nginx using pyenv on Ubuntu Server 16.04

    This guide shows how to setup a production environment for a Django application using PostgreSQL as database, Gunicorn as application server and Nginx as http server using Ubuntu Server 14.04 as Operative System.

    ## Install PosgreSQL, Nginx, Git and Circus

    Install PostgreSQL and Nginx using:

    ```bash
    sudo apt-get install -y postgresql-9.5 postgresql-contrib-9.5 postgresql-server-dev-9.5 nginx git circus
    ```

    ## Creating a user to run the application

    For security reasons, it's a good practice to run web applications as a user with no root privileges so that, if the web applications allows some code execution due to a bug, the code never will adquire root privileges to hack the server.

    Create the user:

    ```bash
    sudo adduser <app_name>
    ```

    Lock user account to deny users login via username and password to the server:

    ```bash
    sudo passwd -l <app_name>
    ```

    Add the nginx user (usually `www-data`) to the `<app_name>` group to have read privileges:

    ```bash
    sudo adduser www-data <app_name>
    ```

    ## Creating database and allow app user to the database

    We have to create a database to store all application info and allow the `<app_name>` user to acces that database.

    Creating database user:

    ```bash
    sudo -u postgres createuser <app_name>
    ```

    Creating database a giving `<app_name>` access to it:

    ```bash
    sudo -u postgres createdb <app_name> -O <app_name>
    ```

    ## Becoming `<app_name>` user

    We have to clone the application in the `<app_name>` user home as the home user.

    Due to we locked the `<app_name>` user, we cannot login to the server with that username.

    Instead, we're going to become the `<app_name>` user from our root user by using:

    ```bash
    sudo -u <app_name> -i
    ```

    Now, we are logged as the `<app_name>` user and in the `<app_name>` user home. We can check it with `pwd`:

    ```bash
    pwd
    /home/<app_name>
    ```

    ## Install pyenv and pyenv-virtualenv

    `pyenv` allow us to install any Python's version in our user account. We are going to user `pyenv` to create a virtualenvironment with the Python's version we need.

    To install `pyenv` and `pyenv-virtualenv` we need to checkout the project from github:

    ```bash
    git clone https://github.com/yyuu/pyenv.git ~/.pyenv
    git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
    ```

    After that, we need to update our `.bash_profile` to make pyenv work properly:

    ```bash
    echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
    echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
    echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
    echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile
    ```

    Now we need to logout and login again as `<app_name>` user_

    ```bash
    logout
    sudo -u <app_name> -i
    ```

    ## Installing our Python version and creating a virtualenv

    ### Installing Python's version needed

    To install the Python's version needed to run our app we can use `pyenv`:

    ```bash
    pyenv install <python_version>
    ```

    Example:

    ```bash
    pyenv install 3.5.2
    ```

    Note that the Python's version will be installed in: `/home/<app_name>/.pyenv/versions/<python_version>`

    ### Creating a virtualenv using the Python's version installed

    Now we can use 'pyenv-virtualenv' to create a virtualenv based on the installed Python's version:

    ```bash
    pyenv virtualenv <python_version> <virtualenv_name>
    ```

    Example:

    ```bash
    pyenv virtualenv 3.5.2 env
    ```

    Note that the virtualenv will be created in: `/home/<app_name>/.pyenv/versions/<virtualenv_name>`

    ## App scaffolding and setup


    ### Clonning the app

    Now we can clone our Django project using `git` in a folder named `app` (this name is optional, obviously):

    ```bash
    git clone <repo_url> app
    ```

    ### Logs folder

    We will create a folder called `logs` to store all logs generated by the application:

    ```bash
    mkdir logs
    ```

    ### Production settings

    As a good practice, we must use a different settings for development and production because **we must not store production data in the repo**.

    So that, we will create a `settings_production.py` to run the application in production:

    ```bash
    nano app/<app_name>/settings_production.py
    ```

    And we will all the settings needed for production but using environment variables (set by a root-privileged user):

    ```python
    import os
    from settings import *

    DEBUG = False
    TEMPLATE_DEBUG = False

    ALLOWED_HOSTS = ['*']

    SECRET_KEY = os.environ.get('SECRET_KEY', SECRET_KEY)

    ADMINS = ((
    os.environ.get('ADMIN_EMAIL_NAME', ''),
    os.environ.get('ADMIN_EMAIL_ADDRESS', '')
    ),)

    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': os.environ.get('DB_NAME', ''),
    'USER': os.environ.get('DB_USER', '')
    }
    }

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

    MEDIA_ROOT = os.path.join(BASE_DIR, os.environ.get('MEDIA_ROOT', "media/"))
    MEDIA_URL = os.environ.get('MEDIA_URL', "/media/")

    ```

    ### Installing project's dependencies and gunicorn in virtualenv

    Now we have to activate the virtualenv:

    ```bash
    pyenv activate env
    ```

    Befor install any package, we should update pip:

    ```bash
    pip install --upgrade pip
    ```

    Now we can install project dependencies:

    ```bash
    pip install -r app/requirements.txt
    ```

    And gunicorn:

    ```bash
    pip install gunicorn
    ```


    ### Aplying migrations, collecting static files and create super user

    Once we have all dependencies installed, we have to apply Django migrations to our database and collect static files to be served from Nginx as static content.

    But before that, we have to export the environment variables related to the database in order to be loaded from the `settings_production file`:

    ```bash
    export SECRET_KEY=<secret_key>
    export DB_NAME=<app_name>
    export DB_USER=<app_name>
    export DJANGO_SETTINGS_MODULE=<app_name>.settings_production
    ```

    Now, access to `app` folder:

    ```bash
    cd app
    ```

    Apply migrations:

    ```bash
    python manage.py migrate
    ```

    Collect static files:

    ```bash
    python manage.py collectstatic --noinput
    ```

    Create super user:

    ```bash
    python manage.py createsuperuser
    ```
    ## Deactivating virtualenv and logout as app user

    Now we are done with all the stuff as `<app_name>` user, so that we have to deactivate virtualenv with:

    ```bash
    pyenv deactivate
    ```

    and `logout` to become a root-privileged user:

    ```bash
    logout
    ```

    # Setting up circus to run gunicorn

    Now we have to install circus, a process manager written in Django that we will use to run gunicorn as a service.

    ## App gunicorn settings

    Let's create a file in `/etc/circus/conf.d` called `<app_name>.ini` to setup all the gunicorn running configuration:

    ```bash
    sudo nano /etc/circus/conf.d/<app_name>.ini
    ```

    The file must have this settings but replacing `<variables>` for their values:

    ```
    [watcher:<app_name>]
    working_dir = /home/<app_name>/app
    cmd = gunicorn
    args = -w 1 -t 180 --pythonpath=. -b unix:/home/<app_name>/app.sock <app_name>.wsgi
    uid = <app_name>
    numprocesses = 1
    autostart = true
    send_hup = true
    stdout_stream.class = FileStream
    stdout_stream.filename = /home/<app_name>/logs/gunicorn.stdout.log
    stdout_stream.max_bytes = 10485760
    stdout_stream.backup_count = 4
    stderr_stream.class = FileStream
    stderr_stream.filename = /home/<app_name>/logs/gunicorn.stderr.log
    stderr_stream.max_bytes = 10485760
    stderr_stream.backup_count = 4
    [env:<app_name>]
    PATH = /home/<app_name>/.pyenv/versions/env/bin:$PATH
    TERM = rxvt-256color
    SHELL = /bin/bash
    USER = <app_name>
    LANG = en_US.UTF-8
    HOME = /home/<app_name>/app
    PYTHONPATH = /home/<app_name>/.pyenv/versions/env/lib/python<PYTHON_VERSION_WITHOUT_LAST_NUMBER>/site-packages
    DJANGO_SETTINGS_MODULE = <app_name>.settings_production
    SECRET_KEY = <secret_key>
    DB_NAME = <app_name>
    DB_USER = <app_name>
    ```

    Now, we only have to run circus:

    ```bash
    sudo service circus start
    ```

    # Nginx setup

    Ok, let's finish by setting up nginx to catch the traffic for a given domain and redirect it to gunicorn to serve dynamic content.

    Let's create a file in `/etc/nginx/sites-available` called `<app_name>`:

    ```bash
    sudo nano /etc/nginx/sites-available/<app_name>`
    ```

    The file must have this settings but replacing `<variables>` for their values:

    ```
    server {
    listen 80;
    server_name <your_domain>;

    access_log /home/<app_name>/logs/nginx-access.log;
    error_log /home/<app_name>/logs/nginx-error.log;

    root /home/<app_name>/app/;

    client_max_body_size 10M;

    location /static {
    alias /home/<app_name>/app/static;
    }

    location /media {
    alias /home/<app_name>/app/media;
    }

    location / {
    include proxy_params;
    proxy_pass http://unix:/home/<app_name>/app.sock;
    }
    }
    ```
    Now we have to make a symlink from the `sites-available` folder to `sites-enabled` folder:
    ```bash
    sudo ln -s /etc/nginx/sites-available/<app_name> /etc/nginx/sites-enabled/<app_name>
    ```

    And now, test the config with:

    ```bash
    sudo nginx -t
    ```

    You should receive:

    ```
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
    ```

    Ok, let's gonna restart nginx:

    ```bash
    sudo service nginx restart
    ```

    # Here we go!

    Just open `http://<your_domain>/admin/` in your browser!