Django Deployment Assistant

First, create the instance in DigitalOcean!
Phase 1: Initial Server Setup
sudo apt update && sudo apt full-upgrade -y sudo reboot adduser USER && usermod -aG sudo USER && sudo usermod -aG www-data
Firewall setup:
ufw app list && ufw allow OpenSSH && ufw enable
SSH Key Migration:
mkdir -p /home/USER/.ssh && cp ~/.ssh/authorized_keys /home/USER/.ssh/ && chown -R USER:USER /home/USER/.ssh
PWless sudo:
sudo visudo
Add last of all:
ALL=(ALL) NOPASSWD: ALL logout
Phase 2: Root Deactivation & Security
Check User (Expected: root):
sudo whoami
Deactivate root:
sudo nano /etc/ssh/sshd_config
Change "#Port 22" to "Port 2244".
Change "PermitRootLogin" to "PermitRootLogin no".
Change "PubkeyAuthentication" to "PubkeyAuthentication yes".
Change "PasswordAuthentication" to "PasswordAuthentication no".
Expected no response:
sudo sshd -t sudo systemctl stop ssh.socket && sudo systemctl disable ssh.socket && sudo systemctl restart ssh sudo ufw allow 2244/tcp && sudo ufw delete allow 22/tcp && sudo ufw delete allow OpenSSH && sudo ufw reload && sudo ufw status sudo passwd -l root && sudo systemctl restart ssh
Check Config:
sudo grep "^Port" /etc/ssh/sshd_config
Check Errors:
sudo sshd -t
Change SSH Active Port:
sudo systemctl stop ssh.socket && sudo systemctl disable ssh.socket && sudo systemctl restart ssh && sudo ss -tulpn | grep ssh
Clear Firewall:
sudo ufw delete allow OpenSSH && sudo ufw delete allow 22/tcp && sudo ufw reload
Install Fail2Ban:
sudo apt update && sudo apt install fail2ban -y && sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local && sudo nano /etc/fail2ban/jail.local
Change:
[sshd] enabled = true port = 2244 filter = sshd logpath = /var/log/auth.log maxretry = 3 findtime = 10m bantime = 1h sudo systemctl restart fail2ban
New Terminal (DON'T CLOSE THIS ONE!): SSH with port 2244
ssh -p 2244 @
Phase 3: Core Dependencies
Install Core Dependencies:
sudo apt update && sudo apt install -y python3-pip python3-dev python3-venv nginx && sudo ufw allow 'Nginx Full'
Set Nginx user to your SSH user:
sudo systemctl stop nginx && sudo nano /etc/nginx/nginx.conf
Change these lines:
http { sendfile on; tcp_nopush on; server_tokens off; proxy_headers_hash_max_size 1024; proxy_headers_hash_bucket_size 128; server_names_hash_bucket_size 128; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } sudo systemctl start nginx
Phase 4: Aliases & Environment
sudo nano ~/.bash_profile PS1=' » ' alias s="source ~/.env/bin/activate" alias d="deactivate" alias r="sudo systemctl restart " alias l="pip3 list" alias o="pip3 list --outdated" alias u="pip3 install -U " alias req="pip3 install -r requirements.txt" alias res="sudo systemctl restart nginx && sudo systemctl restart " alias cs="python3 manage.py collectstatic --noinput" alias run="python3 manage.py runserver 0.0.0.0:8000" alias mkmig="python3 manage.py makemigrations" alias mig="python3 manage.py migrate" alias klear="find . -name \*.pyc -delete && find . -name \*.DS_Store -delete && find . -name \*__pycache__ -delete" alias klearmigs="find . -path \"*/migrations/*.py\" -not -name \"__init__.py\" -delete && find . -path \"*/migrations/*.pyc\" -delete" alias fakemig="python3 manage.py makemigrations && python3 manage.py migrate --fake-initial" . ~/.bash_profile
Python VirtualEnv setup:
python3 -m venv .env && source ~/.env/bin/activate && pip3 install -U pip gunicorn Django
Phase 5: Django Project Setup
django-admin startproject PROJECT
Update ALLOWED_HOSTS in settings.py:
ALLOWED_HOSTS = ['IP', 'localhost', 'DOMAIN'] cd PROJECT && pip3 install -r requirements.txt && deactivate
Phase 6: Gunicorn Systemd
Service Config:
sudo nano /etc/systemd/system/PROJECT.service [Unit] Description=Gunicorn HTTP After=network.target [Service] User= Group=www-data WorkingDirectory=/home// Type=notify UMask=0007 ExecStart=/home//.env/bin/gunicorn \ --workers 5 \ --worker-class uvicorn.workers.UvicornWorker \ --timeout 120 \ --bind 0.0.0.0:8001 \ .asgi:application Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target sudo systemctl start && sudo systemctl enable sudo systemctl daemon-reload && sudo systemctl restart .service sudo systemctl status .service
Service Websocket Config:
sudo nano /etc/systemd/system/PROJECT-ws.service [Unit] Description=Daphne daemon After=network.target [Service] User= Group=www-data UMask=0007 WorkingDirectory=/home// Type=simple ExecStart=/home//.env/bin/daphne \ -b 0.0.0.0 \ -p 8002 \ .asgi:application Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target sudo systemctl start -ws && sudo systemctl enable -ws sudo systemctl daemon-reload && sudo systemctl restart .service -ws.service sudo systemctl status .service sudo systemctl status -ws.service
Phase 7: Nginx Server Block
sudo nano /etc/nginx/sites-available/PROJECT upstream _http { server 127.0.0.1:8001; } upstream _ws { server 127.0.0.1:8002; } server { listen 80; listen [::]:80; server_name www.; return 301 https://$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name www.; ssl_certificate /etc/letsencrypt/live//fullchain.pem; ssl_certificate_key /etc/letsencrypt/live//privkey.pem; return 301 https://$request_uri; } server { listen 443 ssl; http2 on; server_name ; # client_max_body_size 50M; ssl_certificate /etc/letsencrypt/live//fullchain.pem; ssl_certificate_key /etc/letsencrypt/live//privkey.pem; access_log /var/log/nginx/_access.log; error_log /var/log/nginx/_error.log; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; location /ws/ { proxy_pass http://_ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 86400; } location / { proxy_pass http://_http; include proxy_params; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Referer $http_referer; proxy_read_timeout 360s; proxy_connect_timeout 360s; proxy_send_timeout 360s; } # location /static/ { # root /home//bucket/; # } # location /media/ { # root /home//bucket/; # } } sudo ln -s /etc/nginx/sites-available/PROJECT /etc/nginx/sites-enabled sudo nginx -t && sudo systemctl daemon-reload && sudo systemctl restart nginx && sudo systemctl status nginx.service
Phase 8: Database
Connect to the DB with the user doadmin in TablePlus.
Database Permissions:
ALTER SCHEMA public OWNER TO DB_USER; GRANT ALL PRIVILEGES ON DATABASE DB_NAME TO DB_USER;
Alter DNS for "media." on DigitalOcean » Networking » Domains to point to the Droplet.
Phase 9: SSL
Certbot Installation:
sudo snap install --classic certbot && sudo ln -s /snap/bin/certbot /usr/bin/certbot sudo certbot --nginx -d DOMAIN -d www.DOMAIN
Certbot Renewal:
sudo systemctl status snap.certbot.renew.service && sudo certbot renew --dry-run &&sudo systemctl reload nginx
List certificates:
sudo certbot certificates
Delete certificates:
sudo certbot delete --cert-name DOMAIN
Verify Domain Configuration: