For the better part of the last 10 years or so, I was using MAMP (or later MAMP Pro) for my local development workflow. During this time, my requirements changed quite a bit compared to when I started. This became apparent, when we were looking for ways to streamline and automate some of our maintenance workflows. While MAMP is a comparably “easy” solution to setup sites, which works quite well, it has some limits โ especially when you want to automate and script things.
While researching, I stumbled across several different solutions, from the “do-everything-yourself and set up *everything* manually” to docker-based, to varying vagrant vagrants or Laravel Valet.
I never really took the time to finally look into all of them, and just kept everything as it was, still using MAMP. But last week, I was setting up my new MacBook Pro, and I thought there will never be a better time to give it a shot and see what I can optimize.
After reading through some of the docs and especially after reading this article by Jonathan Bossenger, I figured that while all of the solutions have their own pros and cons, Laravel Valet sounds like it fits my workflow the best.
Here’s what I ended up with, at least for now.
๐ The Requirements
- I have my sites stored in one place (
~/Development/vhosts/...
) - I don’t want to have to do all sorts of configurations for each site I install. Most of the sites I’m working with are basic WordPress installs and quite similar to each other
- I want to be able to install and migrate (or just migrate if already installed) the current state of a live site, fast!
- I want to be able to install a new site quickly without much setup, and this should be scriptable from the command line
- I want to be able to install WordPress plugins and themes with the same easy install script
- I want the ability to check out some of the plugins/themes from our git repos, instead of copying them from the live site
- I want to be able to switch between php versions easily, bonus points if this could be defined per site, but I can live with a global switch
- I want to use Mailhog to catch all mails from local installations
๐งช The Ingredients
- macOS Monterey 12.1 (on M1 Apple Silicon)
- Homebrew
- Composer
- valet+ โ a fork of Laravel Valet (needed a bit of a workaround to get it running, see below)
- wp-cli
- wp valet โ custom cli command
- Custom Migration & Installation Scripts
- SSH access to the live servers
- SSH access to the git repos
๐ The Directory Structure
Not that it really matters โ you could structure your setup completely different than mine and it should work exactly the same โ but for the sake of documentation, here’s how I structure my Development directories:
~/Development
~/Development/scripts
~/Development/vhosts
scripts โ contains all my dev scripts, like the ones to install and migrate live servers to local and stuff like that.
vhosts โ contains the actual directories for each of the sites I’m working on
๐ฆ Installing Valet+
Laravel Valet is a minimalist development environment for macOS. It was initially developed by & for Laravel, but it’s perfectly suited for WordPress Development as well. Valet+ is a fork by weprovide, which adds some useful features, like XDebug support, easily switching between PHP versions or Mailhog.
I pretty much followed the installation instructions, which I copied here and added my comments in brackets:
- Install Homebrew or update to the latest version using
brew update
. - Add the Homebrew PHP tap for Valet+ via
brew tap henkrehorst/php
. - Install PHP 7.3 using Homebrew via
brew install valet-php@7.3 --build-from-source
. - Link your PHP version using the
brew link valet-php@7.3 --force command
.
โ ๏ธ Sometimes you need to restart all terminal windows for the link to take effect after install. Especially if you just installed a homebrew PHP version for the first time. - Install Composer using Homebrew via
brew install composer
.
(This was spitting out some errors the first time I ran it, after the second time it worked ๐คทโโ๏ธ) - Install Valet+ with Composer via
composer global require weprovide/valet-plus
. - Add
export PATH="$PATH:$HOME/.composer/vendor/bin"
to~/.zshrc
- Run the
valet fix
command. This will check for common issues preventing Valet+ from installing.
(This showed quite a few errors about uninstalled PHP versions, which I ignored as I only installed v7.3 and will install others later) - Run the
valet install
command. Optionally add--with-mariadb
to use MariaDB instead of MySQL This will configure and install Valet+ and DnsMasq, and register Valet’s daemon to launch when your system starts.
(Would be cool to be able to do it without the daemon and only launch when needed, but that’s ok for now)
๐ตโ๐ซ Now, at this point it was spitting out a whole red wall of errors to my command line. Read on below on how to get those fixed. - Once Valet+ is installed, try pinging any
*.test
domain on your terminal using a command such asping -c1 foobar.test
. If Valet+ is installed correctly you should see this domain responding on127.0.0.1
. If not you might have to restart your system. Especially when coming from the Dinghy (docker) solution.
The big red wall of an error in step 9 nearly made me give up, but after reading them closer, they turned out solvable. At least as a workaround. Here’s how I got valet
install to run properly.
๐ฉโ๐ง Fixing valet install
1. APCU installation location not found
After reading through the errors closely and investigating some GitHub issues that sounded like a similar problem, I found out that PHP seems to search for a library called PCRE/PCRE2 (Perl Compatible Regular Expressions) in the wrong location. To make it work I had to add Symlinks like this. I added those for every PHP version I installed, even though I’m actually not sure if all of them use this library.
ln -s /opt/homebrew/include/pcre2.h /opt/homebrew/Cellar/valet-php@7.3/7.3.27_2/include/php/ext/pcre/pcre2.h
Code language: PHP (php)
2. GeoIP not found
Now that the APCU thing was running through, the next wall of errors was caused by a GeoIP module. According to some comments on Github this is not really used anymore, and the easiest workaround seemed to be deactivating it for all PHP versions I use. I know, I know, not the prettiest thing to change those files directly, but it works for now and I hope this gets fixed in a future version.
To deactivate GeoIP, we need to set some parameters to false for each PHP version by changing this file:
nano ~/.composer/vendor/weprovide/valet-plus/cli/Valet/Pecl.php
Code language: JavaScript (javascript)
In there, we need to set GeoIP to false, like this:
self::GEOIP_EXTENSION => [
'8.0' => false,
'7.4' => false,
'7.3' => false,
'7.2' => false,
'7.1' => false,
'7.0' => false,
'extension_type' => self::NORMAL_EXTENSION_TYPE,
'brew_dependency' => 'geoip'
],
Code language: PHP (php)
After those two changes, I ran the valet fix
and valet install
commands again, and they ran through without any errors:
valet fix && valet install
๐ฃ Register my vhosts
directory with valet
Sites can now be added either manually, or โ and this is what I’m going to do โ I can register my whole vhosts
directory by using the park
command. To register it, I need to navigate to the vhosts directory and run:
valet park
Now all directories inside of vhosts
will automatically be accessible from a web browser at <directory name>.test (or another domain ending you chose, see next step).
๐ฉ Changing the domain ending
I like to have my local sites under <name>.localhost
, instead of the default <name>.test
that valet uses. Luckily it’s easy to change this by running the domain
command:
valet domain localhost
๐ฉโ๐ป Installing the wp valet
cli command
To quickly spin up a new site from the command line there’s even a command for WP-CLI. Install the wp valet
command like this:
wp package install aaemnnosttv/wp-cli-valet-command:@stable
๐ช Installing a new site
Ok, now that everything is setup and running, all I have to do to create a new site is navigating to my vhosts
directory and run the following command (replacing <name>
with a name for the site):
wp valet new <name>
Code language: HTML, XML (xml)
The command also lets me set a lot of parameters for the WP Install. For example, I could install WP version 5.8.3 like this. You can find all the options for the command here.
wp valet new <name> --version=5.8.3
Code language: HTML, XML (xml)
This will do the following things:
- Create a new directory with the name provided
- Creating a new database named
wp_<name>
- Download and install WordPress
- Create an SSL certificate
๐ฃ Removing a site
The following command removes a site completely, including all its files & the database:
wp valet destroy <name>
Code language: HTML, XML (xml)
๐ Changing PHP versions
If you want to let valet run with a different PHP version, you can do so with the use
command, this will download and install (if not already installed) the specified version and restart valet to use this version:
valet use php@8.0
Code language: CSS (css)
โ๏ธ Catching mails with Mailhog
I was using Mailhog before to intercept all mails going out from local installations. This is important to prevent local sites from sending emails, especially when working with client sites.
Luckily, Mailhog comes already preinstalled and configured with valet-plus, and the mails can be accessed by opening:
http://mailhog.localhost
Code language: JavaScript (javascript)
๐ Wrapping up
As you can see, valet does a lot of cool things out of the box, and makes setting up local sites as easy as it can be for me. Apart from what I’ve shown here, valet should also make your sites accessible from your local network or even lets you share them publicly, which is awesome for testing on other devices. I still have a list of things to try and am still working on some scripting to completely automate the process of setting up and migrate live sites, but so far I really like how it works.
I guess the only thing that I’m missing, is that it exclusively runs Nginx, so I might still need another solution for when I really need something tested on Apache. But other than that it fits my workflow quite perfectly!
I wrote this guide mainly to document the process for my future self, when I have to do this again, but maybe it’s helpful for others as well.