The Ultimate Workflow Guide for Teams Building WordPress Sites with ACF, Timber, Foundation, and Local Machines with Remote Servers Through Vagrant and Git.

The Pursuit

As Partner and Technical Lead, I encourage my employees to always be learning with an eye on improving the quality and efficiency of what we deliver to our clients. As an agency, it can be difficult to lock down a workflow. Each project can be vastly different. Yet, we still need to align our workflows and development environments as much as possible to ensure productivity is at its peak. sd

This guide was created to manage varying client work. I’ve spent most of my days recently inside of Vagrant machines banging on Python (Django) and Ruby (Rails). While utilizing MVC patterns and Foundation. Working in these true development environments opened my eyes to where our WordPress workflow failed. This post details how we built an efficient and repeatable workflow for teams developing on WordPress.

Please download all of the following components used in the guide, either now or during setup.

Advanced Custom Fields –
A free WordPress plugin which acts as a GUI layer for custom fields allowing for more flexible data and CMS possibilities.

Timber –
Timber helps you create fully-customized WordPress themes faster with more sustainable code. With Timber, you write your HTML using the Twig Template Engine separate from your PHP files. This cleans-up your theme code so your PHP file can focus on supplying the data and logic, while your twig file can focus 100% on the display and HTML.

UNDERWOOD THEME: Foundation 5/Timber WordPress Theme –
This is a WordPress starter theme based on Foundation 5 by Zurb. This theme differs from the rest. It makes use of the Timber Library, allowing you to use the Twig templating language.

VVV Varying Vagrant Vagrants –
Varying Vagrant Vagrants is an evolving Vagrant configuration focused on WordPress development. The goal of Varying Vagrant Vagrants (VVV) is to provide an approachable development environment with a common server configuration for high traffic WordPress sites.

Variable VVV: VVV Site Wizard –
VV makes it extremely easy to create a new WordPress site by using Varying Vagrant Vagrants. VV supports site creation with several options: site blueprints to set up your plugins, themes, deployments; and plenty additional features.

WP Sync DB –
This WordPress plugin that lets you push, pull, and sync database tables between WordPress installations. WP Sync DB eliminates the manual work of migrating a WP database. Copy your db from one WP install to another with a single-click in your dashboard. This is especially handy for syncing a local development database with a live site.


Before we dive in, it’s important we have a complete understanding of what we’re doing and why we’re doing it. We’re striving to achieve a local development environment driven by a Vagrant machine that is easily deployable to a remote or live server through Git and Github. Inside of this Vagrant machine, we’re using Timber and Foundation in to create faster, more sustainable code with a rock-solid front-end framework. With Timber you manage your theme in PHP and Twig (HTML) files. This separates the logic layer of WordPress from the presentation layer. It’s just like Rails, Django, Node and other platforms. With Foundation, we can quickly create beautiful responsive websites, taking advantage of the built in grid, and components to increase efficiency. Lastly, we use WP Sync DB to push and pull database changes from our local and remote WordPress instances.

Okay, so why is this awesome? It’s awesome because this workflow falls inline with the current direction of most other development workflows. This is especially true within teams. If you have multiple people working on the same code, then we absolutely need to use source control to create feature branches and manage our merges and pull requests. This ensures we’re not breaking the code or causing additional code debt. It’s awesome because WordPress is easier to manage when we separate the logic and presentation, similar to more sophisticated development frameworks. It’s awesome because virtual machines allow us to spin up environments insanely fast, with very little configuration. This also ensures that the environment is identical for all team members. Lastly, it’s awesome because we need to kill off bad practices, even if they are faster or more familiar. The best and most notable example is developing WordPress directly over FTP. Yes, it’s fast, but there are better ways. As front end developers, we have so many great tools to increase our workflow, but we have to be okay with breaking old habits, trying new things, and continuously learning.

Setup: Vagrant Machine

