Is Git/GitHub a good WordPress deployment solution?

I’m currently developing my WordPress locally, committing my code to GitHub with Git and then SSHing into my server and doing a “git pull” to update my code. Is this a good option for code deployment onto a WordPress site (I obviously have root level access to my server in this case.) I know of things like Capistrano, but would that be overkill for deployment to a WordPress site? How can I make the most of Git/GitHub in this case?

Related posts

Leave a Reply

4 comments

  1. I use git for this and find it works really well. A few suggestions:

    • Add your uploads directory (wp-content/uploads) directory to your .gitignore file.
    • Run a web server and database server on your development system so you can test changes locally before pushing them to production.
    • Keep your database connection settings consistent beween dev and prod, or add wp-config.php to your .gitignore file to prevent your development wordpress settings overwriting your production ones.
    • Avoid updating plugins on your production system using the admin interface of WordPress – as at best, your git copy will overwrite any plugins you update as soon as you push/checkout, at worst you’ll get conflicts. Do your updates using the the admin interface on your development system, commit, push and checkout in production.
    • Consider adding a git post-receive hook to checkout your updates automatically into the directory you use to publish wordpress via your web server (e.g. /var/www). This allows you to only check out the files themselves, avoiding any git metadata finding it’s way into your web server’s document root, and also means you can add any permission changes into the post-receive hook so your permissions stay consistent every time. An example is included below:

      #!/bin/sh
      unset GIT_INDEX_FILE
      # the directory your web server serves wordpress from 
      export GIT_WORK_TREE=/var/www/example.com/
      # the local directory where your remote git repository sites
      export GIT_DIR=/home/git/repos/example.com.git/
      # below user is for debain - you want the user and group your webserver uses
      sudo git checkout -f
      sudo chown -R www-data:www-data $GIT_WORK_TREE
      sudo chmod -R 755 $GIT_WORK_TREE
      sudo chmod 600 $GIT_WORK_TREE/wp-config.php
      sudo chmod -R 775 $GIT_WORK_TREE/wp-content
      
  2. I would highly recommend setting up Capistrano – it’s a little bit of upfront work the first time, but after that you can easily use it for new setups.

    The main advantages are

    • being able to deploy from your desktop. It may not sound like much, but ssh-ing into the your remote server, and doing a git pull is still a pain in the ass.
    • easy rollback to a previous version if you need to
    • able to do cool things like setup deployment to staging/production environments.

    I’m adding a set of capistrano scripts to show you how I set things up.

    Capfile

    require 'railsless-deploy'
    load 'config/deploy'`
    

    deploy.rb

    set :stages, %w(production staging local)
    set :default_stage, "staging"
    require 'capistrano/ext/multistage'
    
    set :application, "" # your application name - used to set directory name
    
    set :scm, :git
    set :repository, "" # use the ssh repo access line you get from the provider eg git@github.com:name/repo.git
    set :deploy_to, "/var/www/#{application}" #this is the root site folder on the remote server
    set :deploy_via, :remote_cache # get directly from repo
    set :copy_exclude, [".git", ".DS_Store", ".gitignore", ".gitmodules", "wp-config.php"]
    
    # makes capistrano ask for sudo password or other remote inputs
    default_run_options[:pty] = true
    
    namespace :tasks do
        task :fix_links  do
            run "ln -nfs #{shared_path}/uploads #{release_path}/wp-content/uploads"
            run "ln -nfs #{shared_path}/wp-config.php #{release_path}/wp-config.php"
          run "ln -nfs #{shared_path}/blogs.dir #{release_path}/wp-content/blogs.dir"
          run "ln -nfs #{shared_path}/.htaccess #{release_path}/.htaccess"
          run "sudo chown -R www-data.www-data #{release_path}/"
        end
    end
    
    after "deploy", "tasks:fix_links"
    

    and finally, a sample environment file (if you use the multistage gem, then you can have one of these for each stage of your environment, eg local, staging, production)

    config/local.rb

    server "", :app  #hostname
    set :branch, 'develop' #choose branch to deploy
    set :use_sudo, false #don't use sudo
    
    set :deploy_to, "/var/www/#{application}" #overwrite default path to deploy to
    

    These files might not work without tweaking, and you’ll need some basic Capistrano knowledge, but hopefully will help some people.

    This was the first tutorial I used that got me going with Capistrano and WordPress: http://theme.fm/2011/08/tutorial-deploying-wordpress-with-capistrano-2082/

  3. I actually did a WordCamp presentation on this topic. Rather than repeat myself, here’s a screencast of it and here’s a very simple deployment script to accompany what I discussed.

    In short, I use GitHub to host the repo, and use a webhook to deploy changes based on the git ref. This allows you to use Vincent Driessen’s git branching model and opens you up to having unlimited webheads, staging servers, testing servers, etc., all with automated deployment. I also cover keeping wp-config.php under version control while maintaining separate dev/production versions (by renaming the files and symlinking).

  4. I know this question is a little older however as I’ve not seen this as answer here, I’d like to share what I normally do for single-site git based setups and deployments and it’s working really well, also with working from multiple devices, locations and with multiple developers (all having their own local repos they operate in as it is common for git).

    I can warm-heartedly suggest the following setup:

    It is also outlined in (if you need a second resource to wrap your head around it):

    It basically works (with at least three repos) by:

    1. putting the website on the live-host under git,
    2. create a new bare git repository on the live host.
    3. And then fork from the bare repository to your local development git repo(s).

    When the work is done you push against the remote bare repo you’ve cloned from. The bare repo has hooks to sync with the live repo (in the codes above called prime).

    As WordPress specific settings in the repo I have this .gitignore:

    # uploads are data, excluded from source tree
    wp-content/uploads/
    

    The rest incl. the plugin and theme configuration I keep under version/configuration control. This allows me to easily track changes and review code before using it live. I can also more easily merge against remote trees with my own changes. That is especially useful against the WordPress core which is available on Github.

    This works pretty well for most of my WordPress needs. The bare repo prevents you from pushing conflicting changes. It also syncs to a remote copy first before updating the live-site. That means, updating the live-site is normally pretty fast. Because of the hooks you can even call WordPress update hooks afterwards if you like.

    If have not experimented how much this can be improved with Github hooks, but I normally do not need them as the code is under local version control, not Github.

    To set such a system up for the first time, you should take some time to evaluate if you’ve got all the tools available on your remote host:

    • SSH access
    • GIT
    • A private directory you can put files and sub-directories in (e.g. for your bare git repo)

    The setup-time for the first time should be possible within one two hours incl. the whole environment and you first publish push.

    Depending on your host, you might also want to shield the .git directory from web-access. Here is some example .htaccess code that even does so having WordPress placed inside a sub-directory, which leaves space in the repo not published online (useful):

    Options -Indexes
    
    # fix trailing slash for .git / make it disappear + .gitignore and similar files.
    RedirectMatch 404 ^/.git(.*)$
    
    # mask 403 on .ht* as 404
    <Files ~ "^.ht">
      Order Deny,Allow
      Allow from all
      Satisfy All
      Redirect 404 /
    </Files>
    
    RewriteEngine On
    RewriteBase /
    
    # map everything into public and set environment var
    # to tag the request being valid
    RewriteCond %{ENV:REDIRECT_sitealias} !set
    RewriteRule ^(.*)$ /public/$1 [E=sitealias:set,L]
    

    In short, everything not inside the public directory is not online. Inside the public directory can be the wordpress codebase for example, for the .htaccess there you need then:

    RewriteEngine On
    # mask as 404 if directly accessed
    RewriteCond %{ENV:REDIRECT_sitealias} !set
    RewriteRule .* - [L,R=404]
    

    This prevents direct access to public. Part of this .htaccess-foo you can find outlined here: Requests to .htaccess should return 404 instead of 403. For the environment variables you need to test if that works in your environment. Also you need to decide if you put that under version control or not.

    If you have more control on the hosting, you can do more stuff here (and differently / more optimized), the examples above are targeted for typical shared-hosting environments (that offer GIT, some users say you can easily install it your own as well, I normally ask my hosters to provide such because I prefer if they take care that’s what I pay them for).

    On the negative side, this has some of the common problems also outlined in the other answers. One thing I’m not proud of but what works is to give the development host a change to it’s host file to have the database server point to the development copy. So you can keep one database configuration. Not really cool esp. because of the credentials.

    Automatic Backups

    However I normally don’t care much here but instead have daily backups run on the remote systems which are incrementally which themselves are stored in another remote location then. That’s easy and cheap and allows you to restore both the WordPress install as well as the file-uploads, the database and the git repo. Also for my backup commands I might not be perfectly alright, but those work for me:

    mysql: mysqldump --host=%s -u %s --password=%s %s| gzip > %s
    git  : git gc
           git bundle
    files: tar --force-local -czf %s %s
    

    What I do suggest here is that you keep the processes around your WordPress installation out of WordPress. They need to run on a specific system, so you normally do not have them inside the application (e.g. application can go down but you need to have these continue to work).

    Enabled for Teamwork

    Another nice benefit is that you site is enabled for team-work already. Thanks to the additional bare repo you can’t do much wrong and you can even share remote branches apart from a master or live branch with your colleagues.