In my previous post OS X 10.10 Yosemite, Apache, MySQL, PHP 5.6, (MAMP) Homebrew Dev Setup I covered a lot. Like a lot a lot, but by the end of the post all we had really accomplished was installing the basic building blocks of a development server, namely: Apache, MySQL, and PHP. In this post we’ll take those components and turn them into a sweet development workflow.
Everyone loves running
brew update && brew upgrade just to see what’s been updated. It’s a seriously busy repo, but it’s easy to forget to do and then your versions languish. Fortunately for us Mark Kalmes wrote a LaunchAgent plist for us called, aptly, brewupdate. We’re going to grab the latest version and install it.
curl https://raw.githubusercontent.com/mkalmes/brewupdate/develop/net.mkalmes.brewupdate.plist > ~/Library/LaunchAgents/net.mkalmes.brewupdate.plist launchctl load ~/Library/LaunchAgents/net.mkalmes.brewupdate.plist
The first line downloads the latest version straight from github to the user-space LaunchAgents folder. The second line loads it. You can edit the file and change when it updates, the default is 11 AM (local time zone). I changed mine to 8 AM because I like to start the day with a fresh brew (ha ha.)
Just don’t forget to run
brew upgrade yourself occasionally. You could make another LaunchAgent by changing the parameter from
upgrade, but I really don’t like unattended upgrades as much as updates.
Apache 2: DocumentRoot
That’s a clever title, you see we’re running Apache HTTPD 2, and at the same time this is part 2 of the Apache section… Anyways, at the end of Part 1 Apache was running on port 80 like a champ. The DocumentRoot is set to something ridiculous like
/usr/local/var/www/htdocs which is pretty freaking inconvenient.
First we need to create our /Sites folder and we’ll still keep our web documents inside of that folder.
mkdir ~/Sites ~/Sites/htdocs
Then edit your
/usr/local/etc/apache2/2.4/httpd.conf file and change the DocumentRoot directive, as well as accompanying Directory declaration to match your new htdocs location:
DocumentRoot "/Users/USERNAME/Sites/htdocs" <directory> </directory>
USERNAME with your username’s folder name. Then inside of that directory declaration we’re going to change some of the default Apache values to be more permissive:
Options All MultiViews AllowOverride All
This is given with the usual caveat that OF COURSE you would never default everything like this on a production server- but on a development server there’s generally not much security implication (unless you’re playing with live data!). My full DocumentRoot section looks like this:
DocumentRoot "/Users/dannywahl/Sites/htdocs" <directory> # # Possible values for the Options directive are "None", "All", # or any combination of: # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews # # Note that "MultiViews" must be named *explicitly* --- "Options All" # doesn't give it to you. # # The Options directive is both complicated and important. Please see # http://httpd.apache.org/docs/2.4/mod/core.html#options # for more information. Options All MultiViews # # AllowOverride controls what directives may be placed in .htaccess files. # It can be "All", "None", or any combination of the keywords: # AllowOverride FileInfo AuthConfig Limit AllowOverride All # # Controls who can get stuff from this server. # Require all granted </directory>
Now, restart your httpd daemon:
sudo httpd -k restart
If you didn’t use
--with-privileged-ports when you installed Apache then you don’t need root. Now when you visit http://localhost you should just get a blank directory listing instead of the default “It Works!” page. Let’s put a test page there. First let’s create a sub-directory in our /Sites folder called
phpinfo.localhost (more on the
Now let’s create a page that calls the
phpinfo(); function and place it in that folder. if you don’t know about phpinfo() it’s a simple function that outputs a page that shows you information about your PHP configuration.
printf ‘<?php phpinfo(); ??>‘ >> ~/Sites/htdocs/phpinfo.localhost/index.php
Now you should be able to visit
http://localhost/phpinfo.localhost/ and see the overview of your PHP configuration. That’s it for now for Apache, let’s move on to MySQL.
In the first post we simply installed MySQL and set a root password. It’s good to have a password because some authentication APIs require one, but we’re not necessarily using it for security (otherwise we wouldn’t be using root), so we’re going to save our authentication in a my.cnf options file. Homebrew provides us with a sample file, but it’s not symlinked by default so we’re going to copy it to an available configuration folder and since MySQL is running in user-space, we will override the configuration at the user-space level:
cp -v $(brew –prefix mysql)/support-files/my-default.cnf ~/.my.cnf
Now we have our config override, let’s have it auto-save our credentials.
cat > ~/.my.cnf [client] user=root password=PASS [mysql] user=root password=PASS [mysqldump] user=root password=PASS [mysqldiff] user=root password=PASS EOF
Replace “PASS” with your root MySQL password. If you have special characters in your password, which you shouldn’t, add double quotes around the password. Now you can connect to mysql without having to manually authenticate.
mysql -u root
Finally we’re going to install everyone’s favorite database manager, phpmyadmin. Again, we’ll be using homebrew to install it.
brew install phpmyadmin
After a successful installation you will get a caveat about adding a directory alias to your httpd configuration to enable phpmyadmin, but we’re not going to do that we’re going to symlink it to to our ~/Sites directory.
ln -s /usr/local/share/phpmyadmin ~/Sites/htdocs/phpmyadmin.localhost
This will create a symbolic link in your webroot called phpmyadmin.localhost that takes you to /usr/local/share/phpmyadmin (which itself is a symlink to the Cellar). The benefit here is you don’t need to restart Apache, and again we’ll go over it later but the .localhost is important.
Now that phpmyadmin is installed, we need to configure it. First we’ll import the phpmyadmin linked tabled configuration storage via commandline:
mysql -u root -e 'CREATE DATABASE IF NOT EXISTS `phpmyadmin`;' mysql -u root phpmyadmin
Once the tables are imported open your phpmyadmin config file located at
/usr/local/etc/phpmyadmin.config.inc.php and you're going to do a few things:
- generate a blowfish secret
- Set your
controlpass(I use root again)
- Uncomment the storage database and tables section
In the end your blowfish and servers configuration sections should look something like this:
$cfg['blowfish_secret'] = 'y=PUY@-GleE+~%OiGQVz#)N4I)@Q%Ug_Jr[DxF'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */ /* * Servers configuration */ $i = 0; /* * First server */ $i++; /* Authentication type */ $cfg['Servers'][$i]['auth_type'] = 'cookie'; /* Server parameters */ $cfg['Servers'][$i]['host'] = '127.0.0.1'; $cfg['Servers'][$i]['connect_type'] = 'tcp'; $cfg['Servers'][$i]['compress'] = false; $cfg['Servers'][$i]['AllowNoPassword'] = false; /* * phpMyAdmin configuration storage settings. */ /* User used to manipulate with storage */ // $cfg['Servers'][$i]['controlhost'] = ''; // $cfg['Servers'][$i]['controlport'] = ''; $cfg['Servers'][$i]['controluser'] = 'root'; $cfg['Servers'][$i]['controlpass'] = 'root'; /* Storage database and tables */ $cfg['Servers'][$i]['pmadb'] = 'phpmyadmin'; $cfg['Servers'][$i]['bookmarktable'] = 'pma__bookmark'; $cfg['Servers'][$i]['relation'] = 'pma__relation'; $cfg['Servers'][$i]['table_info'] = 'pma__table_info'; $cfg['Servers'][$i]['table_coords'] = 'pma__table_coords'; $cfg['Servers'][$i]['pdf_pages'] = 'pma__pdf_pages'; $cfg['Servers'][$i]['column_info'] = 'pma__column_info'; $cfg['Servers'][$i]['history'] = 'pma__history'; $cfg['Servers'][$i]['table_uiprefs'] = 'pma__table_uiprefs'; $cfg['Servers'][$i]['tracking'] = 'pma__tracking'; $cfg['Servers'][$i]['designer_coords'] = 'pma__designer_coords'; $cfg['Servers'][$i]['userconfig'] = 'pma__userconfig'; $cfg['Servers'][$i]['recent'] = 'pma__recent'; $cfg['Servers'][$i]['favorite'] = 'pma__favorite'; $cfg['Servers'][$i]['users'] = 'pma__users'; $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups'; $cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding'; $cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches'; /* Contrib / Swekey authentication */ // $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf'; /* * End of servers configuration */
You might notice that I changed my host to "127.0.0.1" from "localhost". By default /etc/hosts should resolve localhost to 127.0.0.1 so it shouldn't trigger a DNS lookup, but by changing it to an IP address you'll never have that happen because there are ways to inadvertently send lookups to your machine DNS for localhost. Again, this is not necessary. After saving your phpmyadmin configuration you should be able to visit the page at
Dnsmasq is a lightweight DNS/DHCP server which can be installed and serve DNS for addresses that aren't in the global namespace. We'll be installing dnsmasq via homebrew and configuring it, along with Apache to serve sites on reserved TLDs.
brew install dnsmasq
Once dnsmasq is installed we need to copy a configuration profile, edit it, and then load the daemon. First we'll copy a provided configuration:
cp /usr/local/opt/dnsmasq/dnsmasq.conf.example /usr/local/etc/dnsmasq.conf printf '\naddress=/localhost/127.0.0.1' >> /usr/local/etc/dnsmasq.conf sudo cp -fv /usr/local/opt/dnsmasq/*.plist /Library/LaunchDaemons sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
Effectively we are telling dnsmasq to resolve all .localhost domains to 127.0.0.1 and setting it to run the daemon at the machine level (with Apache). So you can now try to ping any .localhost domain and it will resolve to 127.0.0.1:
ping -c 1 thisdoesntexist.localhost PING thisdoesntexist.localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.029 ms --- thisdoesntexist.localhost ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 0.029/0.029/0.029/0.000 ms
Obviously trying to visit http://thisdoesntexit.localhost won't work because Apache isn't configured to handle requests to that address, yet. There are two different ways to configure this, the first is to create a vhost entry for every domain you want to use, the second is to enable mod_vhost_alias- which is what we'll do now.
Mod_vhost_alias is an Apache module that let's you do pattern matching from a request URL to a directory structure, there are lots of cool examples at the documentation site- but the one we want is pretty straight forward. Open your httpd.conf file (in /usr/local/etc/apache2/2.4/) and uncomment the mod_vhost_alias module
LoadModule vhost_alias_module libexec/mod_vhost_alias.so
then in terminal we'll configure vhost_alias_module:
printf '\nUseCanonicalName Off\nVirtualDocumentRoot /Users/USERNAME/Sites/htdocs/%%0' >> /usr/local/etc/apache2/2.4/httpd.conf
And finally restart the httpd daemon:
sudo httpd -k restart
The end result is that now every folder that ends in .localhost in your ~/Sites/htdocs/ folder is accessible as a FQDN. Go back to your browser and now you can visit http://phpinfo.localhost and http://phpmyadmin.localhost If you create another folder inside of htdocs that ends in .localhost it will be available in your dev environment as a standalone domain. Here's a couple of examples of how that could be useful:
Say you want a standalone domain of wordpress:
git clone firstname.lastname@example.org:WordPress/WordPress.git ~/Sites/htdocs/wordpress.localhost
What about multiple branches of drupal on standalone domains? Let's make
git clone email@example.com:drupal/drupal.git --branch 6.x --single-branch ~/Sites/htdocs/drupal6.localhost git clone firstname.lastname@example.org:drupal/drupal.git --branch 7.x --single-branch ~/Sites/htdocs/drupal7.localhost
As another example, how about a single domain with multiple moodle instances in sub-directories like
mkdir ~/Sites/htdocs/moodle git clone email@example.com:moodle/moodle.git --branch MOODLE_26_STABLE --single-branch ~/Sites/htdocs/moodle/26 git clone firstname.lastname@example.org:moodle/moodle.git --branch MOODLE_27_STABLE --single-branch ~/Sites/htdocs/moodle/27
So there you go! Mac OS X with brewed versions of important stuff like openssl and git, Apache 2.4, MySQL, and PHP 5.6 and a killer setup using mod_vhost_alias and dnsmasq to provision .localhost domains on the fly. Of course there are a million ways to dev from here, but this is a pretty solid foundation.
If you need help with educational technology in your institution, from strategic planning, to implementation, to technical support contact us and we can help get you on the path to success.