• Stars
    star
    154
  • Rank 242,095 (Top 5 %)
  • Language
  • Created about 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A how-to document outlining how to publish a django website equipped with WebSockets using Django Channels.
  1. What's Covered in this Document
  2. Create Digital Ocean Droplet with SSH login
  3. Install Server Dependencies
  4. Publish your Project to Github
  5. Hosting Static Files with Digital Ocean Spaces
  6. Creating systemd Socket and Service Files for Gunicorn
  7. DEBUGGING
  8. Install and Setup Redis
  9. ASGI for Hosting Django Channels as a Separate Application
  10. Deploying Django Channels with Daphne & Systemd
  11. Starting the daphne Service when Server boots
  12. Domain Setup
  13. Create a superuser
  14. Finishing up
  15. FAQ
  16. References

What's Covered in this Document

Everything involved in publishing a django website equipped with WebSockets using Django Channels.

I use Digital Ocean as the hosting provider. They have amazing products, documentation and customer support. I highly recommend them. Get $100 free with this referral link: Get $100 Free some Digital Ocean.

Keep in mind this document is meant to be followed after watching my course where I show you how to build a real-time chat website. You can check out the course here: Real-time Chat Messenger course.

Specifications

  1. Ubuntu 20.04
  2. PostgreSQL
  3. Django Channels 2
  4. Digital Ocean Spaces (Static files hosted through AWS)
  5. Run Django project as WSGI using gunicorn and systemd
  6. Configure Nginx to Proxy Pass to Gunicorn (protect from attackers)
  7. Configuring the Firewall
  8. Redis Install and Config
  9. ASGI for Hosting Django Channels
  10. Deploying Django Channels with Daphne & Systemd (running the ASGI app)
    1. Starting the daphne service
    2. Writing a bash script that tells daphne to start
    3. Configuring systemd to execute bash script on server boot
  11. HTTPS with letsencrypt

Create Digital Ocean Droplet with SSH login

Create a new droplet


Droplet configuration


SSH key

Make sure to choose the SSH key option for authentication. Otherwise hackers can simply try passwords to log into your server. This is very bad. Using an SSH key is much, much more secure.

To create an SSH key just click the button "New SSH key" and follow the instructions. Make sure to save a backup of the private key and public key. I usually save on an external drive along with on my PC.


Finish up


Your IP

Write down your server ip somewhere. You'll need this for logging into your server.


Log into Droplet with SSH and FTP

Personally I like to use MobaXterm (it's free) to log into my servers. It's great because you can SSH and FTP from the same window. It's very convenient.

Create a new session


Choose SSH


SSH Settings

  1. Set the server ip
  2. set "root" as username
  3. Under "Advanced SSH settings":
    1. click "use private key" and choose the location of where you saved your private key.

Connected

Now connect and it should look like this. There's an FTP on the left and SSH on the right.


Install Server Dependencies

Run these commands in the SSH terminal.

passwd Set password for root. I'm not sure what the default is.

sudo apt update

sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl

sudo -u postgres psql

CREATE DATABASE django_db;

CREATE USER django WITH PASSWORD 'password';

ALTER ROLE django SET client_encoding TO 'utf8';

ALTER ROLE django SET default_transaction_isolation TO 'read committed';

ALTER ROLE django SET timezone TO 'UTC';

GRANT ALL PRIVILEGES ON DATABASE django_db TO django;

\q

sudo -H pip3 install --upgrade pip

sudo -H pip3 install virtualenv

sudo apt install git-all

sudo apt install libgl1-mesa-glx Resolve cv2 issue

adduser django

su django

cd /home/django/

mkdir CodingWithMitchChat You can replace "CodingWithMitchChat" with your project name.

cd CodingWithMitchChat

virtualenv venv

source venv/bin/activate

mkdir src

Publish your Project to Github

  1. Log into Github.com
  2. Create a new repository https://github.com/new
  3. Open a cmd prompt to your local project directory
    • Ex: D:\DjangoProjects\ChatServerPlayground\venv\src
  4. git init
  5. Update gitignore. I suggest copying mine: https://github.com/mitchtabian/Codingwithmitch-Chat/blob/master/.gitignore
  6. git add .
  7. git commit -m "first commit"
  8. git remote add origin https://github.com/mitchtabian/testoinggfmdgmdfk.git Replace with your git project url.
  9. git push -u origin master

Create a "production" branch

The production branch requires different settings. For example things like static files will be served from AWS via 'Digital Ocean Spaces'. So our static file configuration completely changes compared to development.

Because of this, I recommend maintaining a "prod" branch. We will deploy the prod branch to the server.

git checkout master
git checkout -b prod
git push origin prod

We will come back to the prod branch and make changes to it before we publish.

Hosting Static Files with Digital Ocean Spaces

  1. Create new Digital Ocean Space with the name "open-chat-dev"
  2. Create a API key here https://cloud.digitalocean.com/account/api/tokens
  3. Create a folder inside the space and call it "open-chat-static"

Update 'prod' branch

Make the necessary changes to the prod branch.

settings.py

Top of the file:

from pathlib import Path
from decouple import config

DEBUG = config('DEBUG', default=False, cast=bool)

ALLOWED_HOSTS = ["<ip_from_digital_ocean>",]

ROOT_URLCONF = f'{config("PROJECT_NAME")}.urls'

WSGI_APPLICATION = f'{config("PROJECT_NAME")}.wsgi.application'

ASGI_APPLICATION = f'{config("PROJECT_NAME")}.routing.application'

Bottom of the file:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': config("DB_NAME"),
        'USER': config("DB_USER"),
        'PASSWORD': config("DB_PASSWORD"),
        'HOST': 'localhost',
        'PORT': '',
    }
}

AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME')
AWS_S3_ENDPOINT_URL = config('AWS_S3_ENDPOINT_URL')
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = config('AWS_LOCATION')

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

#STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
STATIC_URL = 'https://%s/%s/' % (AWS_S3_ENDPOINT_URL, AWS_LOCATION)
TEMP = os.path.join(BASE_DIR, 'temp')
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'


EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'CodingWithMitch Team <[email protected]>'


BASE_URL = "http://<ip_from_digital_ocean>"

manage.py

#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from decouple import config


def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', f'{config("PROJECT_NAME")}.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

Create settings.ini file in root directory

[settings]
DEBUG=False
SECRET_KEY=e9lgp7glzo&n(l3v&jkwhyt8ye*!o=cwh7y6o@b2a^$muup!#1

AWS_ACCESS_KEY_ID=AHPYOBHNGF4FSKE2TXD7
AWS_SECRET_ACCESS_KEY=FJ/i1oP13VJQTj6xZhNnbB6p0jdTGiW0G9va6IkXj58
AWS_STORAGE_BUCKET_NAME=open-chat-xyz-demo
AWS_S3_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
AWS_LOCATION=open-chat-static

DB_NAME=django_db
DB_USER=django
DB_PASSWORD=password

EMAIL_HOST_USER=<[email protected]>
EMAIL_HOST_PASSWORD=<password>

PROJECT_NAME=CodingWithMitchChat

Update directory names

  1. Change ChatServerPlayground to CodingWithMitchChat
    • This is very important. If you don't have the correct directory names the services we run use to run the django application on the server will not work!

Update header.html

The WebSockets will be communicating through port 8001 (we will configure this later). So make sure in all the Javascript WebSockets you are referencing port 8001.

var ws_path = ws_scheme + '://' + window.location.host + ":8001/"; // PRODUCTION

Update base.html

I forgot to add jQuery. You can get it here https://cdnjs.com/libraries/jquery. Or just add this to base.html in the "body" section. Do not get it from https://getbootstrap.com/. They have the slim version which is not what we want.

<!-- jquery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js" integrity="sha512-WNLxfP/8cVYL9sj8Jnp6et0BkubLP31jhTG9vhL/F5uEZmg5wEzKoXp1kJslzPQWwPT1eyMiSxlKCgzHLOTOTQ==" crossorigin="anonymous"></script>

Add default_profile_image.png to /static/

Inside /media_cdn/ is a file named default_profile_image.png. Move that into /static/.

Remove unnecessary directories

  1. Delete /static_cdn/
  2. Delete /media/
  3. Delete /media_cdn/
  4. Delete all migrations
    • Leave the __init__.py file. Just delete any other migrations.

Update requirements.txt

Make sure to copy my requirements.txt file so you have all the necessary dependencies: requirements.txt.

This requirements.txt file has some extra dependencies that we didn't have in development. They can all be installed manually using pip but I added them to requirements.txt so you wouldn't have to. This is what was added:

  1. gunicorn psycopg2-binary (required for postgres)
  2. django-storages (required for Digital ocean spaces)
  3. boto3 (required for Digital ocean spaces)
  4. python-decouple (required for settings.ini file)

A Problem with how Django interprets static files in javascript

This is a weird thing that happens if you set static files in javascript with django. HTML symbols get translated into their respective code.

Example

& becomes &amp;

Therefore 

https://<some-domain>/&Signature=3454v535435

Becomes

https://<some-domain>/&amp;Signature=3454v535435

This is a problem because the image will not load if the & symbol becomes&amp;. So we need need to fix a couple files for production.

public_chat.html

Change

profileImage.src = "{% static 'codingwithmitch/dummy_image.png' %}"

To

profileImage.src = "{% static 'codingwithmitch/dummy_image.png' %}".replace(/&amp;/g, "&")

.replace(/&amp;/g, "&") means: "Find all the occurances of '&' and replace it with '&'."

account.html

Change

<img class="d-block border border-dark rounded-circle img-fluid mx-auto profile-image" alt="codingwithmitch logo" id="id_profile_image" src="{{profile_image}}">

To

<img class="d-block border border-dark rounded-circle img-fluid mx-auto profile-image" alt="codingwithmitch logo" id="id_profile_image" src="{% static 'codingwithmitch/dummy_image.png' %}">

And down at the bottom make sure to preload the image.

preloadImage("{{profile_image|safe}}", 'id_profile_image')

Push code changes to remote

git add .
git commit -m "update prod"
git push origin prod

Get the Code on the Server

Open MobaXterm and log into your server via SSH.

su django

source venv/bin/activate

cd src

git init

git pull https://github.com/mitchtabian/Codingwithmitch-Chat.git prod Or whatever your git url is

pip install -r requirements.txt

python manage.py collectstatic

At this point you can check in the Digital Ocean spaces console and you should see the static files have been placed there.


Check if you can run your project (TEST)

su root

sudo ufw allow 8000

su django

source venv/bin/activate

cd src

python manage.py makemigrations

python manage.py migrate

python manage.py runserver 0.0.0.0:8000

visit http://<your_ip_address>:8000/

CTRL+C

Creating systemd Socket and Service Files for Gunicorn

We've tested to see if the application will run if we run the app manually, but this isn't how we want it to be running. We want the application to run in a service so it automatically starts/restarts when it needs to. Like when we restart the server or it goes down for some reason.

One way you can do this is with gunicorn. Run this command you'll see that gunicorn can run the application:

gunicorn --bind 0.0.0.0:8000 CodingWithMitchChat.wsgi

visit http://<your_ip_address>:8000/

So we just need a service to run that command when the server starts. One way to do that is using systemd

CTRL+C

Configure systemd to execute gunicorn via a gunicorn.socket file

su root

Navigate to /etc/systemd/system/

Create a file named: gunicorn.socket

Add the following to the file and save:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Create gunicorn service to run the WSGI application (the django app)

create new file: gunicorn.service

Add the following to gunicorn.service and save. It's very important to copy this exactly as I have. Also your directory structure inside /home/django/ must be EXACTLY the same as mine. Otherwise this service file won't know what project you're talking about.

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=django
Group=www-data
WorkingDirectory=/home/django/CodingWithMitchChat/src
ExecStart=/home/django/CodingWithMitchChat/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          CodingWithMitchChat.wsgi:application

[Install]
WantedBy=multi-user.target

sudo systemctl start gunicorn.socket

sudo systemctl enable gunicorn.socket

Helpful Commands

  1. sudo systemctl daemon-reload
    • Must be executed if you change the gunicorn.service file.
  2. sudo systemctl restart gunicorn
    • If you change code on your server you must execute this to see the changes take place.
  3. sudo systemctl status gunicorn
  4. sudo shutdown -r now
    • restart the server

Configure Nginx to Proxy Pass to Gunicorn

We'll be using Nginx as an HTTP Proxy. It helps to protect our website from attackers. You can read more about it here https://docs.gunicorn.org/en/stable/deploy.html. We need to configure Nginx and gunicorn to work together.

Navigate to /etc/nginx/sites-available

Create file CodingWithMitchChat

server {
    server_name <your_ip_address>;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/django/CodingWithMitchChat/src;
    }
    
     location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
    

}

