CentOS Server Preparation

This guide prepares a fresh CentOS server to run a Django project in production using uWSGI and Nginx.

Access the Server

Set permissions on your SSH private key and connect to the server:

chmod 400 file_name.pem
ssh -i path-to-your-key.pem ec2-user@your-server-ip

Initial System Setup

Update packages:

sudo dnf -y update

Enable the extra package repositories and install build tools:

sudo dnf -y install epel-release
sudo dnf config-manager --set-enabled crb
sudo dnf -y groupinstall "Development Tools"

Install base tools:

sudo dnf -y install nano wget git nginx gcc cmake curl firewalld policycoreutils-python-utils

Enable Nginx at boot and start it now:

sudo systemctl enable --now nginx

Enable and start firewall:

sudo systemctl enable --now firewalld

Allow HTTP and HTTPS through the firewall:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Optional: Passwordless Sudo

Open the sudoers file safely:

sudo EDITOR=nano visudo

Add this line at the bottom (replace username):

username ALL=(ALL) NOPASSWD: ALL

Generate SSH Keys (ED25519)

Generate a dedicated SSH key on the server and add it to the SSH agent:

ssh-keygen -t ed25519 -a 100 -C "shahed@aws-server" -f ~/.ssh/id_ed25519_[project_name]

Optional: configure SSH for GitHub key selection:

nano ~/.ssh/config
Host *
   AddKeysToAgent yes
Host github-[project_name]
   HostName github.com
   User git
   IdentityFile ~/.ssh/id_rsa_[project_name]

Optional: add your local public key to the server authorized keys:

sudo nano ~/.ssh/authorized_keys

Install Dependencies

sudo dnf -y install bzip2-devel cairo cairo-devel cryptopp-devel \
expat-devel gdal gdal-devel libffi-devel libpcap-devel libcurl-devel \
libxml2-devel ncurses-devel openssl-devel proj proj-devel python3-devel \
pango pango-devel readline-devel sqlite sqlite-devel tk-devel \
xz-devel xerces-c-devel zlib-devel gcc python3 python3-pip

If you need Python 3.8.15 specifically, build it from source:

cd /tmp/ && wget https://www.python.org/ftp/python/3.8.15/Python-3.8.15.tgz &&
tar xzf Python-3.8.15.tgz && cd Python-3.8.15 && ./configure --enable-optimizations &&
make && sudo make altinstall && python3.8 --version

If your project uses PostgreSQL client libraries, also install:

sudo dnf -y install postgresql-devel

Configure Django Environment

Create an environment file:

nano /var/www/myproject/.env

Example values:

DEBUG=False
SECRET_KEY=change-me
ALLOWED_HOSTS=example.com,www.example.com
DATABASE_URL=postgres://user:password@127.0.0.1:5432/dbname

Make sure your Django settings load these values (for example with python-decouple or django-environ).

Run Django Production Tasks

cd /var/www/myproject
source venv/bin/activate
python manage.py migrate
python manage.py collectstatic --noinput
python manage.py check --deploy

Configure uWSGI Service

Create a systemd unit file:

sudo nano /etc/systemd/system/uwsgi-myproject.service

Use this template (replace myproject and config.wsgi):

[Unit]
Description=uWSGI daemon for Django project
After=network.target

[Service]
User=django
Group=nginx
WorkingDirectory=/var/www/myproject
EnvironmentFile=/var/www/myproject/.env
RuntimeDirectory=uwsgi
RuntimeDirectoryMode=0755
ExecStart=/var/www/myproject/venv/bin/uwsgi \
    --master \
    --processes 3 \
    --chdir /var/www/myproject \
    --module config.wsgi:application \
    --home /var/www/myproject/venv \
    --socket /run/uwsgi/myproject.sock \
    --chmod-socket=660 \
    --vacuum \
    --die-on-term
Restart=always
KillSignal=SIGQUIT
Type=simple

[Install]
WantedBy=multi-user.target

Enable and start uWSGI:

sudo systemctl daemon-reload
sudo systemctl enable --now uwsgi-myproject
sudo systemctl status uwsgi-myproject

Configure Nginx

Create Nginx server config:

sudo nano /etc/nginx/conf.d/myproject.conf

Example configuration:

server {
    listen 80;
    server_name example.com www.example.com;

    location /static/ {
        alias /var/www/myproject/static/;
    }

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/run/uwsgi/myproject.sock;
    }
}

Test and start Nginx:

sudo nginx -t

Install Certbot (Let’s Encrypt)

Install Certbot using Snap and issue certificates with the Nginx plugin:

sudo dnf install snapd -y
sudo systemctl enable --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap
sudo snap install core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx

Firewall and SELinux

Allow HTTP/HTTPS:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Allow Nginx to connect to uWSGI through socket/proxy:

sudo setsebool -P httpd_can_network_connect 1

Validate Server Health

After deployment, verify the core service layers before handing traffic to the application.

sudo systemctl status nginx
sudo systemctl status uwsgi-myproject
sudo journalctl -u uwsgi-myproject -n 50 --no-pager
curl -I http://127.0.0.1/

Check resource pressure and availability:

uptime
free -h
df -h
top

If your application exposes a dedicated health endpoint, use it for automated checks and load balancer probes:

curl -f https://example.com/health/