Also see: https://gist.github.com/lemiorhan/8912188
Here are the simple steps needed to push your local git repository directly to a remote (e.g. prod) server over ssh. This is based on Digital Ocean's Tutorial.
You are developing in a working-directory on your local machine, let's say on the master
branch. Usually people push code to a remote
server like github.com or gitlab.com and pull or export it to a production server. Or you use GitHub's webhooks to send a POST request to a webserver to take appropriate actions such as cloning/checking out a branch on the remote (prod) server.
But here you could simply use a bare
git repository on the production server and publish a branch of your choice (e.g. master
) directly to that server. This remote repo on the server acts upon the push event using a 'git hook' (in this case, the post-receive
git hook) to put the files into a deployment directory on your server. No need for any intermediary such as GitHub.
This creates a scenario where there is no middle-man, high security with encrypted communication (using ssh keys, only authorized people get access to the server) and high flexibility from using a shell script (in the post-receive
hook) for the deployment.
- Know how to use GIT, ssh etc.
- Have a local working-directory ready to deploy, with AT LEAST 2 commits in the
git log
. - Have SSH access to your server using private/public key
-
Have the local workspace dir ready to deploy
-
Create a directory on your remote server to receive the deployment (e.g.
/var/www/html
) -
Add a bare git repository on the remote server
-
Add the
post-receive
hook (shell script) to the bare repository, make it executable -
Add the remote-repository as a 'git remote' to your local git repository
-
Push to the production server, relax.
Nuf said. I assume we are working on master – but you could work on any branch. Ensure there are at least 2 commits.
ssh into your remote (e.g. production) server.
We'll assume that your username is webuser
on server.com
and you access it (for "security-through-obscurity" reasons) over port 234 instead of the usual 22:
$ ssh [email protected] -p234
$ mkdir ~/deploy-dir
(Note: If you ssh in over the default ssh port of 22
, then you can also just use the regular ssh [email protected]
instead of ssh [email protected] -p22
, but let's assume you use port 234
)
Now we'll create a "bare" git repository – one that does not contain any working copy files. It only has the contents of the .git
directory in a normal working copy such as refs
, hooks
, branches
etc. Call it whatever you like, but for our purposes, let's call it bare-project.git
:
$ git init --bare ~/bare-project.git
The post-receive
hook is a shell script that is executed when the push from the local machine has been received. We will write this script so that it deploys the files into required deployment directory (~/deploy-dir
in this example). The post-receive
file is located at this path: ~/bare-project.git/hooks/post-receive
. It must be named exactly as post-receive. Normally, it is not present by default, so use a text editor such as vim
to create and edit it.
The script we will use does check if the correct branch is being pushed (it won't deploy a develop
branch, e.g.)
See the post-receive file for details.
Ensure it is executable: chmod a+x ~/bare-project.git/hooks/post-receive
Now we'll add the bare repository (on the remote server) to your local system as a 'git remote'. For this example, prod
is what we'll call this remote. This could also be called "staging" or "live" or "test" etc if you want to deploy to a different system or multiple systems.
$ cd ~/path/to/working-copy/on-your-local-system/
# assuming you ssh over port 234 instead of 22,
# otherwise if you use the default port (22), you can just omit the `:234` part of the following url:
$ git remote add prod ssh://[email protected]:234/home/webuser/bare-project.git
Make sure bare-project.git
corresponds to the name of the bare repo you used in step 3.
Now you can push the master branch to the remote server:
$ git push prod master
That's it.
You should see something like:
$ git push prod master
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 948 bytes | 948.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Ref refs/heads/master received. Deploying master branch on server...
remote: Already on 'master'
To ssh://[email protected]:234/home/webuser/bare-project.git
d2b8c82..ac8be14 master -> master
If you pushed a "bad" deployment to the remote server and need to roll it back, fret not! It's easy to roll-back to an earlier, "good" commit using a forced push:
# on your local machine
# assuming that HEAD~1 is the commit you want to roll-back to
$ git reset --hard HEAD~1 # or git revert if you prefer that
# force push to the remote server
$ git push -f prod master
#!/bin/bash
#créé par akamasoft
#version 1.0.0
#date 26/05/2025
#description: script de déploiement git via post-receive
set -o pipefail
export PYTHONUNBUFFERED=1
=== Couleurs ===
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
=== Chemins ===
GIT_DIR="/home/akamasoft/akamasoft_depot"
WORK_TREE="/home/akamasoft/akamasoft_online"
VENV="/home/akamasoft/env"
REQ_FILE="$WORK_TREE/requirements.txt"
MANAGE="$WORK_TREE/manage.py"
LOGFILE="/home/akamasoft/logs/deploy.log"
=== Fonction de log ===
log() {
COLOR=$1
MESSAGE=$2
echo -e "${COLOR}${MESSAGE}${NC}"
echo -e "$(date +"%Y-%m-%d %H:%M:%S") - ${MESSAGE}" >> "$LOGFILE"
}
log "$CYAN" "=== 🚀 Déploiement Git via post-receive ==="
1. Vérification du répertoire
if [ ! -d "$WORK_TREE" ]; then
log "$RED" "[ERREUR] Le répertoire $WORK_TREE n'existe pas."
exit 1
fi
2. Checkout
log "$YELLOW" "[1] Checkout dans $WORK_TREE"
GIT_WORK_TREE=$WORK_TREE git --git-dir=$GIT_DIR checkout -f main 2>&1 | tee -a "$LOGFILE"
3. Activation de l’environnement virtuel
if [ -d "$VENV" ]; then
log "$YELLOW" "[2] Activation de l’environnement virtuel"
source "$VENV/bin/activate"
else
log "$RED" "[2] Environnement virtuel introuvable à $VENV"
exit 1
fi
4. Installation des dépendances
if [ -f "$REQ_FILE" ]; then
log "$YELLOW" "[3] Installation des dépendances"
pip install -r "$REQ_FILE" 2>&1 | tee -a "$LOGFILE"
else
log "$YELLOW" "[3] Aucun fichier requirements.txt trouvé. Étape ignorée."
fi
5. Commandes Django
if [ -f "$MANAGE" ]; then
log "$YELLOW" "[4] Collecte des fichiers statiques ..."
python3 "$MANAGE" collectstatic --noinput 2>&1 | tee -a "$LOGFILE"
else
log "$RED" "[!] Fichier manage.py introuvable. Étapes Django ignorées."
fi
6. Redémarrage de Gunicorn
log "$YELLOW" "[7] Redémarrage de Gunicorn ..."
sudo systemctl restart gunicorn
if [ $? -eq 0 ]; then
log "$GREEN" "✅ Gunicorn redémarré avec succès."
else
log "$RED" "[ERREUR] Échec du redémarrage de Gunicorn."
fi
7. Envoi de l'email
send_email() {
SUBJECT="✅ Déploiement terminé - $(date '+%d/%m/%Y %H:%M')"
TO="[email protected], [email protected]"
BODY=$(tail -n 30 "$LOGFILE")
}
send_email
log "$GREEN" "✅ Déploiement terminé avec succès."