Update Nginx config file at /etc/nginx/nginx.conf so we can upload large files (images)

http{
	...
	client_max_body_size 10M; 
}

Configure the Firewall

sudo ln -s /etc/nginx/sites-available/CodingWithMitchChat /etc/nginx/sites-enabled

sudo nginx -t

sudo systemctl restart nginx

sudo ufw delete allow 8000

sudo ufw allow 'Nginx Full'

sudo systemctl restart gunicorn

Set DEBUG=False in settings.ini if it isn't already.

service gunicorn restart (There is no difference between this command and sudo systemctl restart gunicorn)

Restart the server sudo shutdown -r now

Visit http://<your_ip_address>/

DEBUGGING

Here are some commands you can use to look at the server logs. These commands are absolutely crucial to know. If your server randomly isn't working one day, this is what you use to start debugging.

  1. sudo journalctl is where all the logs are consolidated to. That's usually where I check.
  2. sudo tail -F /var/log/nginx/error.log View the last entries in the error log
  3. sudo journalctl -u nginx Nginx process logs
  4. sudo less /var/log/nginx/access.log Nginx access logs
  5. sudo less /var/log/nginx/error.log Nginx error logs
  6. sudo journalctl -u gunicorn gunicorn application logs
  7. sudo journalctl -u gunicorn.socket check gunicorn socket logs

