nginx / MariaDB / PHP / Aegir on Mac OS X with optional Drush 5 (works on Mountain Lion!)

Our original post on this topic has become a little outdated, and with Mountain Lion just around the corner here is an updated version that works on Mountain Lion and lets you elect to use Drush 5 if you want!

Step 1: Xcode and Homebrew

XCode is required for Homebrew to compile dnsmasq, nginx, mariadb and php.

  • Download and install Xcode using the Mac App Store with the link above
    (it's free, but will take a while to download if your Internet connection is slow)
  • Once the download has finished launch Xcode and open it's preferences, goto Downloads tab and click Install on Command Line Tools.
  • Follow the installation instructions for Homebrew, which most likley tell you to run this command in your terminal
    $ ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
Mountain Lion Fixes for Homebrew
  • Let everyone know where Xcode is by entering this in your terminal
    $ sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
  • Install X11: Visit http://xquartz.macosforge.org/trac/wiki and download and install version 2.7.2+.
  • You will need to fix the symlink it makes
    $ sudo ln -s /opt/X11 /usr/X11
  • Reinstall your brews
    $ brew list
Add directories to your path
  • Ensure /usr/local/bin occurs before /usr/bin and add /usr/local/sbin is added to your path, If you are using zsh, update .zshrc instead
    $ nano ~/.bash_profile   
    or
    $ nano ~/.zshrc
  • Paste the following into editor then Ctrl+X and Y to save
    export PATH=~/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin:/usr/local/git/bin:/opt/local/bin
Set up email sending
  • First test if you are able to email yourself, substituting with your own email address
    $ date | mail -s test you@youremail.com
  • If you are on Mountain Lion you need to create and set the permissions for these directories first (for DP4 at least)
    $ sudo mkdir /Library/Server /Library/Server/Mail /Library/Server/Mail/Data /Library/Server/Mail/Data/spool /Library/Server/Mail/Data/spool/maildrop
    $ sudo chown _postfix:_postdrop /Library/Server/Mail/Data/spool/maildrop
    $ sudo chmod 777 /Library/Server/Mail/Data/spool/maildrop
  • I chose to set up gmail as my email relay (this may not be required for you though)
    http://realityloop.com/blog/2011/06/05/os-x-ditching-mamp-pro-part-2-gmail-email-relay
  • Mountain Lion appears to have a few bugs around this at the moment, I have to launch postfix on reboot
    $ sudo postfix start
Add --rsyncable support to gzip

Run the following commands from terminal

  • Ensure that you don't already have gzip installed via homebrew:
    $ brew uninstall gzip
  • Add hombrew dupes:
    $ brew tap homebrew/dupes
  • Reinstall gzip via homebrew
    $ brew install homebrew/dupes/gzip
Step 2: Setting up dnsmasq for local wildcard DNS
  • Install dnsmasq via homebrew
    $ brew install dnsmasq
  • Copy and edit the default conf example
    $ mkdir /usr/local/etc
    $ cp $(brew --prefix dnsmasq)/dnsmasq.conf.example /usr/local/etc/dnsmasq.conf
    $ nano /usr/local/etc/dnsmasq.conf
  • Update the following values as shown
    resolv-file=/etc/resolv.dnsmasq.conf
    address=/.ld/127.0.0.1
    listen-address=127.0.0.1
  • Set up downstream DNS resolver
    $ sudo nano /etc/resolv.dnsmasq.conf
  • Paste the following and save using CTRL + X then Y
    # OpenDNS IPv6:
    nameserver 2620:0:ccd::2
    nameserver 2620:0:ccc::2
    # Google IPv6:
    nameserver 2001:4860:4860::8888
    nameserver 2001:4860:4860::8844
    # OpenDNS:
    nameserver 208.67.222.222
    nameserver 208.67.220.220
    # Google:
    nameserver 8.8.8.8
    nameserver 8.8.4.4
  • Copy the launch daemon so dnsmasq runs on startup, and launch it now
    $ sudo cp $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    $ sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
  • Set hostname as it's required for sane default in aegir setup, we chose rl.ld for Realityloop Local Development you can use something else instead of rl but it needs to end in .ld
    $ sudo scutil --set HostName rl.ld
  • Open your System Preferences, then click on Network, then for each of your interfaces (Wi-Fi & Ethernet), click on Advanced, click on DNS and finally click on the + symbol at the bottom of the left hand-side panel and set 127.0.0.1 as your only DNS Server
Step 3: Install nginx

nginx (pronounced “engine-x”) is a Web server and a reverse proxy server for HTTP, SMTP, POP3 and IMAP protocols, with a strong focus on high concurrency, performance and low memory usage.

  • Unless this is a fresh install of OS X you need to ensure Apache doesn't load on startup
    $ sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist
  • We can add some 3rd party extensions here as well (this list item is optional, if you don't want these extensions just skip to the next list item)
    $ curl -s -L -o /tmp/nginx-upload-progress.tar.gz https://github.com/masterzen/nginx-upload-progress-module/tarball/v0.9.0 && mkdir /tmp/nginx-upload-progress && tar zxpf /tmp/nginx-upload-progress.tar.gz --strip-components 1 -C /tmp/nginx-upload-progress && rm /tmp/nginx-upload-progress.tar.gz
    $ curl -s -L -o /tmp/nginx-fair.tar.gz http://github.com/gnosek/nginx-upstream-fair/tarball/master && mkdir /tmp/nginx-fair && tar zxpf /tmp/nginx-fair.tar.gz --strip-components 1 -C /tmp/nginx-fair && rm /tmp/nginx-fair.tar.gz 
    Now one giant line of sed regex that will edit the Homebrew formula for nginx to add the additional compile options that we need. Make sure it all gets entered as one line.
    $ sed -i '-default' 's/\([[:space:]]*\['\''--\)\(with-webdav\)\('\'',[[:space:]]*"\)\(Compile with support for WebDAV module\)\("\]\)/\1\2\3\4\5,%\1with-realip\3Compile with support for RealIP module\5,%\1with-gzip_static\3Compile with support for Gzip Static module\5,%\1with-uploadprogress\3Compile with support for Upload Progress module\5,%\1with-fair\3Compile with support for Fair module\5,%\1with-mp4\3Compile with support for MP4 module\5,%\1with-flv\3Compile with support for FLV module\5,%\1with-stub_status\3Compile with support for Stub Status module\5/; s/\([[:space:]]* args << "--\)\(with-http_dav_module\)\(" if ARGV.include? '\''--with-\)\(webdav\)\('\''.*\)/\1\2\3\4\5%\1with-http_realip_module\3realip\5%\1with-http_gzip_static_module\3gzip_static\5%\1add-module=\/tmp\/nginx-upload-progress\3uploadprogress\5%\1add-module=\/tmp\/nginx-fair\3fair\5%\1with-http_mp4_module\3mp4\5%\1with-http_flv_module\3flv\5%\1with-http_stub_status_module\3stub_status\5/; y/%/\n/' $(brew --prefix)/Library/Formula/nginx.rb
    Now we'll install Nginx with our new build options and extensions and start it.
    $ brew install nginx --with-realip --with-gzip_static --with-mp4 --with-flv --with-stub_status --with-uploadprogress --with-fair
    $ [ $? -eq 0 ] && rm -rf /tmp/nginx-upload-progress /tmp/nginx-fair
    $ mkdir -vp $(brew --prefix nginx)/var/{microcache,log,run}
  • If you didn't use the above to install extra nginx extensions run this, otherwise skip this command
    $ brew install nginx
  • Once nginx is compiled, backup the default nginx config
    $ mv /usr/local/etc/nginx/nginx.conf /usr/local/etc/nginx/nginx.conf.bak
  • Download our config as follows
    $ curl http://realityloop.com/sites/realityloop.com/files/uploads/nginx.conf_.txt > /usr/local/etc/nginx/nginx.conf
  • Set your username in the downloaded config file
    $ sed -i -e 's/\[username\]/'`whoami`'/' /usr/local/etc/nginx/nginx.conf
  • Make nginx log files visible in Console app
    $ sudo mkdir /var/log/nginx
  • Create the following directorty to stop “"/var/lib/nginx/speed" failed (2: No such file or directory)” error
    $ sudo mkdir /var/lib/nginx
