Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Deploy your site with git

Deploy your site with git

This gist assumes:

  • you have a local git repo
  • with an online remote repository (github / bitbucket etc)
  • and a cloud server (Rackspace cloud / Amazon EC2 etc)
    • your (PHP) scripts are served from /var/www/html/
    • your webpages are executed by apache
    • apache's home directory is /var/www/
    • (this describes a pretty standard apache setup on Redhat / Ubuntu / CentOS / Amazon AMI etc)

you should be able to do the same with Java, Perl, RoR, JSP etc. however you'll need to recreate the (rather simple) PHP script

1 - On your local machine

Here we add the deployment script and push it to the origin, the deployment script runs git commands to PULL from the origin thus updating your server

Grab a deployment script for your site

See deploy.php

Add, commit and push this to github

git add deploy.php
git commit -m 'Added the git deployment script'
git push -u origin master

2 - On your server

Here we install and setup git on the server, we also create an SSH key so the server can talk to the origin without using passwords etc

Install git...

After you've installed git, make sure it's a relatively new version - old scripts quickly become problematic as github / bitbucket / whatever will have the latests and greatest, if you don't have a recent version you'll need to figure out how to upgrade it :-)

git --version

...on CentOS 5.6

# Add a nice repo
rpm -Uvh
# Install git
yum install --enablerepo=webtatic git-all

...using generic yum

sudo yum install git-core

Setup git

git config --global "Server"
git config --global ""

Create an ssh directory for the apache user

sudo mkdir /var/www/.ssh
sudo chown -R apache:apache /var/www/.ssh/

Generate a deploy key for apache user

sudo -Hu apache ssh-keygen -t rsa # choose "no passphrase"
sudo cat /var/www/.ssh/

3 - On your origin (github / bitbucket)

Here we add the SSH key to the origin to allow your server to talk without passwords. In the case of GitHub we also setup a post-receive hook which will automatically call the deploy URL thus triggering a PULL request from the server to the origin

GitHub instructions

Add the SSH key to your user

  2. Create a new key
  3. Paste the deploy key you generated on the server

Set up service hook

  2. Select the Post-Receive URL service hook
  3. Enter the URL to your deployment script -
  4. Click Update Settings

Bitbucket instructions

Add the SSH key to your account

  2. Create a new key
  3. Paste the deploy key you generated on the server

Set up service hook

  1. Go to: Repo > Admin > Services
  2. Select "POST"
  3. Add the URL to your deployment script -
  4. Save

Thanks to DrewAPicture in the comments for this one

4 - On the Server

Here we clone the origin repo into a chmodded /var/www/html folder

Pull from origin

sudo chown -R apache:apache /var/www/html
sudo -Hu apache git clone /var/www/html


Now you're ready to go :-)

Some notes

  • Navigate the the deployment script to trigger a pull and see the output:
  • When you push to GitHub your site will automatically ping the above url (and pull your code)
  • When you push to Bitbucket you will need to manually ping the above url
  • It would be trivial to setup another repo on your server for different branches (develop, release-candidate etc) - repeat most of the steps but checkout a branch after pulling the repo down