Alright, let’s dive in. First, let’s setup our Vagrant machine. Follow these instructions to ensure you can get up and running: The ideal scenario here is to cd into your VVV directory, enter vagrant up and then go to to see a new WordPress environment. For the sake of repeating instructions, follow the first link since the Github repo will always be up to date. Place your final VVV directory somewhere in your filesystem, where you other development files reside. For example, I have most of my development living under ~/Documents/Development. This VVV directory is going to act as a development environment with your different WordPress sites living inside a folder called /www.

Please be sure to read the Varying Vagrant Vagrants Github repository in its entirety to fully understand what this machine is doing. We’re essentially treating our VVV instance as a MAMP/XAMPP replacement. Read more here:


Setup: WordPress Site Inside of Vagrant

Now that you have your development environment up and running, feel free to play around in the VVV directory. If you want to start banging on code right away, go to /VVV/www/wordpress-default to start editing. You’ll see all of your changes live at Since we’re treating our VVV directory as a development environment, be sure to create new sites inside of it so we don’t end up editing the defaults. This will guarantee you only need 1 copy of VVV instead of downloading a fresh copy each time you want to make a new WordPress site.

Go over to, and follow the instructions. If you’ve made it past setting up Vagrant, this should be a piece of cake. Once you have this installed, you can verify it’s working by typing vv in terminal or iTerm. You should get back the typical Usage, Options, Commands, etc. Once you verify, we can jump in to creating a new WordPress site. Note: you may get this error: “[Error] Path specified is not a VVV root directory. Where is VVV installed?”. You can ignore it for the time being and ctrl+c out.

New Site Setup

