For a long time now, I’ve preferred Vagrant for local development. My starting point of choice for using Vagrant on a project has been the excellent trusty32-lamp VM, maintained by Andrew Berry. However, with Ubuntu 14.04 reaching end of life, Andrew thought to merge the best of trusty32-lamp VM with Laravel’s Homestead. Thus, in a beautiful instance of open source collaboration, it was so.
Homestead is a similarly fashioned Vagrant box, maintained by the Laravel community, built on Ubuntu 18.04. The result of the marriage is a feature packed, ready to go local development environment that includes multiple versions of PHP, your choice of NGINX or Apache, Xdebug support, profiling with XHProf and XHGui, your choice of MySQL or MariaDB, and so much more.
Let’s look at how you would get set up with Homestead for your Drupal project.
tldr;
If you’re the type to dive into code rather than wade through an article, and you’ve worked with Vagrant before, run this, and take the box for a spin. It sets up a stock Drupal 8 site, ready to install. The database name is drupal_homestead
, the root database user and password to install Drupal is homestead
/ secret
.
$ composer create-project m4olivei/drupal-project:8.x-dev drupal_homestead
$ cd drupal_homestead
$ vendor/bin/homestead make
$ vagrant up
Onwards.
Preliminaries
I’m kind of assuming that you’ve worked with Vagrant before, but if you haven’t, fear not! Homestead makes the world of Vagrant very approachable. You’ll just need some software before continuing. You’ll need to install a VM provider, eg. VirtualBox, VMWare, Parallels or Hyper-V. I use VirtualBox, as it’s free and the most straightforward to install. Also, you’ll need Vagrant.
Composer all the things
One really nice thing about Homestead is it can be installed and setup as a composer package. This means that you can easily add and share a Homestead local setup with everyone on your project via version control.
We’ll start with a clone of the Composer Drupal project and add Homestead to it. If you’re adding Homestead to an existing project, skip this step.
$ composer create-project drupal-composer/drupal-project:8.x-dev drupal_homestead --no-interaction
$ cd drupal_homestead
$ git init .
$ git add .
$ git commit -m "Initial commit"
Now that we have a Drupal site to add Homestead to, change into your project directory (wherever your root composer.json file is) and continue by requiring the laravel/homestead
package:
$ composer require laravel/homestead --dev
Home at last
At this point, we’re ready to setup Homestead for our project. Homestead comes with a handy console application which will scaffold some files that are required to provision the Vagrant box. Run the following:
$ vendor/bin/homestead make
This will copy a handful of files to your project directory:
- Homestead.yaml
- Vagrantfile
- after.sh
- aliases
At the very least we’ll want to make tweaks to Homestead.yaml
. By editing Homestead.yaml
we can easily customize the Vagrant box to our liking. In a typical Vagrant box setup, you would edit the Vagrantfile
directly, but here, Homestead exposes the essentials to customize on a per project basis in a much more palatable form. Open up the Homestead.yaml
file in your editor of choice. As of this writing, it’ll look something like this:
ip: 192.168.10.10
memory: 2048
cpus: 1
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
- ~/.ssh/id_rsa
folders:
-
map: /Users/m4olivei/projects/drupal_homestead
to: /home/vagrant/code
sites:
-
map: homestead.test
to: /home/vagrant/code/public
databases:
- homestead
name: drupal-homestead
hostname: drupal-homestead
It’s worth highlighting a couple things here. If you have better than a single core machine, bump the cpus to match. For my 2013 Macbook with a core i7, I’ve set this to cpus: 4
. The VM won’t suck CPU when it’s idle, so take advantage of the performance.
Next folders
lists all the folders you wish to share with your Homestead environment. As the files change on your local machine, they are kept in sync between your local machine and the Homestead environment *.
folders:
-
map: /Users/m4olivei/projects/drupal_homestead
to: /home/vagrant/code
Here all the files from /Users/m4olivei/projects/drupal_homestead
will be shared to the /home/vagrant/code
folder inside the Vagrant box.
Next sites
, as you might guess, lists all of the websites hosted inside the Vagrant box. Homestead can be used for multiple projects. I prefer to keep it to a single project per VM, especially since Drupal codebases tend to be so huge, so we’ll just keep the one site. Homestead ships with the option to use either NGINX or Apache as the web server. The default is NGINX, but if you prefer Apache, like I do, you configure that using the type
property.
sites:
-
map: drupal-homestead.local
to: /home/vagrant/code/web
type: "apache"
xhgui: "true"
Notice I’ve also changed the map property to drupal-homestead.local. That’s for a couple of reasons. First, I want my domain to be unique. Homestead always starts you with a homestead.test domain, assuming you may use the same Homestead instance for all your projects, but that’s not the case for per-project setups. Second, I’ve used .local as the TLD to take advantage of mDNS. Homestead is configured to work with mDNS**, which will mean that you shouldn’t have to mess around with your /etc/hosts file (see caveats), which is nice. I also changed the to property to reflect the web root for our project. Composer Drupal project sets that up as <project root>/web. Finally, I’ve added xhgui: "true" to my site configuration. We’ll talk more about that later.
Next, we’ll customize the database name:
databases:
- drupal_homestead
Homestead will create an empty database for you when you first up the box. Homestead can also automatically backup your database when your Vagrant box is destroyed. If you want that feature, simply add backup: true
to the bottom of your Homestead.yaml.
Finally, we’ll want to add some services. We’ll need mongodb
for profiling with xhprof and xhgui. I also like to use MariaDB, rather than MySQL, and Homestead nicely supports that. Simply add this to the bottom of your Homestead.yaml
:
mongodb: true
mariadb: true
In the end, we have a Homestead.yaml
that looks like this:
ip: 192.168.10.10
memory: 2048
cpus: 4
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
- ~/.ssh/id_rsa
folders:
-
map: /Users/m4olivei/projects/drupal_homestead
to: /home/vagrant/code
sites:
-
map: drupal-homestead.local
to: /home/vagrant/code/web
type: "apache"
xhgui: "true"
databases:
- drupal_homestead
name: drupal-homestead
hostname: drupal-homestead.local
mariadb: true
mongodb: true
There are plenty more configurations you can do. If you want to learn more, see the Homestead documentation.
Fire it up
With all our configuration done, we’re ready to fire it up. In your project directory simply run:
$ vagrant up
On your first time running this, it will take quite a while. It needs to first get the base box, which is a pretty hefty download. It then does all of the provisioning to install and configure all the services necessary. Once it’s finished, visit http://drupal-homestead.local in your browser and you’ll be greeted by the familiar Drupal 8 install screen. Yay!
Features
You get a lot all nicely configured for you with Homestead. I’ll highlight some of my favourites, having come from trusty32-lamp VM. I’m still exploring all the Homestead goodness.
Xdebug
Xdebug comes bundled with Homestead. To enable it, SSH into the Vagrant box using vagrant ssh
and then run:
$ sudo phpenmod xdebug
$ sudo systemctl restart php7.3-fpm
Once enabled, follow your IDE’s instructions to enable debugging.
Profiling with Tideways (xhprof) and xhgui
Every now and again, I find it useful to have a profiler for weeding out poorly performing parts of code. Homestead comes bundled with Tideways and xhgui that make this exercise straightforward. Simply append a xhgui=on
query string parameter to any web request and that request and any that follow are profiled. To read the reports navigate to /xhgui
, eg. for our configuration above, http://drupal-homestead.local/xhgui.
Database snapshots
Here's another of my favourite features. From the documentation:
Homestead supports freezing the state of MySQL and MariaDB databases and branching between them using Logical MySQL Manager. For example, imagine working on a site with a multi-gigabyte database. You can import the database and take a snapshot. After doing some work and creating some test content locally, you may quickly restore back to the original state.
I’ve found this to be a huge time saver for instances where I need to work on issues that only manifest with certain application state stored in the database. Load the database with the errant application state, create a branch using sudo lmm branch errant-state
, try your fix that processes and changes that application's state and if it doesn’t work, sudo lmm merge errant-state
to go back and try again.
Portability and consistency of Vagrant
This is more of a benefit of Vagrant than Homestead, but your local dev environment becomes consistent across platforms and sharable. It solves the classic works-on-my-machine issue without being overly complicated like Docker can be. Homestead does add some simplicity to the configuration over just using Vagrant.
Moar
There are many more features packed in. I mentioned the ease of choosing between Apache and NGINX. Flipping between PHP versions is also easy to do. Front-end tooling including node, yarn, bower, grunt and gulp are included. Your choice of DBMS between MySQL, MariaDB, PostgreSQL and Sqlite is made incredibly easy. Read more about all the features of Homestead in the Homestead documentation.
Caveats
* File sharing
By default, the type of share used will be automatically chosen for your environment. I’ve personally found that for really large Drupal projects, it’s better for performance to set the share type to rsync
. Here is an example of that setup:
folders:
-
map: /Users/m4olivei/projects/drupal_homestead
to: /home/vagrant/code
type: "rsync"
options:
rsync__exclude: [".git/", ".idea"]
rsync__args: ["--verbose", "--archive", "--delete", "-z", "--chmod=g+rwX"]
An rsync
share carries some added maintenance overhead. Namely, you need to ensure that your running vagrant rsync-auto
to automatically detect and share changes on the host up to the Vagrant box. If you need to change files in the Vagrant box, you would kill any vagrant rsync-auto process you have running, vagrant ssh
into the box, make your changes, and then on your host machine run vagrant rsync-back
before running vagrant rsync-auto
again. Not ideal, but worth it for the added performance gain and all the joys of Vagrant local development. There are other options for type
including nfs
. See the Homestead documentation for more details, under “Configuring shared folders”.
** DNS issues
A handful of times I’ve run into issues with mDNS where the *.local
domains don’t resolve. I’ve seen this after running vagrant up
for the first time on a new vagrant box. In that case, I’ve found the fix to be to simply to reload the vagrant box by running vagrant up
. In another instance, I’ve found that *.local
domains fail to resolve after using Cisco AnyConnect VPN. For this case, it sometimes works to reload the vagrant box, and in others, I’ve only been able to fix it by restarting my machine.
Acknowledgments
Big thanks to the following individuals for help with this article:
- Andrew Berry for porting features from trusty32-lamp VM to Homestead and also for technical and editorial feedback.
- Matt Witherow for technical and editorial feedback.
- Photo by Polina Rytova on Unsplash