* Used for automatically deploying websites via github or bitbucket, more deets here:
// The commands
$commands = array(
'echo $PWD',
'git pull',
'git status',
'git submodule sync',
'git submodule update',
'git submodule status',
// Run the commands for output
$output = '';
foreach($commands AS $command){
// Run it
$tmp = shell_exec($command);
// Output
$output .= "<span style=\"color: #6BE234;\">\$</span> <span style=\"color: #729FCF;\">{$command}\n</span>";
$output .= htmlentities(trim($tmp)) . "\n";
// Make it pretty for manual user access (and why not?)
<html lang="en-US">
<meta charset="UTF-8">
<body style="background-color: #000000; color: #FFFFFF; font-weight: bold; padding: 0 10px;">
. ____ . ____________________________
|/ \| | |
[| <span style="color: #FF0000;">&hearts; &hearts;</span> |] | Git Deployment Script v0.1 |
|___==___| / &copy; oodavid 2012 |
<?php echo $output; ?>

FYI, I got this to work using BitBucket pretty easily. They may have made allowances for POST requests in the last few months.

On BitBucket, went to the Repo > Admin > Services. Selected POST, dropped in my URL and saved it. Seems to be working as expected.

Thanks for the script, it makes my life a lot easier.


oodavid commented Jun 21, 2012

Glad it helped you out - the real trick for me was getting it so that Apache could play nicely with git...

nebriv commented Jul 31, 2012

Thanks! it works well for me on shared hosting, without keys!

Just a heads up. It would be smart to add git reset --hard HEAD in the commands before git pull. If for some reason your working directory is dirty (files changed, log files created etc) then the deploy will fail. Resetting the HEAD to the last commit will prevent a deploy failure.

Do you worry about security of the deploy.php script? Is there any harm is someone hits that script, or anything malicious they can do?

got stuck here, any ideas on how to change permissions?

root@staging ~]# sudo -Hu apache ssh-keygen -t rsa # choose "no passphrase"
Generating public/private rsa key pair.
Enter file in which to save the key (/var/www/.ssh/id_rsa): githubkey
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
open githubkey failed: Permission denied.
Saving the key failed: githubkey.

I created a key by skipping the create directory but now i get

[root@staging ~]# sudo -Hu apache git clone /var/www/html/ fatal: destination path '/var/www/html' already exists and is not an empty directory.

natxet commented Sep 29, 2012

as @mattbanks says, no security?

This might interest you: JamesBrooks/git-runner (with the git-runner-deploy gem).

I got this working but am missing why I created SSH keys?

If I understand the script correctly anyone can hit the URL ( and it will execute a git pull. How is the difference detected between the GitHub WebHook service and a random visitor or a web crawler?

I think I will do some more digging, I found with a similar function but more advanced options like IP filtering and SSH/FTP support.

I've rewrote this gist and created a 'normal' repository for it. Check it out!

It features:

  • easy configuration
  • no need to create the clone on the server first, it's doing that automatically
  • basic security (through obscurity) so the script can't be triggered by anyone except the one who knows the secret
  • update the code using non-master branch
  • delete files that are no longer in the repository (DANGEROUS!)
  • ignore certain files/directories when updating
  • error detection: if any step prior to deploy fails, nothing is changed
  • stamp the deployed code version
  • backup the target prior to deploy

If this is all "local" why not just use native git? using a "working tree" and a "bare repo"?

Also things like branch, and other useful into are already coming over in the payload from the github post-receive hook, not sure why those are passed in via $_GET

Awesome, thanks! Just a heads-up, on Ubuntu my user was www-data, not apache.

@mattbanks, @natxet: RE: security: Two things I usually do is to rename the script to {randomstring}.php, and modify it not to expose the username for all to see. Another option is to restrict referrer access to the script via htaccess.

davemac commented Feb 15, 2013

I agree with @zanematthew. This method seems like overkill for what can be accomplished with a post-receive hook and a bare repo on the target server. Or am I missing something?

Already had ssh keys for my server because was pulling in changes manually. I'm using this in development and works perfect for me. Thanks!

This is only when my server is on PHP really? At same I can't to build another for Python, it's cool this congrats. Still I not test this, but I think so it will function... thanks.

In my case I needed to change (etc/passwd) [Centos 6.2]

apache502:503:Apache server:/:/sbin/nologin
apache502:503:Apache server:/var/www:/sbin/nologin


ghost commented Feb 2, 2014

Another PHP tool for the job. A bit more elaborate one.

I've found and recommend this:

zenware commented Aug 28, 2014

Your tag link to the deploy.php file is incorrect, it should be:

I need help actually I did all the steps to
auto update
production updates from master branch
staging updates from staging branch
it works but only for production server ..

what i want is when I will push my changes to any working branch and merge it will staging branch my updates automatically goes to staging server and when I will merge my staging branch to master branch my updates automatically goes to production server

if you change

$tmp = shell_exec($command);
$tmp = shell_exec($command.' 2>&1');

it will show error output too (handy when in wrong directory etc)

nickk80 commented Jan 31, 2015

I become every time this when I execute manually deploy.php:

On branch master

nothing to commit (working directory clean)

if I made changes to the master on github, the POST process and the deploy.php do nothing. ?? I'm to be desperate to do this! I kill all an go on your README 4 times and every time with the same problem.

Some security issues with this:

  • if the github repo is public, any clown in the world can co-opt your server into their botnet
  • Deploy.php doesn't even bother to validate that the incoming ping is actually from github. The approach described here fails to describe how one sets up a secret to sign the hook payload or to check that payload in PHP.
  • If this were implemented on a shared host, then pretty much anyone could take a peek at the ssh keys, both private and public.
  • Every single file in your web root can be written by Apache. On a shared host your files are at the mercy of anyone else on the server. Even if your machine is a single-purpose one, a single vulnerability in your code could easily compromise your entire web root.

mssyogi commented May 21, 2015

I m unable to got working.

I hit the url with post hook as well as the browser but not get updated my repo.

I've found little tools to help deploy your code from Github or Gitlab so I created to handle Web-Hooks and call scripts to deploy on your own server.
This handles the JSON that's sent from github or gitlab and uses your own script to react. You can use your own PHP or shell scripts after receiving the pull notification...
Check it out:

I modified the PHP script which should comply with Github's instruction on webhooks while also updated the instructions.

Deploy your site with git securely

If you have permission problems with git pull, but you can still git status, make sure your .git folders are owned by your apache git user. A git pull needs permission to modify the .git files.

Our solution:

chown -R www-data:www-data .git

from repository root directory

For "security" you could:

  1. add at the beggining of the file....
if (!isset($_GET['randomvar']) || $_GET['randomvar']!= 'random_value') {

and add ?randomvar=random_value to the webhook url when you create it.

  1. Rename the file to something random. (this - you should do it anyways)

  2. use .htaccess rules for that file - allow/deny and whitelist the ranges given by github or bitbucket for the webhooks... Not sure what they are for github, but for bitbucket it's this (table col that is labeled as Outbound (for hooks like POST))

That's about all that comes to mind....

Thanks for the script!

adnanh commented Nov 21, 2015

Instead of a PHP script, one could use webhook and a shell script. For security, one can define trigger rules to include github payload signature check, etc...

erm3nda commented Jan 21, 2016

Hum, i heard something that say it uses Github to deploy code. I'll use it, as it's perfect to sync the code.
What Im thinking now, is that i would not publish sensible PHP data into the Github public repo... I suspect that the user added Bitbucket due to it's private free repos 👍

Wich one of the suggested worked more like "out of the box"?
I prefer simpler than complex, just sync.


Looks interesting. I noticed a small problem in your Gist. The anchor link to "See deploy.php" is incorrect. The anchor is currently #file_deploy.php, but actually should be #file-deploy.php.


ghost commented Jun 13, 2016

Hi All,

Thanks in advance.
I am new to jenkin and CI. My question is below.
My project is in Angular, node and express js and SCM is git. So i want to automate the build and deployment via Jenkins, so please help me on this.
As of now i have configured the Gitub plugin in Jenkins which get trigger when we make any commit. I need the script which help to deploy the changes to Windows Web server.

Note : I am working on Windows.

I like your script idea but I agree with @bgallagh3r . If something happens were a file is deleted, a git pull is not bring this file back. You have to use a git reset.

The shell_exec command on the git pull doesn't do anything for me. All other git commands run fine.

Hey, I've successfully created the and deploy the initial (private repo)repository, but after all while trying to git pull the ssh key was rejected. can you help me with this? machine is running on Ubuntu 14

Apologize if it seems a promotion: but made this tool: gitpull as a hosted service.

avramovic commented Aug 14, 2016

@tareq1988 This seems nice but you should add an option to execute custom commands before/after git pull. For example if I want to migrate the database after git pull. Also, add some feedback form so you can get ideas such as this one from your users.

Not Too Bad!

the link for setting up service hook ( doesn't work, 404

I would suggest optionally adding 'git reset --hard,' to ensure the pull happens correctly. If you're using the git workflow properly, it shouldn't matter if local changes are dumped, because all the important files should be in the repo anyways.

geobde commented Feb 16, 2017

hi guys i try this method with gitlab and dont work you have any ideas?

Hi, i am using Ubuntu OS, i cloned repo from bitbucket on local env & set the ssh key access. if i tried to pull / push via terminal then all is working fine. but if i run that script on my local then getting a error : Host key verification failed.
fatal: Could not read from remote repository.

Also whoaim command showing a user - "www-data" but i am using "web-dev" user on my system.

Please help to resolve this issue.

agrublev commented Apr 7, 2017

For those interested on ubuntu this is what you have to do (sad nobody added it yet, took me an hour)

Edit /etc/sudoers to add
www-data ALL = NOPASSWD: /usr/bin/git
Then add to the git pull in deploy.php
'sudo git pull'

Without this I could not pull.

I have Nginx server, how can I do that..

Great script! It's running like a charm with Bitbucket. Thank you for the help!

xenu256 commented Jun 5, 2017

Isn't it better to name things more appropriately?[username]/[project]/admin/hooks

oinstead of:

Great script its working fine.

why not use this and save typing all these commands?

If the purpose is to publish a git repository to a live server, then just cut the middle man.
This explains how to use git to publish from the local host to the remote server directly and using different possible branch like beta, live, etc to which one can associate sub domain for validation for example.

mckaycr commented Sep 25, 2017

This was easy to integrate, thanks. I use 1and1 shared webservice (or whatever its call). Bottomline it doesn't support ssh keys, only passwords unless I upgrade to the next package. Upgrading is probably not an option for my non-profit group. Is there a way to implement this so that the server doesn't need a key to pull changes from my (custom) gitlab server?

nate-fr commented Oct 23, 2017

This Script is vulnerable. DO NOT USE IT.


Quick question, is this script easily adapted for Nginx servers?

Quick question please i don't understand the URL to your deployment script -

What's my URL to the deploy script?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment