Last Updated: September 29, 2021
·
199.1K
· kerrick

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
#!/bin/sh
git checkout -f
^D
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:

#!/bin/sh
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 \
ssh://user@example.com/home/user/www/example.com

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

36 Responses
Add your response

Awesome tips. Thank you.

over 1 year ago ·

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

over 1 year ago ·

@gpoulter

(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.

over 1 year ago ·

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

over 1 year ago ·

+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.

over 1 year ago ·

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)

over 1 year ago ·

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.

over 1 year ago ·

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

over 1 year ago ·

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:

http://adamjohnsondesign.com/blog/moving-ftp-git-deployment-designers-guide/

over 1 year ago ·

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.

over 1 year ago ·

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

over 1 year ago ·

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

over 1 year ago ·

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.

over 1 year ago ·

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

over 1 year ago ·

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

over 1 year ago ·

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.

over 1 year ago ·

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

over 1 year ago ·

@NARKOZ They actually can be wrong.

over 1 year ago ·

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.

over 1 year ago ·

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.

over 1 year ago ·

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.

over 1 year ago ·

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).
over 1 year ago ·

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.

over 1 year ago ·

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.

over 1 year ago ·

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.

over 1 year ago ·
git config core.worktree $(pwd)

Saves some time

over 1 year ago ·

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!

over 1 year ago ·

Love it, nice job with this.

over 1 year ago ·

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

over 1 year ago ·

just use beanstalkapp :)

over 1 year ago ·

This is excellent. Thank you.

over 1 year ago ·

Also what is wrong with this post, though probably a problem with the blog platform not the post: Not dated, and comments are also not dated. Was this posted in 2006, 2010, or 2014? It matters.

over 1 year ago ·

Trying to get this to work and I've gone through the entire process but I still get a "'/html/newproject' does not appear to be a git repository" when I try to push. I'm ssh into godaddy shared hosting. I try to do ssh://username@hostname/html/newproject but apparently that's not right. All the git files show up if I ftp into it.

Anyone have a solution for this?

over 1 year ago ·

Finally time to kill FTP! Also @jore no no no, developing live on the server is not always a brilliant idea, develop locally, use git to a git server with backups and then use git to deploy!!

over 1 year ago ·

Never knew git was able to do this....awesome!

over 1 year ago ·

after git push, git says 'everything up to date' but nothing on the server except .git

over 1 year ago ·