Alright, let’s do it. Follow the commands below inside of Terminal.

  cd ~/Documents/DevelopmentWherever you like to store your dev files.
  vv createYou’ll get back an error, and prompt asking you where VVV is installed. Enter the path to your install.
  You’ll now be taken through a prompt to setup your new site.
  Name of new site directory: SITE_NAME
  Domain to use (leave blank for left this blank.
  WordPress version to install (leave blank for latest version or trunk for trunk/nightly version):I left this blank.
  Install as multisite? (y/N): N
  Git repo to clone as wp-content (leave blank to skip):I left this blank. We’ll come back to this.
  Local SQL file to import for database (leave blank to skip):I left this blank.
  Remove default themes and plugins? (y/N): N
  Add sample content to site (y/N): N
  Enable WP_DEBUG and WP_DEBUG_LOG (y/N): y
  Continue (y/n)? : y

The site wizard will now perform its magic and install your new site inside of Vagrant. You can find your new site here:

Username: admin
Password: password

Ta-da! Congratulations! You’ve created a shiny new WordPress site inside of your Vagrant machine. What’s next?


Bonus. If you want to delete a site in it’s entirety. Simply type:

  vv delete
  ~/Documents/Development/VVVEnter path to VVV
  Site directory to delete: SITE_NAME
  Continue (y/n)? : y

Setup Local Git Repo/Link with Github

Now that we have our site up and running, it’s time to connect it with Github. Through trial and error, I’ve found it’s best when working by myself or with teams to only sync the /wp-content folder and not all of WordPress. This will grab all of your themes, plugins, and uploads.

  cd ~/Documents/Development/Github/VVV/www/SITE_NAME/htdocs/wp-content
  git init This will initialize an empty git repo with a working tree.

Head over to Github and create a new repository.

  git remote add origin
  git add —all
  git commit -am “Init commit of /wp-content dir"
  git push -u origin master

Fantastic. Now, you can continue to edit your /wp-content folder, inside of your site and push the changes to Github. Remember, the Git repo is our /wp-content folder inside of our theme, and not the root SITE_NAME.


Setup Remote Git Repo/Link with Github

Local environments are great, but only if they are easy to push to a live URL so others can see, play, and test. You will need ssh (shell access) for your server with this step. Small plug, I use for all of my development. I’ve been using them for over 10 years, and cannot strive enough how great they are with developer support.

Site5 SSH setup link: This should be similar to any service provider you use, assuming they grant shell access. I am going to continue on to the next step, assuming you’ve setup ssh, and can connect successfully.

Install WordPress on your server. This may be through a 1 step button, or manually. Your preference.

  cd /www/SITE_NAME/wp-contentSince your directories may be different, the above path is an example path that I use.
  rm -rf *Delete all of the contents inside of wp-content. You can do this through FTP, or you can type the command above. We’re going to replace the contents with your git repo in a minute.
  git clone . The period at the end allows us to clone into current dir without creating a folder.

To keep your server up to date with Github.

  cd /www/SITE_NAME/wp-content
  git pull

Pretty easy and awesome, but how can we make it awesome-er? Well, it would sure be nice to remove that last git pull step and just have it be automatic. Let’s set that up.


Automatic Git Pull on Server

Github never ceases to amaze me. What we’re going to do now is setup a webhook. Webhooks allow you to build or set up integrations which subscribe to certain events on When one of those events is triggered, we’ll send a HTTP POST payload to the webhook’s configured URL. Read more here:

  cd /VVV/www/SITE_NAME/htdocs/wp-content
  echo '<?php `git pull`;' >github.phpThis creates a new file, github.php with the contents of <?php `git pull`;’ >. This is a .php script that simply executes “git pull”.
  git add —all
  git commit -am “Added git sync file"
  git push


Ok, now we have a file, called github.php inside of our /wp-content folder and it’s pushed to our Github repo. However, this file is not yet on our server. Let’s get it on the server. This will be the last time we need to do this.

  cd /www/SITE_NAME/wp-content
  git pull


Awesome. Now our server has the github.php file. Copy the URL of this file. It should look something like:

Now, go on over to the below url:


Click Add webhook
Paste URL of your github.php file in Payload URL
Ignore Content Type, Secret, and leave “Just the push event” radio button selected.
Click Add webhook

If setup properly, the github.php file will execute “git pull” every time we push to this repo from our local. Thus, our local repo is in sync with our live URL, automatically. Pure awesome sauce. I haven’t done this yet, but to make it even better I would suggest setting up a staging URL that is pulled automatically, then a production URL that does NOT use this script. This way you’ll be able to login to production and do a pull when you feel like staging is reviewed and tested.

Download and Install Theme and Plugins

At this point, we have our Vagrant machine and WordPress site up and running and we can easily push changes to our server through Git. Now, let’s get Timber installed along with our themes and our database sync plugin. Download the .ZIP files below:

Advanced Custom Fields:
UNDERWOOD THEME: (.ZIP button in right sidebar)
WP Sync DB: (.ZIP button in right sidebar)

  cd /VVV/www/SITE_NAME/htdocs/wp-content
  open .

Extract your plugin .ZIPs and move your ACF, Timber and WP Sync DB to your plugins folder
Extract your theme .ZIP and move the UNDERWOOD THEME to your themes folder
Go to (admin/password)
Activate your plugins and set your theme.

Gulp and the Asset Pipeline

Once complete, your site should look like the screenshot below.


Currently, our theme is a bit broken. This is because our theme is using Gulp. Gulp is a task/build runner for development. It allows you to do a lot of stuff within your development workflow. You can compile sass files, uglify and compress js files, and much more. Right now, our asset pipeline does not have any generated CSS file (main.css). This is the core issue.

  cd /VVV/www/SITE_NAME/htdocs/wp-content/themes/timber-foundation-theme-master
  npm install && bower install && gulpWe're installing required package managers and running gulp. You may need to do these individually, and append sudo. You can read more information here:


Now that those dependencies are installed, simply cd into your theme folder and run gulp to begin compiling your CSS and other assets.

  cd /VVV/www/SITE_NAME/htdocs/wp-content/themes/timber-foundation-theme-master


Modifying our .gitignore for our Remote

Perhaps you jumped the gun and pushed all of our changes thus far. If you did, you’ll notice that our theme is still broken on our live server. This is because we need to modify the .gitignore that comes with our theme.

  cd /VVV/www/SITE_NAME/htdocs/wp-content/themes/timber-foundation-theme-master
  atom open .gitignore Open our .gitignore with whatever editor you use. I do recommend atom though!

Delete /js/ (line 6)
Delete /css/ (line 7)


Now, when you push your changes, your /css and /js directories will be pushed as well. Go ahead and push everything live.

  git add —all
  git commit -am “Added theme, plugins, and modified .gitignore"
  git push

You probably don’t have to do this step if you have your server automatically compiling assets. There should be plugins to help you with this. I haven’t tried yet, but I’ll update later if this does actually work better.


Syncing Databases between Local/Remote

To be clear, we’re not keeping our database inside of Git. Instead, like Git, WP Sync DB utilizes the same push/pull mentality and makes syncing simple. This is really great if you have your remote server setup and client facing, as a staging server. You can let your client add content at will and simply grab their database changes when they are finished. Assuming you’ve already pushed the plugins folder to your live server, let’s begin syncing below.

Activate the plugin on both your local and remote site
Add some pages on your remote site
Go to http://SERVER_NAME/SITE_NAME/wp-admin (admin/password)
Go to Tools > Migrate DB
Go to Settings
Copy Connection Info string
Check “Accept pull requests…” checkbox
On Local, go to Tools > Migrate DB
Click Pull radio button and paste Connection Info string
Adjust options to your liking (Only thing I check is to exclude spam comments)
Click Migrate DB
Viola, your local database is now what was on remote in a matter of a couple minutes.

You can of course do this vice versa as well. For more information, I suggest reading about the plugin on Github, They also have some easy to follow videos located here,


Develop your Theme/Site!

If you’ve made it this far, congrats! Your workflow is now complete. Let’s pretend we’re just waking up and want to start banging out some code. Little refresher below of how to get up and running.

Note: If you’re done developing for the day, run the command below.

  vagrant haltThis suspends our Vagrant machine. You should run this when you’re done developing. Read more here:

Assuming you’re now starting the day, follow the workflow below.

  cd /VVV/www/SITE_NAME/htdocs/wp-content/themes/timber-foundation-theme-master
  vagrant upStarts our virtual environment.
  gulpStarts our asset compiling, most notably for SASS/CSS.

Code to your heart’s desire.
See your changes at

  git add —all
  git commit -am “Change log message."
  git push

See your changes, http://SERVER_NAME/SITE_NAME/
And of course, sync your databases as needed.

And that’s all folks

I hope you find this guide useful. This is purely a workflow tutorial and did not cover the basics with Timber/Twig or how we like to work with ACF. Look out for these posts in the future.

Follow us on Twitter, Facebook, and our Log.

Roket is a small group of designers and developers creating transformative products and brand experiences for startups and growing companies. We are entrepreneurial-minded, efficient doers who combine design and development to solve complex business problems. We have transparent and client-inclusive process and tools to help get our client’s further, faster. We’d love to work on your next challenge, email us at or call 412-444-4616.


  1. Really great article! Thanks for sharing. Can I email you if I have any problems arise?

  2. Love this post. Really appreciate the effort and detail.

  3. Nice post Alex!

    As a bonus, I highly recommend installing a nice dashboard for VVV, I use this one:

    1. alexhendershott

      Great addition Thad! I will look into this and edit the instructions if we integrate. Thanks for the heads up!

  4. I really appreciate this clear, detailed instructions and the effort in putting this together and sharing. I’m sharing it with co-workers to make the jump.

    1. alexhendershott

      No problem Pablo! Let me know how it goes. If you have any questions, feel free to email me at alex[at]roketco[dotcom]!

  5. Great tutorial. So much respect. Only noob friendly one that makes sense!

    1. alexhendershott

      Thanks Ben! Don’t hesitate to reach out if you have any questions. There are additional lessons learned I have not included here. If you have any questions, feel free to email me at alex[at]roketco[dotcom]!

  6. Just wanted to show my appreciation for a great article – thank you ! We’re having a go at implementing your workflow !

    1. alexhendershott

      Awesome! Great to hear, Chris. Let me know how it goes. If you have any questions, feel free to email me at alex[at]roketco[dotcom]!

  7. Just wanted to know how you handle merging data between local and live?

    So for instance, you’ve pushed everything to the live site from local. The client is editing the live site as you’re creating new features on the local site (changing both files and data)… so it can’t be a case of just copying over the live site.

    A lot of articles I read are perfect world scenarios – so when you added new features, change data on the local site, the live site isn’t touched. So it’s a simple case of copying over the live site with the local site.

    1. alexhendershott

      Well you nailed the perfect world scenario. Honestly, it’s tough. I don’t have an answer for a great workflow, but I can share what we do. Our client sites aren’t updated every day. And even if it’s every other day, it’s usually only blog content or something similar.

      1. Communication. The client knows we’re updating, and we have a timeline. Likewise, we know what they’ll be editing, and when, if anything. We’re open with our clients during development and there is a general, “paused” expectation. I cannot recall a time when this has failed or data has been lost as long as everyone knew the gears we’re turning at the same time.

      2. Backups. We’ll do dailies and prior to start of dev and prior to final push. We of course have everything tracked with Git, and we have the WP Sync DB backups. However, the additional backup in our workflow is It’s just easy to install, save, and deploy on another test server. It’s nice to do this as a guarantee, rather than worrying about individual data. It also makes it very easy to simply compare before/after data, prior to destroying anything.

      3. Stick to a strict Local/Staging/Production Flow. We never do database edits on local. We do them on Staging, and then pull from Staging to Local. Since we’re working in teams, it’s easier to do database changes on a shared resource to avoid overriding other people’s changes they push and then don’t pull. Communication is important here too. Let people know you’re making changes and how they might be affected prior to starting.

      Hope this helps. I’ll share a better workflow if we ever get there. It probably exists, but what we’re doing now has worked pretty well. It’s fallen down a couple times, but never on a live site that can’t be fixes very quickly, and never without any means to recover.

  8. Great article thank you.
    I’m wondering if you have issues having nested git repos?

    With vvv being a repository and then having project repositories inside of the vvv directory…any issues with keeping vvv updated etc?

    1. Never had an issue here. Come to think of it though, I did not clone VVV. I downloaded the .ZIP. Let me know if you have any issues if you are cloning. We’re typically working on 1 primary client project at a time here at Roket. I imagine a good strategy going forward would be to update VVV in between projects when you have cleanup time.

      Just as an FYI, I’ll be making some updates to this article soon. Better .gitignore strategy and our now preferred method to handle images with Amazon S3 instead of keeping them in the Github repo!

  9. Have you used Wordmove?

    I use your exact setup, except for the git-pull on the server part. I’ll have a Movefile in the root of all my sites and when it’s time to push I can push to staging, then if satisfied, push to production. This pushes the theme, uploads, plugins, and database (with the proper url modifications on the database each time).

    If you need to get the database from production, you can pull production, and push to staging. Easy as that. I’d like to know your thoughts on it. Thanks!

    1. Hi Sam. Thanks for sharing! I have not heard of Wordmove but it looks quite cool. I will try it out on our next project and let you know my thoughts!

      I’m curious, how long goes a migrate take? Are you pushing changes, or replacing all? Can you specify easily what you want to push or pull?

  10. Fantastic in-depth article, thanks but I’m a bit confused on the Automatic Git Pull on Server step however.

    If I SSH into my remote server, CD to the /wp-contents folder and run ‘git pull’, should I also be specifying the repo with git pull? If I add the URL to my repository (e.g. then I get a “Not a git repository…” error.

    1. Thanks Jon!

      Hmm, did you go through “Setup Remote Git Repo/Link with Github” in the guide above? The git pull doesn’t need to be specified because you cloned the contents of the repo in the previous step. However, we are using Github, and not Bitbucket. Confirm for me you went through the setup step first and I’ll see if I can do a bit more research!

      1. Hi Alex, I had completed that step but yes, I’m using Bitbucket and not Github. I was quietly hopeful that the webhook instructions for this would be compatible…

        Sanity check – can you confirm the syntax and formatting you’ve used for the contents of github.php? This doesn’t seem like valid PHP to me (it errors if I request the page in a browser). I’ve pasted the example above at


  11. Hey Alex, this is a great article and looks like a great workflow. The one piece that’s holding me up is how to manage third-party plugins and/or uploads. I guess this is a similar question to the one from Rob Jones.

    You mentioned that you use a strict local/staging/prod flow, and only make db edits on staging. Since (some/many/most) plugins modify db tables, does this apply to plugins as well?

    Let’s say, for example, that your team is developing a site, and couple of iterations in you need to add an event management plugin. I see a couple of scenarios:

    1. Add plugin in staging, push changes from staging to remote repo, have team pull from the repo and db sync with staging.

    2. Add plugin locally, push changes to staging, sync db with staging, have team pull from (now updated) staging.

    I’m sure there are other scenarios, but I can’t quite get my head around managing plugins, and whether its worth the effort.

    Thanks again for documenting a great workflow!

    1. Hi Eric! Thanks for the compliments. The guide is a tad out of date now, but I will do my best to update it soon. One big change is no more uploads in Git. Looking back, this was a poor decision, and we’ve since moved most image serving to Amazon S3. More on that to come.

      Regarding plugins, we’re still keeping them in Git. I believe we’ve done both of your outlined methods in 1 and 2. But, from our implementations thus far, neither one really mattered. The much bigger pain point was management of plugins AFTER they’ve been circulated to all of the environments (mostly updating). For this, we’ve had a slightly different workflow.

      1. Either on local or on staging, create a quick test branch for updating plugins. Update all, or specific plugins, and verify that code is still functioning as expected. If it is working, stash your changes, drop them, and delete the branch. If something breaks, fix the issue, and check in the fix without the plugin updates.

      2. On the production server, or whatever your highest level environment is, update the plugins. Commit those updates, and check into Git.

      3. Pull down from local, and sync DB. Plugins should be updated and database should be fine.

      We’ve encountered many headaches where plugins are updated on local or staging, and then pushed to production and the Git workflow breaks, is out of sync, or code stops functioning. This scenario is not very pleasant, especially if it’s your final environment. Honestly though, in the future we may stop tracking plugins along with uploads. We’ll probably be testing this soon, so I’ll be sure to keep you up to date!

      1. Hi Alex,

        Would you mind commenting on your latest workflow compared to this article and your latest comments?

        I wish I had this article last year and it’s quite similar to my current flow. Timber is a nice discovery for me.


        1. Hi Raoul,

          Since writing this guide, our workflow has not changed much. The biggest addition is getting rid of images in Git and using Amazon S3 and Cloudfront to host/distribute. I’ll add this to the guide over the coming weeks and shoot you an update! I think one other notable piece to add would be the daily workflow of updates. The real scenario of adding new features to a live client site that is updated on a daily basis by the client and by us (the agency). You can assume some of the workflow from the guide, but it can be tricky. I’ll include this as well!

  12. I’ve been waiting for months checking back every so often… Any updates on helping a rookie with your latest workflow to learn from? This sounds perfect but would like to utilize AWS and version control.. Thanks!! Hope you get some time to work on this!! :)

  13. It’s so adorable… and clean.

    All of this explanation is true & works!!

    Realy Thanks!!

  14. Thank you so much for this article! I’ve read probably 50 and still cant get this workflow to work but i keep trying. Would love to see a video of you going through the process, might help me catch what my errors are. Really appreciate the work you put into this and am looking forward to the updated workflow.


  15. I’m late to the party here, but this guide massively helped me setup VVV, and now I am building WordPress sites locally for the first time. This is a massive boost in efficiency for me, so thank you.

    I’ve got the important parts setup, but now I’d like to automate the git pull to server process. I’ve followed the steps in your section, “Automatic Git Pull on Server,” but the pulls are not occurring as expected. The webhook on GitHub is indeed firing every time I push, but the server is not pulling. Any ideas on how to troubleshoot that?

    BTW nice directory name: gazorpazorp. Gazorpazorp-field. White guilt…milquetoast…

Leave a Reply

Your email address will not be published. Required fields are marked *