Install and Setup Redis

Redis is used as a kind of "messaging queue" for django channels. Read more about it here https://channels.readthedocs.io/en/stable/topics/channel_layers.html?highlight=redis#redis-channel-layer

sudo apt install redis-server

Navigate to /etc/redis/

open redis.conf

CTRL+F to find 'supervised no'

change 'supervised no' to 'supervised systemd'

SAVE

sudo systemctl restart redis.service

sudo systemctl status redis

Should see this:


CTRL+C to exit.

sudo apt install net-tools

Confirm Redis is running at 127.0.0.1. Port should be 6379 by default.

sudo netstat -lnp | grep redis

sudo systemctl restart redis.service

ASGI for Hosting Django Channels as a Separate Application

From the Django channels docs:

ASGI (Asynchronous Server Gateway Interface), is the specification which Channels are built upon, designed to untie Channels apps from a specific application server and provide a common way to write application and middleware code.

su django

Create file named asgi.py in /home/django/CodingWithMitchChat/src/CodingWithMitchChat with this command:

cat > asgi.py 'django' must be the owner of this file.

Paste in the following:

"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""

import os
import django
from decouple import config
from channels.routing import get_default_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", f'{config("PROJECT_NAME")}.settings')
django.setup()
application = get_default_application()

CTRL+D to save.

You can open the file to confirm everything looks good.

ls -l to check ownership. django needs to be the owner.


Deploying Django Channels with Daphne & Systemd

Gunicorn is what we use to run the WSGI application - which is our django app. To run the ASGI application we need something else, an additional tool. Daphne was built for Django channels and is the simplest. We can start daphne using a systemd service when the server boots, just like we start gunicorn and then gunicorn starts the django app.

Here are some references I found helpful. The information on this is scarce:

  1. https://channels.readthedocs.io/en/latest/deploying.html
  2. https://stackoverflow.com/questions/50192967/deploying-django-channels-how-to-keep-daphne-running-after-exiting-shell-on-web

su root

apt install daphne

Navigate to /etc/systemd/system/

Create daphne.service. Notice the port is 8001. This is what we need to use for our WebSocket connections in the templates.

[Unit]
Description=WebSocket Daphne Service
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/home/django/CodingWithMitchChat/src
ExecStart=/home/django/CodingWithMitchChat/venv/bin/python /home/django/CodingWithMitchChat/venv/bin/daphne -b 0.0.0.0 -p 8001 CodingWithMitchChat.asgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target

systemctl daemon-reload

systemctl start daphne.service

systemctl status daphne.service

You should see something like this. If you don't, go back and redo this section. Check that your filepaths are all exactly the same as mine in daphne.service. That is the #1 reason people have issues.


CTRL+C

Starting the daphne Service when Server boots

With gunicorn and the WSGI application, we created a gunicorn.socket file that tells gunicorn to start when the server boots (at least this is my understanding). I couldn't figure out how to get this to work for daphne so instead I wrote a bash script that will run when the server boots.

Create the script to run daphne

Navigate to /root

create boot.sh

#!/bin/sh
sudo systemctl start daphne.service

Save and close.

Might have to enable it to be run as a script (not sure if this is needed) chmod u+x /root/boot.sh

If you want to read more about shell scripting, I found this helpful: https://ostechnix.com/fix-exec-format-error-when-running-scripts-with-run-parts-command/.

Tell systemd to run the bash script when the server boots

Navigate to /etc/systemd/system

create on_boot.service

[Service]
ExecStart=/root/boot.sh

[Install]
WantedBy=default.target

Save and close.

systemctl daemon-reload

Start it

sudo systemctl start on_boot

Enable it to run at boot

sudo systemctl enable on_boot

Allow daphne service through firewall

ufw allow 8001

Restart the server

sudo shutdown -r now

Check the status of on_boot.service

systemctl status on_boot.service

Should see this. If not, check logs: sudo journalctl -u on_boot.service


Check if the daphne service started when the server started:

systemctl status daphne.service

Should see this. If not, check logs: sudo journalctl -u daphne.service


Where are the logs?

journalctl is my general go-to. You can filter specifically for a service like this:

sudo journalctl -u on_boot.service // for on_boot.service
sudo journalctl -u daphne.service // for daphne.service

Domain Setup

If you want a custom domain name (which probably everyone does), this section will take you through how to do that.

Purchasing a domain

I like to use namecheap.com but it doesn't matter where you buy it from.

Point DNS to Digital Ocean

On the home screen, click the "manage" button on the domain you purchased.


In the "nameservers" section, select "custom DNS" and point to digital ocean.


Add the Domain in Digital Ocean

Select your project in digital ocean and click "add domain" on the right.


Fill in your domain name.


Add the following DNS records. Replace open-chat.xyx with your domain name. And you can ignore the CDN.


Update Nginx config

Earlier we configured Nginx to proxy pass to gunicorn. We need to add the new domain to that configuration.

visit /etc/nginx/sites-available

Update CodingWithMitchChat

server {
    server_name 157.245.134.6 open-chat-demo.xyz www.open-chat-demo.xyz;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/django/CodingWithMitchChat/src;
    }
    
     location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

sudo systemctl reload nginx

Make sure nginx configuration is still good.

sudo nginx -t

Update ALLOWED_HOSTS

Navigate to /home/django/CodingWithMitchChat/src/CodingWithMitchChat/

Update settings.py with the domain you purchased. Also make sure your ip is correct.

ALLOWED_HOSTS = ["157.245.134.6", "open-chat-demo.xyz", "www.open-chat-demo.xyz"]

Apply the changes

service gunicorn restart

TIME TO WAIT...

It can take some time to see your website available at the custom domain. I don't really know how long this will actually take. I waited about an hour and it was working for me.

How do you know it's working?

Visiting your domain you should see this OR you should see your project live and working.


HTTPS (If you have a domain registered and it's working)

Do not do this step unless you're able to visit your website using the custom domain. See How do you know it's working?

Install certbot

HTTPS is a little more difficult to set up when using Django Channels. Nginx and Daphne require some extra configuring.

sudo apt install certbot python3-certbot-nginx

sudo systemctl reload nginx

Make sure nginx configuration is still good.

sudo nginx -t

Allow HTTPS through firewall

sudo ufw allow 'Nginx Full'

sudo ufw delete allow 'Nginx HTTP' Block standard HTTP

Obtain SSL certificate

sudo certbot --nginx -d <your-domain.whatever> -d www.<your-domain.whatever>

For me:

sudo certbot --nginx -d open-chat-demo.xyz -d www.open-chat-demo.xyz

Verifying Certbot Auto-Renewal

sudo systemctl status certbot.timer

Test renewal process

sudo certbot renew --dry-run

You should see this


Update CORS in digital ocean

Update for HTTPS in spaces settings


Update settings.py

Set BASE_URL variable in settings.py to your domain name.

Update nginx config

We need to tell nginx to allow websocket data to move through port 8001. I'm not really sure how to explain this. I don't understand it fully. Similar to how we allow gunicorn to proxy pass nginx.

Navigate to /etc/nginx/sites-available

Update CodingWithMitchChat

server {

    ...
    
    location /ws/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8001;
    }

    ...
}

Update daphne.service

Tell daphne how to access our https cert.

Navigate to /etc/systemd/system

Update daphne.service