Step 4: MariaDB

MariaDB is a community-developed branch of the MySQL database, the impetus being the community maintenance of its free status under GPL, as opposed to any uncertainty of MySQL license status under its current ownership by Oracle.

The intent also being to maintain high fidelity with MySQL, ensuring a "drop-in" replacement capability with library binary equivalency and exacting matching with MySQL APIs and commands. It includes the XtraDB storage engine as a replacement for InnoDB.

  • Still in the ‘Terminal app’, type the following command
    $ brew install mariadb --use-llvm --env=std
  • Once compilation has finished unset TMPDIR
    unset TMPDIR
  • Then mysql_install_db
    $ mysql_install_db --user=`whoami` --basedir="$(brew --prefix mariadb)" --datadir=/usr/local/var/mysql --tmpdir=/tmp
  • but don't follow any more of the prompts just now or you will run into problems, we'll do the rest later.
Step 5: Install the homebrew version of php
  • Execute the brew install process using hombrew-alt php brew file
    $ brew tap josegonzalez/homebrew-php
    $ brew tap homebrew/dupes
    $ brew install php53 --with-mysql --with-fpm --with-imap
    $ brew install php53-xhprof
    $ brew install php53-xdebug
    $ brew install php53-uploadprogress
  • Edit the php.ini
    $ nano /usr/local/etc/php/5.3/php.ini
    Search for the extenstions section by using CTRL + W and typing: extension=php_zip.dll (check the installed versions match the text below, and update accordingly)
    extension="/usr/local/Cellar/php53-xhprof/0.9.2/xhprof.so"
    extension="/usr/local/Cellar/php53-uploadprogress/1.0.3.1/uploadprogress.so"
    zend_extension="/usr/local/Cellar/php53-xdebug/2.2.1/xdebug.so"
  • Set your timezone http://www.php.net/manual/en/timezones.php, I added the follwing under the ;date.timezone = line
    date.timezone = Australia/Melbourne
  • Find and disable magic quotes, adding if not in php.ini already
    magic_quotes_gpc = Off
    magic_quotes_runtime = Off
    magic_quotes_sybase = Off
  • And updated the Memory limit as follows, then saved with Ctrl+X then Y
    memory_limit = 256M
  • Edit the php-fpm.conf file
    $ nano /usr/local/etc/php/5.3/php-fpm.conf
  • Add the following line below ;pid = run/php-fpm.pid
    pid = /usr/local/var/run/php-fpm.pid
  • Remove the ; from the start of the following lines then save using Ctrl+X then Y
    pm.start_servers = 3
    pm.min_spare_servers = 3
    pm.max_spare_servers = 5
    pm.max_requests = 500
  • Make our log file visible in Console app
    $ sudo ln -s  $(brew --prefix josegonzalez/php/php53)/var/log/php-fpm.log /var/log/nginx/php-fpm.log
