Automating Django Deployments: A Reusable Shell Script
Managing multiple Django projects on a single server can quickly become complex, especially when coordinating code pulls, migrations, static file collection, and service restarts (like uWSGI, Celery, and Apache/Nginx). Manual execution is error-prone and time-consuming.
The solution is a robust, reusable shell script that handles both full deployments and simple service restarts, making your workflow predictable and smooth.
This script was developed during a troubleshooting session for the makeitexist project to standardize operations across five different Django websites hosted on a single Linode VPS.
The Goal: A Single Source of Truth
We aimed to create one script capable of managing all projects on the server, handling two main actions:
deploy: Pull code, install dependencies, run migrations, collect static files, and restart services.restart: Just restart all associated system services (web server, worker, beat).
The script was designed to accommodate specific naming conventions, such as using the first letter of the project name for the virtual environment directory (e.g., m_env for makeitexist).
The Final Script: project_manager.sh
This script uses standard Unix utilities, functions, and conditional logic (case statements and if checks) for maximum flexibility.
Save the following code as /var/www/project_manager.sh on your server:
#!/bin/bash
# Define global variables based on arguments $1 (project name) and $2 (action)
PROJECT_NAME=$1
ACTION=$2
# Configuration Variables
PROJECT_DIR="/var/www/$PROJECT_NAME"
# Dynamically determine the VENV name (e.g., m_env for makeitexist)
FIRST_LETTER=$(echo "$PROJECT_NAME" | cut -c 1)
VENV_DIR="${FIRST_LETTER}_env"
# Service names for uWSGI, Apache, and Celery (must match systemd config)
UWSGI_SERVICE_NAME="${PROJECT_NAME}.service"
APACHE_SERVICE_NAME="apache2.service"
CELERY_WORKER_SERVICE="/etc/systemd/system/${PROJECT_NAME}_celery_worker.service"
CELERY_BEAT_SERVICE="/etc/systemd/system/${PROJECT_NAME}_celery_beat.service"
# --- Functions ---
# Function to handle all service restarts
restart_services() {
echo "--- Restarting services for $PROJECT_NAME ---"
sudo systemctl daemon-reload
# Restart uWSGI and Apache (these are always required)
sudo systemctl restart $UWSGI_SERVICE_NAME
sudo systemctl restart $APACHE_SERVICE_NAME
# Conditionally restart Celery services if their files exist
if [ -f "$CELERY_WORKER_SERVICE" ]; then
echo "Restarting Celery Worker..."
sudo systemctl restart $(basename $CELERY_WORKER_SERVICE) # Use basename for systemctl command
else
echo "Celery Worker service file not found. Skipping restart."
fi
if [ -f "$CELERY_BEAT_SERVICE" ]; then
echo "Restarting Celery Beat..."
sudo systemctl restart $(basename $CELERY_BEAT_SERVICE)
else
echo "Celery Beat service file not found. Skipping restart."
fi
echo "--- Restart complete ---"
}
# Function to handle the full deployment process
deploy_project() {
echo "--- Starting full deployment for $PROJECT_NAME ---"
# Navigate, pull code, install deps
cd $PROJECT_DIR || { echo "Error: Project directory not found at $PROJECT_DIR"; exit 1; }
git pull origin master
source $VENV_DIR/bin/activate
pip install -r requirements.txt
python manage.py migrate --noinput
python manage.py collectstatic --noinput
deactivate
restart_services # Calls the conditional restart function
echo "--- Deployment finished successfully for $PROJECT_NAME ---"
echo "NOTE: Remember to manually run loaddata if needed for initial fixtures."
}
# --- Main Logic (Argument Parsing) ---
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <project_name> <deploy|restart>"
echo "Example (Full Deploy): $0 makeitexist deploy"
echo "Example (Just Restart): $0 makeitexist restart"
exit 1
fi
case "$ACTION" in
deploy)
deploy_project
;;
restart)
restart_services
;;
*)
echo "Invalid action: $ACTION. Use 'deploy' or 'restart'."
exit 1
;;
esac
How to Use the Script
- Save the file: Place it in a shared directory like
/var/www/project_manager.sh. - Make it executable:
bash chmod +x /var/www/project_manager.sh - Ensure
sudoersis configured: Edit/etc/sudoersto allow your deployment user to run thesystemctl restartcommands without a password. - Run a Full Deployment:
bash /var/www/project_manager.sh makeitexist deploy - Run a Simple Restart:
bash /var/www/project_manager.sh makeitexist restart
This script provides a professional, repeatable, and robust way to manage your Django projects, turning a tedious manual process into a single, reliable command.