[Unit]
Description=WebSocket Daphne Service
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/home/django/CodingWithMitchChat/src
ExecStart=/home/django/CodingWithMitchChat/venv/bin/python /home/django/CodingWithMitchChat/venv/bin/daphne -e ssl:8001:privateKey=/etc/letsencrypt/live/open-chat-demo.xyz/privkey.pem:certKey=/etc/letsencrypt/live/open-chat-demo.xyz/fullchain.pem CodingWithMitchChat.asgi:application  
Restart=on-failure

[Install]
WantedBy=multi-user.target

Create a superuser

Before you test the server create a superuser.

su django

cd /home/django/CodingWithMitchChat/

source venv/bin/activate

cd src

python manage.py createsuperuser

Finishing up

Restart the server and visit your website to try it out. Everything should be working now.

If you followed my course remember to create a public chat room from the admin with the title "General".

Thanks for reading and feel free to contribute to this document if you have a better way of explaining things. I am by no means a web expert.

FAQ

Here are some things I wish I knew when doing this for the first time.

If you change a file or pull a code update to the project, do you need to do anything?

Yes.

If you only change code that is not related to django channels then you only need to run service gunicorn restart.

But if you change any code related to django channels, then you must also restart the daphne service: service daphne restart.

To be safe, I always just run both. It can't hurt.

service gunicorn restart
service daphne restart

Service Status Errors

Throughout this document we periodically check the status of the services that we set up. Things like:

  1. sudo systemctl status gunicorn
  2. sudo systemctl status redis
  3. systemctl status daphne.service
  4. systemctl status on_boot.service
  5. sudo systemctl status certbot.timer

If any of these fail, it's not going to work and you've done something wrong. The most common problem is the directory structure does not match up. For example you might use /home/django/django_project/src/ instead of /home/django/CodingWithMitchChat/src/. You need to look very carefully at your directory structures and make sure the naming is all correct and correlates with the .service files you build.

When you make a change to a .service file, Always run sudo systemctl daemon-reload. Or to be safe, just restart the damn server sudo shutdown -r now. Restarting the server is the safe way, but also the slowest way.

CORS error in web console

You are getting an error in web console saying: "No 'Access-Control-Allow-Origin' header is present on the requested resource".

Fix this by adding CORS header in spaces settings. See This image for the configuration.

References

  1. https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-18-04
  2. https://channels.readthedocs.io/en/latest/
  3. https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-20-04
  4. https://www.digitalocean.com/community/tutorials/how-to-set-up-object-storage-with-django
  5. https://stackoverflow.com/questions/61101278/how-to-run-daphne-and-gunicorn-at-the-same-time
  6. conda-forge/pygridgen-feedstock#10
  7. https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04

More Repositories

1

Android-Instagram-Clone

Develop your own Instagram Clone App
Java
921
star
2

Open-API-Android-App

Kotlin, MVI, Hilt, Retrofit2, Coroutines, Room Persistence, REST API, Token Authentication
Kotlin
688
star
3

MVVMRecipeApp

Kotlin, MVVM, Navigation Component, Hilt, Jetpack Compose, Retrofit2
Kotlin
542
star
4

Clean-Notes

Clean Architecture by layer
Kotlin
540
star
5

Dota-Info

Multi-module, Kotlin, MVI, Compose, Hilt, Navigation Component, Use-cases, SQL-Delight, Ktor
Kotlin
391
star
6

Food2Fork-KMM

Kotlin Multiplatform project that gets network data from Food2Fork.ca
Kotlin
369
star
7

Dagger-Examples

Some dagger-android examples with Retrofit2, MVVM architecture, RxJava, (Java)
Java
278
star
8

Dagger-Hilt-Playerground

A playground for learning dagger hilt on android
Kotlin
275
star
9

Google-Maps-2018

Google maps, directions, markers, clusters, custom icons, real-time gps updates (like uber) and more
Java
271
star
10

EspressoUITest-Examples

Examples of UI Testing with Espresso, Mockk, androidx.test
Kotlin
250
star
11

food2fork-compose

Kotlin, MVVM, Navigation Component, Hilt, Jetpack Compose, Retrofit2, Room, Use cases, Unit Testing
Kotlin
225
star
12

Navigation-Components-Example

An example using a single activity, several fragments, animations, arguments, with Navigation Components (Kotlin)
Kotlin
215
star
13

Local-db-Cache-Retrofit-REST-API-MVVM

App that interacts with a REST API using Retrofit. There is a local db cache and architecture is MVVM
Java
187
star
14

