Deploy a website to a remote server with Git push

Three steps to configure the deployment process with a Git Hook.


Git is a great tool for version control, but not only. It can also simplify the deployment process, thanks to Git Hooks.


Three steps to configure the deployment process:

1. Create an empty Git repo on the server

2. Write a Git Hook to deploy the code

3. Deploy from the local computer

tl;dr

On the remote server:

# Copy the 'project-create.sh' bash script https://gist.github.com/francoisromain/58cabf43c2977e48ef0804848dee46c3
# Then create a new project
# It configures a Git repo, and creates a Git hook
$ bash ./path/to/project-create.sh <your-project>

On your local computer:

# Add your server as a Git remote called 'deploy'
git remote add deploy ssh://<your-name>@<your-ip>/srv/git/<your-project>.git/
# Push your code and deploy
git push deploy master

Prerequisite
- Connect to the server via SSH without root.
- Git is installed globally on the server and locally.

User access and permissions

Connect to the server: $ ssh <your-name>@<your-ip>

Add your user to the users group, so it has permissions to use Git: $ sudo adduser <your-name> users.

On the server, we are going to use two directories:

  • /srv/tmp/ is a temporary directory for deployment.
  • /srv/www/ contains the actual production files (this could be /var/www/html/ on an Apache server).

Create the directories and set the permissions so your user can deploy without a root access:

sudo mkdir -p /srv/tmp/
cd /
srv/tmp/
sudo chgrp -R users .
sudo mkdir -p /srv/www/
cd /srv/www/
sudo chgrp -R users .

1. Create an empty Git repo on the server

First we need to configure an empty Git repo on the remote server to push the content of our local git repo.

Create an empty Git repo

sudo mkdir -p /srv/git/<your-app>.git

# Init the repo as an empty git repository
cd /srv/git/<your-app>.git
sudo git init --bare

Conventions:

  • Add a .git extension on a git repo.
  • Store git repos in srv/git/.

Set the permissions on the Git repo

We need to set the permissions on the repo so that we can modify its content without sudo (from stackoverflow).

cd /srv/git/<your-app>.git

# Define group recursively to "users", on the directories
sudo chgrp -R users .
# Define permissions recursively, on the sub-directories 
# g = group, + add rights, r = read, w = write, X = directories only
# . = curent directory as a reference
sudo chmod -R g+rwX .

# Sets the setgid bit on all the directories
# https://www.gnu.org/software/coreutils/manual/html_node/Directory-Setuid-and-Setgid.html
sudo find . -type d -exec chmod g+s '{}' +

# Make the directory a Git shared repo
sudo git config core.sharedRepository group

2. Write a Git hook to deploy the code

When we push our code to the server, we want to trigger a command to deploy the code from the git repo. To do so, we use a Git post-receive hook.

Create the Git hook file

cd /srv/git/<your-app>.git/hooks

# create a post-receive file
sudo touch post-receive

# make it executable
sudo chmod +x post-receive

Edit the /srv/git/<your-app>.git/hooks/post-receive file content:

#!/bin/sh
# The production directory
TARGET="/srv/www/<your-app>"
# A temporary directory for deployment
TEMP="/srv/tmp/<your-app>"
# The Git repo
REPO="/srv/git/<your-app>.git"
# Deploy the content to the temporary directory
mkdir -p $TEMP
git --work-tree=$TEMP --git-dir=$REPO checkout -f
cd $TEMP
# Do stuffs, like npm install…
# Replace the production directory
# with the temporary directory
cd /
rm -rf $TARGET
mv $TEMP $TARGET

Here is a bash script to automate the two steps above. Upload it somewhere on your server, then call bash ./project-create.sh <your-project>.

3. Deploy from the local computer

Initialize the repo and add the server repo as a remote

cd <your-app>

# Initialize git repo
git init

# Add the server repo as a remote called "deploy"
git remote add deploy ssh://<your-name>@<your-ip>/srv/git/<your-app>.git/

Push to the server (and deploy)

git add . 
git commit -m "<commit message>"
git push deploy master

Next steps:

  • Automate testing before deployment.
  • Configure a load balancer to ensure no downtime.