Extending Homestead: how to customize Laravel's Virtual Machine (the example of Apache)2015/05/13
Homestead offers a nice pre-packaged environment. But as a project grows in complexity, there will be a time where extra packages will be necessary. How to install them properly, and not to lose everything any time we need to recreate the box? How does one extend Homestead?
Impatient kids can skip to the next section.
The more I use Laravel, the more I enjoy it.
I am also quite a fan of Vagrant.
That's actually one of the things that got me interested in the framework in the first place: that they were offering a pre-packaged Vagrant box (namely Homestead). The fact they are using such a cool tool can only be a good sign, right?
But then I realized they were implicitely advocating using the same box for all sorts of PHP projects.
I understand that most of them wouldn't even use all of the included features, but it nonetheless feels like it goes against the principle of Vagrant, that is isolating project environments and more specifically mimicking the production server as closely as possible. It's like leaving the window open for the "it works on my machine" syndrom when the door has been carefully locked.
It also poses another problem: if the included software is pretty good as it is, chances are developers will need extra packages at some point. There will be those who don't really know how to install them, as everything was set up for them from the start, and those who are comfortable enough to do it from the CLI, but then what if they need to recreate the Vagrant box for some reason? All the extra stuff is gone.
I stumbled upon Yitzchak Schaffer's post on the subject, where I got introduced to the "Laragarden" term, which defines the feeling quite well. He explains his concerns in a more detailed way than I do, and if you are interested in the subject I invite you to read his post.
Now those familiar with Vagrant will probably have thought of provisioning by now, and that was my thinking too. I started to look for a clean way to customize Homestead, which led me to this post, in which the author advocates creating a bash script to run any time the Homestead box is (re)created.
This looked clean enough to me, except that I couldn't be bothered executing the script manually every time, hence my decision to edit Homestead's
VagrantFile instead. That's when I realised the guys behind Laravel had that covered already.
More on that in a bit.
Might be worth mentioning that my host machine runs Mac OS, and the steps below might slightly differ if you are on a different OS.
Looking at the
VagrantFile, I noticed a few interesting lines:
afterScriptPath = File.expand_path("~/.homestead/after.sh")
and, later on:
if File.exists? afterScriptPath then config.vm.provision "shell", path: afterScriptPath end
And here is the content of
# If you would like to do some extra provisioning you may # add any commands you wish to this file and they will # be run after the Homestead machine is provisioned.
Gotta love Laravel...
What it means is that after all the base Homestead set up is done, the script will look for a file named
after.sh in the
.homestead/ folder under your user's directory, and run its content if it exists.
This file is created along with the
Homestead.yaml file when you first run
bash init.sh after cloning the Homestead repository. As
~/.homestead/ is an independent folder, and if you work with other developers on the same project(s), it might be a good idea to version it for consistency across the team.
From there, all that is left to do is actually to put the necessary install scripts in
The example of Apache
Let's take a simple example here. Homestead comes with Nginx preinstalled (nothing bad about that, quite the opposite actually), but you might have some projects that need to be run on Apache. If both servers cannot run at the same time, there is no harm in having them both installed and starting one or the other according to the current need.
From your host machine, open a terminal window and edit the
after.sh mentioned above:
Add these lines at the end of it:
sudo apt-get update sudo apt-get install -y apache2
What this will do is it will update the package lists to make sure we'll grab the latest version of the Apache one, and then install it (
-y is there to answer "yes" by default where the user is normally prompted).
Save the file and go to
~/Homestead/, then run:
This will force Homestead to re-run the provision scripts, and install the Apache server using the script you've just added.
ssh your Homestead box and run:
sudo service apache status
If everything went alright, you should get something like that:
* apache2 is not running
This is expected, as it means that 1. Apache has been correctly installed and 2. Nginx is already running and we can't have both running at the same time. What we need to do now is to stop Nginx first and then start Apache:
sudo service nginx stop sudo service apache2 start
Accessing your Homestead machine from the browser should now display the default Apache screen (chances are the address is http://192.168.10.10):
That being said and done, you should be aware that Homestead will not create the Apache Virtual Hosts for you (as it does for the Nginx server confs). You will have to add them yourself in
This also means that if you recreate the Homestead box, these Virtual Hosts will be gone (it is not that difficult to use provisioning to automate this, but this is beyond the scope of this article - more on that in the conclusion).
Here is how to extend your Homestead box in a clean and simple way (all it takes is to update an existing file, after all).
Of course it implies using bash scripts and that might be a bit frustrating for Chef or Puppet users (even though I don't think there would be any harm in updating the
Vagrantfile directly), but I'd say it is a good start already, and it kinda shows that Laravel's intent is not necessarily to lock you up in their Laragarden (even though you won't find any mention of
after.sh in the official documentation, at the time of writing).
To my opinion, Homestead is definitely a good kick-start and a nice first exposure to Vagrant to some users, but as a project grows in complexity, using a dedicated Vagrant box might be a good idea.
By the way, if you want to learn more about Vagrant, I wrote an article explaining how to set up a Vagrant box for local development step by step. It will also show you how to import a Nginx server config, which can be adapted to Apache Virtual Hosts. Just sayin' ;)