CodingWithMitch-Blog-Course

Web development with Django (Python) for Beginners
JavaScript
168
star
15

Video-Player-RecyclerView

Auto play videos in a RecyclerView with ExoPlayer like YouTube does
Java
160
star
16

TabianCustomCamera

Custom camera for android using camera2 api (DEPRECATED)
Java
152
star
17

RestApiMVVM

App that interacts with a Rest Api. Architecture is MVVM.
Java
151
star
18

MVIExample

A simple MVI Architecture example
Kotlin
146
star
19

Spotify-Clone

Audio streaming application with MediaBrowserServiceCompat, ExoPlayer and Firestore
Java
139
star
20

Recyclerview

How to use a recyclerview
Java
131
star
21

Google-Maps-Google-Places

Android Google Maps API & Google Places API Course
Java
130
star
22

CodingWithMitchBlog-REST-API

A continuation of the CodingWithMitchBlog course. Adding a REST API using Django REST-framework
JavaScript
127
star
23

SQLite-for-Beginners-2019

SQLite on Android with Room Persistence Library (beginner course)
Java
120
star
24

Codingwithmitch-Chat

Real-time chat server with Django
JavaScript
117
star
25

MVVMExample1

A simple of example of MVVM in Android with LiveData and MutableLiveData
Java
113
star
26

KMM-Playground

Playground for learning Kotlin Multiplatform Mobile
Kotlin
113
star
27

Sending-and-Receiving-Data-with-Bluetooth

Java
85
star
28

Kotlin-Coroutine-Examples

Examples for using Coroutines
Kotlin
80
star
29

DaggerMultiFeature

Multi-feature app using dagger for learning purposes
Kotlin
70
star
30

ForSale

Android classified applications using ElasticSearch
Java
66
star
31

Unit-Testing-2

Unit Testing with JUnit5, JUnit4, Mockito, MVVM (repository pattern), Room Persistence, RxJava, (Java)
Java
60
star
32

SaveReadWriteDeleteSQLite

How to Save/Read/Write/Delete data from SQLite database Android
Java
57
star
33

Kotlin-RecyclerView-Example

Simple example with a RecyclerView that displays images and text with Kotlin, Glide and CardViews
Kotlin
52
star
34

Kotlin-Singleton-Example

Example of creating Singletons with Kotlin with some MVVM architecture
Kotlin
49
star
35

Bound-Services-with-MVVM

A simple example of how to bind an activity to a service while using MVVM
Java
49
star
36

FirestoreChatApp

Simple real-time chat application using Firestore
Java
46
star
37

TabFragments

How to use Tabs in Android Application
Java
40
star
38

Reddit-RSS-App

how to create a reddit app in Android
Java
39
star
39

Google-Maps-Compose

Example using Google maps with compose. This uses GoogleMap composable and MapEffect.
Kotlin
39
star
40

DataBindingGettingStarted

Getting started with data binding for Android
Java
37
star
41

AppBarLayouts

How to add an App Bar on the bottom and top of your Android app
Java
37
star
42

Bluetooth---How-to-Pair

How to pair with a bluetooth device in your android applications
Java
33
star
43

Retrofit-Caching-Example

An example of how to use Retrofit2 to cache HTTP responses
Java
33
star
44

EspressoDaggerExamples

UI Testing with Jetpack and AndroidX
Kotlin
33
star
45

Flows-and-Channels

Playground for Kotlin Flows and Channels
Kotlin
31
star
46

MVVM-Koin-Repository-Pattern

Experimenting with MVVM, Koin and Repository pattern in a simple TODO app.
Kotlin
30
star
47

Giffit

Kotlin
29
star
48

Dictionary

Dictionary app for Threads course on Pluralsight
Java
27
star
49

Flutter-Recipes-App

This will be a Flutter copy of the repository: "Local-db-Cache-Retrofit-REST-API-MVVM"
Dart
26
star
50

EnableBluetooth

Enabling and disabling Bluetooth in Android Studio
Java
25
star
51

ListViews

How to use ListViews
Java
25
star
52

Kotlin-Multiplatform-Utility

A repository containing useful classes for Kotlin Multiplatform projects
Kotlin
25
star
53

ComposePlayground

Playground for learning Jetpack Compose
Kotlin
25
star
54

FirestoreGettingStarted

Getting started with Firebase Cloud Firestore
Java
23
star
55