Step 6: Service Launch Daemons

This is so everything runs automatically on startup

  • Nginx needs to run as root for port 80 so do the following
    $ sudo cp $(brew --prefix nginx)/homebrew.mxcl.nginx.plist /Library/LaunchDaemons/
    $ sudo chown root:wheel /Library/LaunchDaemons/homebrew.mxcl.nginx.plist
  • Edit the plist
    $ sudo nano /Library/LaunchDaemons/homebrew.mxcl.nginx.plist

    Remove the following 2 groups of text and save your changes using Ctrl+X then Y
    <key>KeepAlive</key>
    <true/>
     
    <key>UserName</key>
     <string>yourusername</string>

    Start nginx
    $ launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.nginx.plist
  • Make a directory for the rest of our launch daemons
    $ mkdir -p ~/Library/LaunchAgents
  • Copy the LaunchDaemon to load mariadb on boot into place and start it now
    $ cp $(brew --prefix mariadb)/homebrew.mxcl.mariadb.plist ~/Library/LaunchAgents/
    $ launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist
  • Copy php launch daemon and start it
    $ cp $(brew --prefix josegonzalez/php/php53)/homebrew-php.josegonzalez.php53.plist ~/Library/LaunchAgents/
    $ launchctl load -w ~/Library/LaunchAgents/homebrew-php.josegonzalez.php53.plist
  • Complete the mariaDB setup
    $ sudo $(brew --prefix mariadb)/bin/mysql_secure_installation
  • Answer the prompts as follows, replace [password] with a password of your own chosing
    Enter current password for root (enter for none): [Enter]
    Set root password? [Y/n] y
    New password: [password]
    Re-enter new password: [password]
    Remove anonymous users? [Y/n] y
    Disallow root login remotely? [Y/n] y
    Remove test database and access to it? [Y/n] y
    Reload privilege tables now? [Y/n] y
