Despite working on Liquid Web’s Managed WordPress and Managed WooCommerce hosting products, a fair amount of the development work I do these days has very little to do with WordPress. In fact, my main project right now is using Laravel, and it’s the sixth Laravel application (depending on how you count projects) I’ve worked on in just under two years at the company.
Laravel’s an incredibly powerful application framework with a thriving ecosystem. Thanks to tools like Composer and Packagist, I have access to thousands of libraries, extensions, and utilities to help me build the best applications possible. Even out of the box, the framework has support for (among many other things) multiple database and caching engines, event-driven architecture, and websockets, giving me a strong foundation for building modern web applications.
Of course, incorporating multiple platforms and tools into a single application can make on-boarding new team members more difficult. How do you make sure they’re running the right versions of PHP, your RDBMS of choice, Redis, and more?
Easy environments with Laravel Homestead
While containerization is all the rage right now, I’m still a huge fan of Laravel Homestead, the official virtual machine for Laravel development.
Homestead comes with the most common tools for Laravel sites right out of the box: the latest versions of PHP, MySQL, Postgres, and more. Homestead maintainer (and friend of mine) Joe Ferguson works hard to keep Homestead up-to-date, lean, and well-suited for Laravel development.
Laravel Homestead enables the virtual machine to be installed on a per-project basis, which (in my opinion) is highly-preferable to one global installation. The machine can be installed, tailored to the needs of the current project, then disposed of once it’s no longer needed.
Adding Laravel Homestead to a project
To add Laravel Homestead to a project, it’s a matter of adding it as a development dependency via Composer:
1 |
$ composer require --dev laravel/homestead |
Once the package is installed, we need to do a few more things to set up our Homestead box:
First, ensure that both Vagrant and a supported provider (VirtualBox, VMWare, etc.) are installed on your machine — Homestead defines a Vagrant virtual machine, but the provider is what’s actually running the VM.
Next, we’ll run the Homestead installer:
1 |
$ php vendor/bin/homestead make |
This will create a few files in your project:
after.sh
(additional steps to run after provisioning)aliases
(custom aliases and functions that will be imported into thevagrant
user’s shell environment)Homestead.yaml
(the Homestead configuration — more on this in a moment)Vagrantfile
(the actual Vagrant configuration)
All but one of these files — after.sh
should be added to your project’s .gitignore
file, as we don’t want them to be versioned. Homestead may take care of adding these for you, but all of the following should be in your .gitignore
file:
1 2 3 4 |
.vagrant aliases Homestead.yaml Vagrantfile |
Why do we exclude these files? Not every environment will have a Vagrant configuration (production, CI environments, staging, etc.), and different users might have different host machine configurations.
Before we do anything else, let’s take a look at the Homestead.yaml
file, as this defines how Homestead should work.
Understanding Homestead.yaml
By default, your Homestead.yaml
file will look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
ip: 192.168.10.10 memory: 2048 cpus: 1 provider: virtualbox authorize: ~/.ssh/id_rsa.pub keys: - ~/.ssh/id_rsa folders: - map: /Users/steve/Sites/my-test-app to: /home/vagrant/code sites: - map: homestead.test to: /home/vagrant/code/public databases: - homestead name: my-test-app hostname: my-test-app |
Some of the settings will seem pretty self-evident: in this file, my Vagrant box will be available at 192.168.10.10
and utilize 1 CPU core with 2GB of RAM. My host machine (e.g. my Mac) uses Virtualbox as its provider, and my SSH public key — which lives in ~/.ssh/id_rsa.pub
will be imported.
The next node is folders:
, which tells the virtual machine that /Users/steve/Sites/my-test-app
should be available at /home/vagrant/code
within the Homestead box. If I needed multiple directories to be mapped, I could add more entries under this heading.
sites:
describes how Homestead’s web server (nginx by default, but Apache is available) will be configured. In the default code above, accessing https://homestead.test
from my host machine will resolve to the public
directory of my Laravel application.
Next up, we come to the database:
key: this is a list of one or more databases that should automatically be created when the box is provisioned. Most applications will only need one database, and the default .env.example
file that ships with Laravel already has the credentials (homestead
/secret
) configured.
The last of the defaults, name:
and hostname:
will define the name of the virtual machine and the VM’s hostname, respectively. By default, Homestead will attempt to determine these based on the project directory name.
Starting our Homestead VM
Once we’re satisfied with the Homestead.yaml
file, we can finally start up our new virtual machine by running vagrant up
. Vagrant will handle downloading the base box and applying all of Homestead’s customizations, giving you a full-featured development environment in a few minutes (or less)!
Once the virtual machine has been provisioned, we’ll run vagrant ssh
to SSH into the server. From here, all of our work — running tests, compiling JavaScript, etc. — will be done within Homestead.
At the end of the day, exiting the SSH session and running vagrant halt
will spin down the virtual machine, ready for whenever you come back to the project. If you’re done on the project for a while, you may destroy it with vagrant destroy
. If you’ve made changes to the Homestead.yaml
file, you may re-provision the VM by running vagrant provision
.
Customizing Laravel Homestead for a project
Now that we have a per-project instance of Laravel Homestead, let’s tailor the installation process to our project.
Contributing documentation
Whether a project has multiple team members working on it or is open-source and looking for contributors, starting with a strong contribution guidelines document is important. Typically, this will be stored in the root of the project as CONTRIBUTING.md
.
An example of a current project I’m working on gives other developers everything they need to know to get started:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Contributing to <PROJECT> ## Installation For local development, the easiest way to get started is via [Laravel Homestead](https://laravel.com/docs/master/homestead), the official [Vagrant](https://www.vagrantup.com/) box for Laravel development. The Homestead virtual machine contains all of the necessary tools for building and running the application. To get started, begin by cloning this Git repository onto your local machine, then run the following commands: ```sh # Install Composer dependencies. $ composer install # Prepare the repository for Laravel Homestead. $ composer homestead # Provision the Vagrant VM. $ vagrant up # Finally, SSH into the Vagrant box to work with the app: $ vagrant ssh ``` |
Four steps. That’s all it takes to get this environment up and running. A recent addition to our team went through this and exclaimed “wait, that’s it? That’s all I need to do to get started?!”
Of course, getting on-boarding down to that few steps requires a bit of work:
Composer scripting
There are a few scripts I always like to add to my Laravel projects’ composer.json
file: starting with this one-liner:
1 2 3 4 5 6 7 8 |
{ "scripts": { "post-install-cmd": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env') \ && system('php artisan key:generate');\"" ] } } |
This script will do the following anytime someone runs composer install
:
- Check to see if a local
.env
file exists. If one does, do nothing more. - If a
.env
file doesn’t exist, copy.env.example
(which comes with Laravel installations out of the box) to.env
, then runphp artisan key:generate
to populate theAPP_KEY
environment variable in the newly-created.env
file.
This saves developers a step when setting up the project: as soon as they run composer install
to get all of their vendor dependencies in place, a .env
file will be created (based on the [versioned] .env.example
template) and application key generated automatically.
The other item I like to add to the “scripts” section of composer.json
is:
1 2 3 4 5 6 7 |
{ "scripts": { "homestead": [ "@php vendor/bin/homestead make" ] } } |
This is just an alias for php vendor/bin/homestead make
, which just feels a little cleaner.
Leveraging the after.sh script
The after.sh
script is my secret weapon when setting up a project. Laravel Homestead will automatically look for this file and execute it after the rest of the Homestead VM has been built. My after.sh
file often looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#!/bin/bash # 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. # # If you have user-specific configurations you would like # to apply, you may also create user-customizations.sh, # which will be run after this script. # When a user connects via SSH, start in the project directory. grep -Fq "cd ~/code" ~/.bashrc || echo -e "\\n# Start in the code/ directory\\ncd ~/code" >> ~/.bashrc cd ~/code || exit 1 # Install npm dependencies and build site scripts/styles. npm install npm run dev # Run database migrations php artisan migrate # Add the default Laravel crontab. # # @link https://laravel.com/docs/master/scheduling crontab -l | grep -Fq "# Laravel crontab" || \ echo -e "# Laravel crontab - https://laravel.com/docs/master/scheduling\n* * * * * php /home/vagrant/code/artisan schedule:run >> /dev/null 2>&1" | crontab - |
There are comments peppered throughout explaining exactly what’s happening, but generally speaking I’m using the script to do common setup steps like installing & building npm dependencies, running database migrations, and anything else that a developer might otherwise have to do.
There are two special items in my after.sh
script, though:
First, Laravel Homestead will normally start users in the /home/vagrant
directory after running vagrant ssh
. Since that’s almost always immediately proceeded by cd ~/code
, this command simply scripts the process. Now, when a user SSHs into the VM, they’re automatically started in the /home/vagrant/code
directory.
The other line automatically sets up Laravel’s default cron job, which is needed for Laravel’s task scheduling features. This can be handled by Homestead automatically, but I prefer to explicitly include in in after.sh
since the Homestead.yaml
file will be different for each copy of the application.
Depending on the project, the after.sh
file can range from convenient (for example, seeding a database) to crucial (installing third-party dependencies). Thanks to the after.sh
file, we can handle all of it automatically!
User-specific settings
If there are scripts you want to run after Homestead has been provisioned that not all developers on the project might want/need, you might consider adding these to a user-customizations.sh
file, which I’ve written about before. These can be really helpful if, for example, you have strong opinions on Vim v. Nano as a default editor.
What else does your project need?
A solid on-boarding process can make all the difference whether you’re bringing on new members to a project or looking for contributions: if people can spin up your application easily, they’re far more likely to get involved. With Laravel Homestead (and maybe a little extra scripting), you can make the process as simple as possible.
Paul
I tried running this but unfortunately Vagrant blows up with the following error (after a huge Ruby stacktrace):
Stderr from the command:
bash: line 5: /sbin/ifdown: No such file or directory
bash: line 19: /sbin/ifup: No such file or directory
Not sure if those relate to host or guest.
Steve
That appears to be related to Vagrant on the host machine, not Homestead. I was able to find this similar issue on the Homestead GitHub repository.
The Homestead MOTD also includes this notice, verifying that it appears to be a Vagrant issue:
Paul
It seems that Homestead (via Vagrant) is broken on Ubuntu 18.04 and 18.10 then. If you use the version from the repositories (2.0.2) you have to SSH into the VM, install ifupdown and then re-provision. If you use the version from the Vagrant website (2.2.3), you get the warning about the NIC and a 502 gateway error when trying to access the VM from the host.
Ben Wagner
Good read!
Niels P
Thanks Steve. Pretty old post, but still one of the best help I found to streamline our team’s development workflow with Laravel and homestead.
Quick question: While playing with this setup I ran into problems with the ‘npm install’ command. Not sure why, but it kept breaking with a ‘ENOENT: no such file or directory’ error. After some googleing I found some remarks that when collaborating on a project it is better to run ‘npm ic’, to actually install the versions from the package-lock file, instead of grabbing the latests versions. What’s your thought on that? Thanks!