- One-time server setup
- Authorized developers
- Create a new repository on the server
Wouldn’t it be nice to publish a website with a simple git push?
Here I’ll explain you how I usually do this.
I assume that the environment of the staging/production servers are the following:
- Operating system: Ubuntu 14.04 or Ubuntu 16.04
If your servers are not the above, the approach described in this document should work with minor changes given that they are *nix.
Let’s assume that:
- the remote server is available at the address
- you want to create a new repository named
- the web server impersonates the user in the group
- you want to execute these commands after the push:
One-time server setup
Install required packages
First of all, you need to install git on the server.
You can do this by running the following command:
sudo apt-get update sudo apt-get install -y git
If the above command fails with an error like
Unable to locate package git, you can try this:
sudo apt-get install -y git-core
Install and configure Composer
In order to have Composer, you need PHP and some PHP extension. On Ubuntu you can install all of them with:
sudo apt-get update && sudo apt-get install -y php-cli php-json php-mbstring
Then you can download Composer with this command:
# Go to your home directory cd # Download and install Composer curl --silent --show-error https://getcomposer.org/installer | php # Move Composer to the default binary folder sudo mv composer.phar /usr/local/bin/composer # Check that Composer works composer --version
In order to save some space, let’s add a script that execute Composer with cache disabled.
sudo nano /usr/local/bin/composer-without-cache
And type this content:
#!/bin/bash export COMPOSER_CACHE_DIR=/dev/null composer "$@"
Finally, make it executable:
sudo chmod a+x /usr/local/bin/composer-without-cache
We need to create a user account on the server. This account will be the one used by the publish process.
With the following command we create that account:
sudo adduser --gecos Git --disabled-login --disabled-password --shell /usr/bin/git-shell --home /home/git --ingroup git
Here’s the explanation of the above options:
--gecos Git: set the full name of the account to
Git(this essentially in order to avoid asking useless data like the account room number and work/home phone)
--disabled-login: the user won’t be able to use the account until the password is set.
--disabled-password: disable the login using passwords (we’ll access the system with SSH RSA keys)
--shell /usr/bin/git-shell: when the user access the system, he will use the fake shell provided by git
--home /home/git: set the home directory for the user to
--ingroup: add the new user to the
group instead of the default one
git: the username of the new account
Strengthen login security
We then need to configure the git shell.
In order to improve the security of unwanted logins and abort shell sessions, we create a file that is executed when the
git user logs in the shell and that will abort the session.
sudo mkdir /home/git/git-shell-commands sudo nano /home/git/git-shell-commands/no-interactive-login
In the editor type in these commands:
#!/bin/sh printf '%s\n' "Hi $USER! You've successfully authenticated, but I do not" printf '%s\n' "provide interactive shell access." exit 128
To save the new file hit
To quit the editor, hit
We finally have to make the file executable:
sudo chmod +x /home/git/git-shell-commands/no-interactive-login
git user needs to be able to publish files acting like
In order to allow this, run this command:
Go to the end of the editor contents and add these lines:
Defaults!/usr/bin/git env_keep="GIT_DIR GIT_WORK_TREE" git ALL=() NOPASSWD: /usr/bin/git, /usr/local/bin/composer-without-cache
The first line tells the system that when the
git command is executed with a
sudo, we need to keep the two environment variables
The second line tells the system that the
git user can execute the
git command acting as
without any further authentication.
Every developer that should be able to publish needs an SSH-2 RSA key pair.
It’s possible (and recommended) to use a different key for every developer.
Create the keys under Windows
So, open PuTTYgen and:
- in the
Parameterspage be sure to check
Number of bits in a generated key
- Hit the
Generatebutton and move randomly your mouse over the PuTTYgen window
- In the
Key commentfield enter
ServerName - DeveloperName(where
ServerNameis the name of the server where the key will be used, and
DeveloperNameis the name of the developers owning the key)
- In the
Confirm passphrasefields enter a password of your choice
- Hit the
Save private keybutton to save the private key to file
- Copy the contents of the
Public key for pasting into OpenSSH authorized_keys fileand save it: this is the public key that we’ll need.
Create the keys under *nix
Simply run this command:
ssh-keygen -t rsa -b 2048 -f key-for-git -C 'ServerName - DeveloperName'
-t rsa: we want public/private key pair in the SSH-2 RSA format
-b 2048: we want 2048 bits in the key
-f key-for-git: this is the name of the file that will contain the private key
-C 'ServerName - DeveloperName': this is the comment to associate to the key (it’s a good practice to specify the name of the server and the one the developer owning the key)
Once you run the
ssh-keygen command and specified a password of your choice, you’ll have these two files:
key-for-git: contains the private key
key-for-git.pub: contains the public key
Allow the developer to publish to the server
Login to the server and run this command:
sudo mkdir /home/git/.ssh sudo nano /home/git/.ssh/authorized_keys
Go to the end of the editor contents and add a new line containing the previusly generated public key.
The public key is a single line that starts with
ssh-rsa , followed by a quite long list of characters and ending with the
ServerName - DeveloperName comments specified during the creation of the key.
Create a new repository on the server
We’ll end up with:
- Directory to be published:
- Repository directory:
First of all, we create the directory that will contain web site (it will be owned by the
sudo mkdir -p /var/www/ sudo chown -R : /var/www/ sudo chmod u+rw -R /var/www/
Then we create the directory that will contain the bare repository data:
sudo mkdir -p /var/git/.git cd /var/git/.git sudo git init --bare sudo git config core.sharedRepository group
core.sharedRepository group option of the git repository is needed in order to grant write access to both the
users (they both belong to the same user group -
And now the key concept of this whole approach: when someone pushes to this repository, we checkout the repository to the publish folder:
sudo nano /var/git/.git/hooks/post-receive
In the editor type these lines:
#!/bin/bash currentUser=`whoami` currentServer=`hostname` repoDirectory=/var/git/.git pubDirectory=/var/www/ echo "Hello! I'm $currentUser at $currentServer" echo "I'm going to publish from" echo " $repoDirectory" echo "to" echo " $pubDirectory" rc=0 sudo -u git --git-dir=$repoDirectory --work-tree=$pubDirectory checkout master -f if [ "$?" -ne "0" ]; then echo "GOSH! GIT FAILED!!!!" rc=1 fiif [ $rc -eq 0 ]; then echo "Changing directory" pushd $pubDirectory if [ "$?" -ne "0" ]; then echo "GOSH! PUSHD FAILED!!!!" rc=1 else echo "Running composer install" sudo -u composer-without-cache install \ --prefer-dist --no-dev --no-progress --no-suggest --no-ansi --no-interaction if [ "$?" -ne "0" ]; then echo "GOSH! COMPOSER FAILED!!!!" rc=1 else echo "Great! Composer succeeded!" fi popd fi fiif [ $rc -eq 0 ]; then echo "Don't worry, be happy: everything worked out like a charm ;)" fi exit $rc
We finally need to update the permissions of the newly created directory:
sudo chown -R git: /var/git/.git sudo chmod -R ug+rwX /var/git/.git sudo chmod -R ug+x /var/git/.git/hooks/post-receive
Everything is almost ready!
The only step required to deploy with a simple
git push is to add the remote to your repository.
For instance, here’s how to add a remote named
deploy to your local repository:
git remote add deploy git@:/var/git/.git
When you push to the
deploy remote, the published directory willbe updated automatically.
Nice, isn’t it?