Step 7: Drush and Aegir

You're in the home stretch now!

  • Install wget so Drush will work properly (need to do this on Mountain Lion)
    $ brew install wget
  • Make a few small changes required for this to work properly
    $ sudo mkdir /var/aegir
    $ sudo chown `whoami` /var/aegir
    $ sudo chgrp staff /var/aegir
    $ sudo dscl . append /Groups/_www GroupMembership `whoami`
  • Allow your user to restart nginx, be sure to replace [username] with your own username.
    $ sudo -i
    $ echo "[username] ALL=NOPASSWD: /usr/local/bin/nginx" >> /etc/sudoers
    $ exit
  • Create a symbolic link to aegir configuration
    $ sudo ln -s /var/aegir/config/nginx.conf /usr/local/etc/nginx/aegir.conf
  • To install aegir remember to replace client_email in the last command with your own email address
    $ brew install drush
    $ drush dl --destination=/users/`whoami`/.drush provision-6.x-2.x
    $ drush hostmaster-install --aegir_root='/var/aegir' --root='/var/aegir/hostmaster-6.x-2.x-dev' --http_service_type=nginx --aegir_host=aegir.ld  --client_email=email@domain.com
    Remember to copy the one time login link and set your aegir admin password.
  • Remove the default platforms dir and create a symlink for so you can put your Platforms in ~/Sites/ directory
    $ mkdir /Users/`whoami`/Sites
    $ rmdir /var/aegir/platforms
    $ ln -s /Users/`whoami`/Sites /var/aegir/platforms
  • Disable the forced caching in aegir (this is for local dev after all!)
    $ nano /var/aegir/config/includes/global.inc
    Paste the following text at the bottom of the document and save with CTRL +X then Y and return
    header('X-Accel-Expires: 0'); //disable nginx caching
    unset($conf['cache']);          // disable hardcoded caching
    unset($conf['preprocess_css']); // disable hardcoded css aggregation
    unset($conf['preprocess_js']);  // disable hardcoded js aggregation
  • Open your web browser and start creating platforms and sites!
    http://aegir.ld
note: some components of nginx step borrowed from EchoDitto Labs

Comments

Thanks Brian! Excellent work. I'm up and running now and it's working great. Currently running the same process over a few other machines in the office, so i'll let you know how we go on some newer machines!

I'm new to nginx, tried MNPP before, but MNPP is not compatible with 10.8. Tried to go through your howto. It seems to be everything ok, but I can not install phpmyadmin or set up an working nginx host. I would like to use it to local development (wordpress). Can you give me some tipps pls how to set up a host?

This blog post is specifically focused around using it for Drupal development, you can use different includes for your own manually created vhosts, take a look here for some info:
http://drupal.org/node/1044678

I've personally created a directory at:
/usr/local/etc/nginx/vhost.d

