Where developers come to connect, share, build and be inspired.


FTP is so 90's. Let's deploy via Git instead!


Update: This article has been published in Hacker Monthly issue 42.

First, create a directory on your server and initialize an empty git repository. I like to serve my websites from ~/www/, so that's what I'll do in this example.

mkdir ~/www/example.com && cd ~/www/example.com
git init

Next, let's set up your server's git repo to nicely handle deployment via git push.

git config core.worktree ~/www/example.com
git config receive.denycurrentbranch ignore

Finally, we'll set up a post-receive hook for git to check out the master branch so your web server can serve files from that branch. (Remember, ^D is Control+D, or whatever your shell's EOT character is.

cat > .git/hooks/post-receive
git checkout -f
chmod +x .git/hooks/post-receive

Keep in mind that you can add whatever you like to the post-receive hook if you have a build process. For example, one of my sinatra projects uses the following post-receive hook:

git checkout -f
bundle install
touch ~/www/example.com/tmp/restart.txt

Back on your local machine, let's get your git repo ready for deployment.

cd ~/www-dev/example.com
git remote add origin \

For the first push to your server, run the following command.

git push origin master

Now, whenever you want to deploy changes you've made locally, simply run the following command!

git push


  • Blank-mugshot

    This is so wrong. This is looks like PHP-dev discovered git, but didn't read much about it.

    1) If you going to deploy via git, at least do git export. 2) Capistrano exists for a reason

  • Dsc_8066v-square

    This is literally the first straight-forward walkthrough on how to do this that I've seen. Excellent work, man!

  • Blank-mugshot

    Why even FTP? Just develop live on the web server.

  • Blank-mugshot

    Great method. I prefer using a bare repo outside of my public directory that has its working tree inside the pub directory.

    This way there is no .git dir there and it tells you in pro git that this is the recommended way.

    I made protip about this.

  • Cv_normal

    The problem with this is the committed files are updated in a potentially long process, so the application may be broken for a few seconds or minutes if it contained large files.

    I would suggest Capistrano as well or a CI instead depend on the size of the project.

  • Blank-mugshot

    I came up with deliver https://github.com/gerhard/deliver to address this very problem. It's bash utility that automates git-based deploys and comes with pre-built strategies for the most common deployment scenarios: generated sites (think Jekyll), shared (WordPress, PHP etc.), ruby, node-js, S3 etc. I did a talk on it at my London Ruby User Group in March: https://speakerdeck.com/gerhardlazu/deliver

  • E941e87bca46ac341aab98b6f4aab436_normal

    Oh, how cool is that? Not really. Used it years ago with subversion and other SCM. You can also use Dropbox and other methods. Your tip is so 2000. But coming back to git or better SCM: never ever use the Master branch. I say never. That is so wrong. Use a dedicated branch or a tag or similar, especially in a multiuser environment. You need someone who has the knowledge who will guarantee, that the final deploy will work. You cannot guarantee that in the master, when several users have write access. You need someone who gives the final go after review.

  • Blank-mugshot

    More tips:

    If you're working with a team and you're the one who deploy the code:

    1. init and setup the deploy server repo

    2. on your git repo, use git remote add deploy %serverdeployurl%

    3. optional: setup a branch for deployment if you have some extra configuration, or just use master. If you use a branch, make sure you checked out the correct branch in post-receive hook

    4. use git push deploy master (or name of your deployment branch)

  • Blank-mugshot

    Just doing a git pull --rebase in another directory (that isn't your webroot), copy to webroot, then remove the .git/ directory (plus any other sanitizing you'd like) is both low-risk and easy to script via something like Fabric or Capistrano.

  • Blank-mugshot

    Awesome tips. Thank you.

  • Blank-mugshot


    (a) I Haven't looked into multi-server deployment, sorry. (b) Just use git push origin master -f to force when it's not a fast-forward. (c) If there's a build, script it and add it to the post-receive hook. It's just a shell script. (d) That's true, but this assumes all your files are in the git repo. If not, you'll have to update those manually (such as via scp), or add something in the post-receive hook to take care of it.

  • 0_fivc9moczce9ccy1fkya92d3cbojrcd15gaj92mlhcodx6vpwlomzuvxjdy6z5206h4yjwycvjqp

    Using an SCM (be it GIT, svn, hg or whatever) is an excellent means of distributing all kinds of assets - images, control files, scripts. I even know people who keep their .bashrc and .profile files (and other portable tools) in github and pull them down as they move to different client sites. The efficiency that comes with versioning, hosting and accessing through these tools can make you look super-organised

  • Blank-mugshot

    It's pretty cool, although pull deploys scale better. Simples implementation of pull deploy would be with cron job fetching the git repo and more advanced way is to use Chef or Puppet for it, so that you can also introduce configuration changes in line with code changes.

  • Blank-mugshot

    @NARKOZ They actually can be wrong.

  • Blank-mugshot

    To be honest, this feels totally wrong. Writing 15 lines of command-line code, some of them totally cryptic, for a simple deployment, does not feel "simple", clean and professional. I would recommend professional Continious Integration tools and doing deployments with one click/command AND having the possibility to roll-back anytime.

  • Blank-mugshot

    This post-receive hook method is going to cause problems when (a) there is more than one server (a remote for each) (b) the deploy is not fast-forward (conflicts) (c) there is any "build" involved in making the code ready to run (d) there are other artifacts in the working tree not from the code repo that need to be updated as well

  • Blank-mugshot

    +1 for the principle. Reminds me of a blog post about using a git repo from blogging. It has all the concepts of editing, posting etc.

  • Blank-mugshot

    Great post. I plan on writing something similar in the future; essentially, how to "roll your own" git deployments.

    For anyone still confused, here's something I wrote that is even more simple and involves much less command line knowledge:


  • 0_v24zqlsiwfj_oivyvhebqqihiiasoduy4e2fqqezq5vgzwupnohns6orlamtwoskrfvqr3zktcnv

    It's a good post, but i must ask, why? FTP is in use over Git because it's simple to use, just like Windows is used by the majority even though Linux is technically better.

  • Blank-mugshot

    Or Shell script: $ export name=mynewrepo; curl -L -s tiny.cc/gitdeploy

    Save to alias: $ echo -e "\n alias scr='curl -L -s \$1'\n" >> ~/.bashrc $ export name=mynewrepo2; scr tiny.cc/gitdeploy | sh

  • Blank-mugshot

    Checkout https://github.com/slimjim - my attempt at making this processes a little more easier

  • Blank-mugshot

    @andoriyu 99, 400 people can't be wrong.

  • F235af1f8c6146e9dee33905b46b1572_normal

    FTP is dead !!!!! I use a tool ( http://dyndrop.com ) that takes exactly the same thing but even more easily. This is especially useful for my projects that I want to deploy quickly.

  • Blank-mugshot

    The biggest problem with git is the scalability. We currently use it in an awsautoscaled environment. When the servers come up, obviously nothing is on /mnt. This causes git pack to cause high com and high io in the master as it computes from commit 0. This can be solved using shallow copy, however, there are major bugs with that if you use branches and you risk deploying the wrong code to the wrong server.

    So what is the solution? We are still working on a good one. Right now wend up mounting an nfs mount when the server bootstraps and copy over a base image of the repository avoiding git pack-objects and the delta overhead which goes with it. Is it the answer? No. The hunt for that continues.

  • Blank-mugshot

    Some additional explanation would be helpful.

    • What's the purpose of changing worktree? (I'm unfamiliar with the option, I've never needed to use it.)
    • Why are you calling the server remote origin? Seems more like "destination." (This could be very confusing to people users who aren't that familiar with git remotes.)
    • An explanation of the post-receive hook would be useful (I now get that after pushing you have to checkout on the remote to actually update the working dir but that wasn't immediately apparent).

  • Blank-mugshot

    Why wouldn't you just use a tool like Capistrano? Capistrano can deploy from Git as well, but it follows best practices like exporting a bare copy of the repo. It also provides niceties like rollback and ordered tasks (with before/after hooks) for that moment when you realize that you need to do something outside of dumping code in to a directory. Tacking on ad hoc bash scripts is what you do when better tools aren't available.

    I'm all for exploring new ways to do the same old things, but this just strikes me as terribly incomplete and introduces some bad practices, like hosting a full copy of your git repo in the httpd document root.

  • Blank-mugshot

    I think it makes more sense to deploy onto a stage, and archive the deployment files, then push them to the server in some way that is simple.That way they are assured to have been created.

    We have been using VCS to do deployments of Wow addons for years, that has worked great - but you still have to wait for the built product to be prepared so you can post it ti alternate sites. It is nice though to have your build updated every time you commit.

  • Blank-mugshot

    git config core.worktree $(pwd)

    Saves some time

  • Blank-mugshot

    This works really well for an event space site I manage. It's a simple site that deploys to two places; one prod server, and github. I have two remotes set up in git config: eg:

    [remote "origin"] url = https://github.com/natalieolivo/suite116.git fetch = +refs/heads/:refs/remotes/origin/ [remote "prod"] url = "ssh://suite116@suite116.com/var/chroot/home/content/33/7051533/html" fetch = +refs/heads/:refs/remotes/prod/

    So when I need to deploy to the prod server I do a "git push prod". When I deploy to github I just do a "git push". No more ftp!

  • 1c5643af128c7ebffee890d24aefbdb8_normal

    Love it, nice job with this.

  • Blank-mugshot

    it works with GoDaddy Shared Hosting (note: GoDaddy Shared hosting is come with git v1.8)

  • Blank-mugshot

    just use beanstalkapp :)

  • Gnu-and-penguin-color

    This is excellent. Thank you.

Add a comment