Jens Segers on Jul 27 2013

Deploying websites with Git (and Composer)

I have been using Git to deploy my websites for quite some time now. It is so much easier than having to drag 'n drop files with FTP or SFTP. When I switched to Laravel 4 earlier this year, I updated the script to automatically update composer packages.

To deploy a website using Git you basically have to set up 2 repositories; one on your workstation and one on the webserver with the actual live "production" code. You push changes from your workstation to the "production" repository, where a Git post-receive hook will update the live code.


Start by initialising a non-bare Git repository in your website's directory:

> cd /var/www/website
> git init
Initialized empty Git repository in /var/www/website/.git/

After this we need a post-receive hook that will make sure that any time anything is pushed to the repository, the live code is updated. These hooks are custom scripts that are executed when certain important events occur, and are located in the .git/hooks directory. In this case we need to create the .git/hooks/post-receive hook with the following content:


# Update the working tree after changes have been pushed here
cd ..
env -i git checkout -f

# Check if a composer.json file is present
if [ -f composer.json ]; then

    # Install or update packages specified in the lock file
    composer install


The first lines update the live code with the changes that are pushed, and the part below checks if a composer.json file is present. The composer install command will install or update your packages with the versions specified in the composer.lock file, so make sure this file is not in your .gitignore file. This way, your remote host will always have the same package versions as your workstation. For more information about this lock file, and why it should not be in your .gitignore file check:

Ensure the hook is executable:

> chmod +x .git/hooks/post-receive

To be able to push to a non-bare repository you need to execute the following command:

> git config receive.denyCurrentBranch ignore


The only thing that is left is to add the correct remote to the website's repository on your workstation:

> git remote add production ssh://[email protected]/var/www/website

To test if everything is working, modify something and push it to the newly created production remote:

> touch test
> git add test
> git commit -m "Testing deployment"
> git push production master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 268 bytes, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Composer up to date
To ssh://[email protected]/var/www/website
   cf43bc5..96f33d8  master -> master


[email protected] 1 year ago

A coleção Primavera-Verão 2013/2014 da Timberland traz várias novidades na linha de Polos. A coleção está bem colorida, com uma cartela de cores vibrante, perfeitas para os dias calorentos. Lisas ou listradas, com certeza você vai encontrar uma que é a sua cara. Todas são feitas com 100% de algodão orgânico ou com o tecido Super Piqué, que combina o algodão orgânico com o poliéster reciclado, dispersando melhor a umidade, garantindo mais respirabilidade e ainda protegendo contra os raios UV. Agora é hora de aprender a usar este coringa com muito estilo e em todas as ocasiões! tall timberland boots's-Timberland-Waterproof-Work-and-Hunt-Boots.html

Piotr Nowak 2 years ago

I'm using - for me it's the best solution :)

Piotr Nowak 2 years ago

I'm using http://buddy works - form me it's the best solution :)

Neo Ighodaro 4 years ago

It wasn't suggested but i think you should also specify a way to actually protect accidental access to the /.git folder

Jens Segers 4 years ago

@Sebastiaan, on my production server I use a more complicated post-receive hook that is prepared by Chef :)

Sebastiaan 4 years ago

You've probably found out by now, but if not, take a look at Capistrano. And Puppet if you want to automate all your running services. Takes a bit of time to setup, but pretty neat once it works. You could replace Capistrano with new Remote class if you're using Laravel 4.1.