and my nginx.conf includes vhost files from that directory for any non drupal sites, but you will need to figure out the nginx rules that you require for them yourself.

All done! The only hiccup for me that wasn't mentioned in your post was that I needed to restart the machine before the local .ld domain was recognised.

I found it also to work great. OS X 10.7.4

Brian,

Awesome walkthrough, thanks for the write-up! I went through it and got pretty much everything working. I can log into Aegir and create platforms and sites. But whenever I go to visit the site after Aegir has created it, I get a 404 from nginx. Any idea what might be wrong? The Aegir backend works just fine.

Oddly enough the problem resolved itself. Not quite sure what I did.

I was having all kinds of problems installing both MariaDB (specifically with Readline) and PHP threw Homebrew.

The solution was to add "--env=std" to their respective install commands.

Hi Brian,

Thanks a lot for this article. I'm trying to get through it but I'm stuck in the mariadb steps. I get the following when running mysql_install_db

$ mysql_install_db

FATAL ERROR: Could not find ./bin/my_print_default

For anyone else with that problem, I had to run the mysql_install_db from following directory:

/user/local/Cellar/mariadb/5.5.27

I also had this problem and appreciated these comments as they helped me out. Thank you!
I had a little more complication so I thought I would share.

mysql_install_db wasn't found for me either. I found it in a slightly different directory then what Tim posted.
/usr/local/Cellar/mariadb/5.5.28/bin/mysql_install_db

I also had to sudo this. So my final command was:
sudo /usr/local/Cellar/mariadb/5.5.28/bin/mysql_install_db

*If you are still having trouble locating this file, try a search "find / -name "mysql_install_db"

You can also run "which mysql_install_db" to find it.

Drush 5.x uses Gzip with --rsyncable option which does not work with native Mountain Lion. In practise Hostmaster using Drush 5.x fails to backup sites, so also migrate fails as it is based on create-backup-generate-new-site-from-backup -workflow.
Solution is to install Macports from http://www.macports.org/install.php and upgrade gzip to newer version
$ gzip --version
gzip 1.3.12
Copyright (C) 2007 Free Software Foundation, Inc.
Copyright (C) 1993 Jean-loup Gailly.
This is free software. You may redistribute copies of it under the terms of
the GNU General Public License .
There is NO WARRANTY, to the extent permitted by law.
Written by Jean-loup Gailly.
$ sudo port
MacPorts 2.1.2
Entering interactive mode... ("help" for help, "quit" to quit)
> info gzip
gzip @1.5 (archivers)
Variants: universal
>install gzip
[for some 15 mins port will compiles gzip and required components]
>exit
Goodbye

Now exit terminal or reload bash configuration for change to take effect. Restarting nginx is probably also be necessary.
You should now have gzip version 1.5 as given in info gzip.

Rpsu, this can be done via homebrew too, hopefully it will be included in dupes proper soon:
brew tap homebrew/dupes
cd /usr/local/Library/Taps/homebrew-dupes
wget https://raw.github.com/trevor/homebrew-dupes/1305e5d3685c55fc7b9fd95cb32de0eabce9b047/gzip.rb
brew install homebrew/dupes/gzip

Thanks Brian, would have saved me some time if I had seen this a couple of weeks ago ;)

I also noticed that with (solution I described) editing $PATH in ~/.bash_profile was after all not enough, but after some pondering I realized cron had a different $PATH settings. I just switched /opt/local/bin as first item. Before that I just go bunch of tar write errors as a result of gzip failing with --rsyncable -parameter.

Still not working for me with gzip v. 1.5 that I picked up from homebrew.

Am about to try http://drupal.org/node/1901508

I uninstalled Macports and thus their gzip. Tested with "regular" gzip through homebrew/dupes and it did not work. Version is the same, but flag --rsyncable does not exist. Patched one as per http://drupal.org/node/1901508 works fine.

Running OS X 10.8.x

My patch to the homebrew version of gzip is now included, so you don't need to apply it anymore