RxJava-FlatMap-Example

Getting data from multiple sources using a FlatMap Operator
Java
23
star
56

Bluetooth---Discover-Devices

Discovering bluetooth devices
Java
23
star
57

ABTestLayouts

Serve a Compose UI or an XML UI depending on Firebase remote config.
Kotlin
22
star
58

SwipeMenuListView

Android SwipeMenuListView example
Java
22
star
59

Android-SQLite-Beginner-Course

All source code for the Android SQLite Beginner Course
Java
22
star
60

TabianConsulting

Firebase on Android series on Pluralsight
Java
21
star
61

GesturesGettingStarted

Starting point for Android Gestures introduction
Java
20
star
62

SwipingViewPager

example of using a viewpager to swipe through product variations
Java
18
star
63

DatePickerDialog

how to add a DatePickerDialog popup to your android app
Java
18
star
64

Fragments

How to use Fragments in your Android Apps
Java
18
star
65

UI-Communication-with-MVI

Effective UI Communication with MVI architecture, a BaseActivity and the Repository Pattern.
Kotlin
18
star
66

Pie-Chart-Tutorial

MPAndroidChart Library
Java
17
star
67

FirebaseDirectMessage

Send Firebase Cloud Messages using a Firebase Cloud Function
Java
17
star
68

MotionLayout-Examples

Some examples using Androids new MotionLayout
Kotlin
17
star
69

FlutterPlayground

Playing around with flutter
Dart
16
star
70

Firebase-Read-Database

Learn how to read data from a firebase database
Java
16
star
71

CleanMultiModule

Multi-module playground with Compose, Hilt, Nav Component
Kotlin
16
star
72

OpenChat

Real-time chat android app to communicate with open-chat.xyz.
Kotlin
15
star
73

DialogFragmentToFragment

Capture data from a dialog fragment and send it to a fragment
Java
15
star
74

FirebasePushNotificationTopics

How to send push notifications to specifc topics
Java
15
star
75

ImportFromExcel

How to import data from excel into your Android Application
Java
14
star
76

EditableListView

Java
14
star
77

FragmentToFragmentCommunication

How to communicate between fragments and activities
Java
14
star
78

Android-Apps-with-Kotlin-SharedPreferences-and-Settings-Screens

Pluralsight course: Android Apps with Kotlin: SharedPreferences and Settings Screens
Kotlin
13
star
79

TabianDating

Android Keyboard Inputs: Getting Started
Java
13
star
80

MVI-Beginner-Example

An example using MVI architecture for beginners.
Kotlin
12
star
81

Memory-Leaks-on-Android

Memory leaks example for android
Java
12
star
82

CardView

how to create a CardView and use it in a ListView
Java
12
star
83

CodingWithMitchStore

Source code for Android Gestures Pluralsight Course
Java
12
star
84

ForSale-Starting-Point

A starting point for the ForSale repository
Java
12
star
85

GoogleMaps2018-Test

test application for google maps course
Java
11
star
86

Food2Fork

Project repo for Food2Fork.ca
Python
11
star
87

Retrofit

Retrofit Tutorials for Android
Java
11
star
88

Android-Studio-Settings

information on my android studio settings
11
star
89

Spotify-MVVM-MVI-Showcase

Kotlin
11
star
90

DialogFragmentToActivity

Capture data from a dialog fragment and send it to an activity
Java
11
star
91

Sockets-Playground

Playground for learning how to use sockets on android (realtime chat)
Java
11
star
92

FirebaseOnClickPushNotification

How to open an Activity when a Push Notification is clicked
Java
11
star
93

PhoneTest

Retrieve device properties, display properties and OS
Java
11
star
94

AndroidImageCropper-Example

Android image cropping example with a great 3rd party library
Kotlin
11
star
95

Firebase-Save-Images

How to save images to your firebase database
Java
11
star
96

BottomNav-MultipleBackstacks-NavigationComponent

playground setting up a bottom navigation with multiple backstacks using navigation component
Kotlin
10
star
97

Firebase-Save-User-Information

How to save user information into your firebase database
Java
10
star
98

MirrorTest

Android Developer Test
Java
10
star
99

NoteKeeper

Application for Kotlin course on SharedPreferences and Preferences Framework
Kotlin
10
star
100

Enable-Bluetooth-Discoverability

Enable bluetooth discoverability is android studio
Java
10
star