12 min read
Native LAMP Stack in Mac OS X!
A Guide to Native LAMP Stack in Mac OS X
Are you using MAMP Pro for web development? Has it ever caused you to drink heavily while simultaneously banging your head against the keyboard? Follow this guide, and you will never have to experience the horror that is the MAMP stack again!
Don’t get us wrong – MAMP was great for a while. Convenient even. It had this nice little button labeled “Start”, and then it would turn green when it was ready. It handled all those pesky VHosts and hosts file entries for me. Then one day, out of nowhere, it reared its ugly head when I had the audacity to try to use the ImageMagick extension for PHP.
This is the fly in the ointment when it comes to MAMP. It lives off in its own little world on your computer, oblivious to the endless stream of change in the real world of web development. It is an added layer to the already complex set of layers standing between your keyboard and a web application. It eats memory like a pig, has a hard time following directions, and does not play nice with others.
So, what to do? First, a disclaimer: the following guide is what you would call an ‘overhaul’ of your development environment. It can be fraught with peril – and subsequent triumph – for the determined soul. In other words, it may take you longer than you like to get back up and running. Plan accordingly.
Backup and cleanup
Dump and backup databases from MAMP’s environment:
Get all databases (grab a beer and a sandwich, this could take a while):
~$ mysqldump -u root -p --all-databases > database_backups.sql
Note that this will dump “ALL” databases into a single file. If you want to be up and running quickly with specific sites, try the next command instead. Also, this does not preserve users and permissions. So if you use a separate user/password for each of your databases, make sure you know what those are / how to set them up later.
Or, if you prefer, just grab the ones you need one at a time:
~$ mysqldump -u root -p --databases database_name > database_name_backup.sql
- Backup /Applications/MAMP/htdocs (your websites!)
- Remove MAMP Pro (drag to trash)
- Delete MAMP from your system path
~$ echo $PATH
If you see any MAMP related thing in your path, find it and remove it. Usually, this will be a line in your bash profile.
- Edit /etc/paths (with your favorite editor!):
~$ sudo emacs /etc/paths
Add /usr/local/bin to the first line. This will make sure that packages installed with Homebrew are the first thing in your path.
Now close this terminal window and open a new one. This will update your environment with the new path.
- Install Homebrew (if you haven’t already):
~$ ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
- Normalize Homebrew (if already installed):
~$ brew doctor
- Fix any errors. (DO NOT SKIP THIS PART!) Usually these errors have to do with your path, or extraneous files and libraries in /usr/local, or brew not finding Mac OS System libraries where it expects them to be. There is a wide range of errors that occur, and this step can take some time, and some serious googling. Also, make sure you run:
~$ brew update
- Install lunchy (for starting and stopping daemons):
~$ sudo gem install lunchy
A Side Note about daemons
Mac OS X relies on launchctl to start and stop services. It is similar to init.d in linux, or service in Ubuntu. lunchy is a wrapper for launchctl. It makes it easier to start and stop services. Services that are owned by root have init scripts in /Library/LaunchDaemons. To start and stop these services using lunchy, you must use sudo. Services owned by the user have init scripts in ~/Library/LaunchAgents/. Lunchy does not require sudo to start and stop these services.
Install Packages using Homebrew
PHP
~$ brew tap homebrew/dupes ~$ brew tap josegonzalez/homebrew-php ~$ brew install php53
- Install useful PHP extensions (using brew)~$ brew install php53-xdebug – There are a smorgasbord of extensions to choose from:
- php-code-sniffer
- php53-apc
- php53-imagick
- php53-memcached
- php53-mongo
- php53-redis
- php53-uploadprogress
- php53-xdebug (recommended)
- php53-xhprof
These extensions are completely optional and can be installed at any time. You can always run a brew info package_name, and check out the web link for more info on each extension.
- Edit php.ini (xdebug settings & memory limit)If you like to use memory hungry PHP frameworks like Drupal, you will need to up the memory limit for PHP scripts.~$ emacs /usr/local/etc/php/5.3/php.iniSearch for memory_limit and set it to 2048M
MariaDB (MySQL)
MariaDB is an optimized fork of MySQL. It works great as a drop-in replacement for the standard version of MySQL. If you would rather not use MariaDB you can do all of these same steps, just replace the word mariadb with the word mysql.
~$ brew install mariadb ~$ cp $(brew --prefix mariadb)/support-files/my-large.cnf /usr/local/etc/my.cnf ~$ sed -i "" 's/max_allowed_packet = 1.*M/max_allowed_packet = 32M/g' /usr/local/etc/my.cnf ~$ cp /usr/local/opt/mariadb/homebrew.mxcl.mariadb.plist ~/Library/LaunchAgents/ ~$ mysql_install_db --verbose --user=`whoami` --basedir="$(brew --prefix mariadb)" --datadir=/usr/local/var/mysql --tmpdir=/tmp ~$ launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist ~$ mysqladmin -uroot password 'password'
Setting up Apache
Mac OS X has the Apache webserver built-in. Let’s make it play nice with our newly installed PHP module.
~$ sudo emacs /etc/apache2/httpd.conf
Add the following line at the end of the LoadModule section:
~$ LoadModule php5_module /usr/local/opt/php53/libexec/apache2/libphp5.so
Now we can restart apache:
~$ sudo apachectl restart
Setting up VirtualDocumentRoot
Apache allows for a wildcard to be used when specifying the document root. The implications of this are that you can create a directory, and apache will treat that directory as if it were a VirtualHost. No more creating separate VHosts for every project!
It can help to create a config file for the OS X user:
~$ sudo touch /etc/apache2/users/username.conf ~$ sudo emacs /etc/apache2/users/username.conf
<VirtualHost *:80> ServerName dev DocumentRoot /Users/username/Sites VirtualDocumentRoot /Users/username/Sites/%-2/htdocs UseCanonicalName Off <Directory "/Users/username/Sites/*/htdocs"> AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost>
This sets up Apache to serve your projects from the Sites folder inside your home directory. Apache will serve files from the htdocs folder, so websites will be stored on your local filesystem here:
~/Sites/projectname/htdocs
There is one other configuration setting that is nice to have:
~$ sudo emacs /etc/apache2/httpd.conf
Find the ServerName directive and change it to:
ServerName localhost:80
This will suppress the warning about apache not being able to resolve a fully qualified domain name. Now restart apache.
~$ sudo apachectl restart
From there, simply check that all is well.
Note: if you haven’t actually created a ~/Sites/project directory yet, you may get a warning here:
~$ apachectl -S
VirtualHost configuration: wildcard NameVirtualHosts and _default_ servers: *:80 dev (/etc/apache2/users/samheuck.conf:1) Syntax OK
Note that when using VirtualDocumentRoot, any site that is using RewriteRules to route all requests through a front controller (index.php), you will have to set RewriteBase / in .htaccess.
On Mac OS X, apache runs under the user _www. There is also a group called _www. Your project’s htdocs folders should have permissions set accordingly.
~$ chown -R username:_www project/htdocs
Setting up DNS
Now that Apache is all set up, we need an easier way to point our project names to localhost so that we can type something like projectname.dev in a browser. You could easily do this by adding lines to /etc/hosts, but that is tedious manual labor, and I will not stand for it.
~$ brew install dnsmasq ~$ sudo cp /usr/local/opt/dnsmasq/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
Setup the config file for dnsmasq:
~$ touch /usr/local/etc/dnsmasq.conf ~$ emacs /usr/local/etc/dnsmasq.conf # /usr/local/dnsmaq.conf address=/.dev/127.0.0.1 rebind-domain-ok=/xip.io/nip.io/
Start dnsmasq:
~$ sudo lunchy start dnsmasq
Dnsmasq is a caching DNS server that runs locally and handles DNS requests. We just set it up to send all requests ending in .dev to localhost. Now we just need to tell Mac OS to use dnsmasq instead of the DNS server that is assigned to you by your gateway. The most straightforward way of doing this is to simply add 127.0.0.1 to your list of DNS servers. The problem is that you will also need to make sure that you have another DNS server for other requests. I like to use OpenDNS.
Navigate to System Preferences -> Network -> WiFi (or your most used network interface) Click Advanced, and go the DNS tab. Under DNS servers, add 127.0.0.1 at the top of the list, followed by 208.67.222.222 (or any other nameserver).
There is a way to do this automatically with some scripting. This works well if you are always using different wireless networks and such.
Once DNS is set up, you should be able to:
~$ ping project.dev
and get a response from 127.0.0.1
That’s it! Now you can put your sites back into the sites directory you defined in your apache configuration, restore your databases, and you are off to the races. Happy coding!
LeslieHenry says
I’m setting up a new local dev environment on a Mac and I’m glad I found your post before I installed MAMP. Thanks for the write-up.
Jorge says
Hi, I would like to add error and access log files to my Users/username/Sites/project directory in a directory names logs/ so something like the line below
ErrorLog /Users/username/Sites/%-2/logs/error_log
I tried to add that right below the VirtualDocumentRoot line but it ends up giving me an error. Anyway, any advice would be very much appreciated.
Also, thanks for the write up. It worked perfectly.
Sam Heuck says
Thanks for the comments! I’m glad this setup is working for you guys.
Jorge – Unfortunately I don’t think that apache handles log location the same way that VirtualDocRoot handles the document root folder. The ErrorLog directive doesn’t understand the dynamic variables, although this would be a really cool feature. The way I get around this is actually by logging the name of the vhost for each log entry, and I also structure my log files as JSON. Then I can use command line tools to grep the JSON logs for specific vhosts.
If you really wanted to go crazy with logging, I’d check out Logstash for log management, and Kibana for full text search and analysis of saved logs.
Oscar Ishen says
Nice article! I see you prefer emacs over vim or nano… 😉
Sam Heuck says
Yeah, I can’t stand modal editing, and nano doesn’t have macros. Plus, there’s this: http://xkcd.com/378/
Bronius Motekaitis says
I’m just not seeing how to manually stop/start mariadb/mysqld. The mariadb way (mysql.server stop) reports success, but ps ax has other ideas.
Sam Heuck says
@broniusmotekaitis:disqus
Have you tried using lunchy? It should pick up on the plist file in ~/Library/LaunchAgents that controls the service.
~ lunchy stop mariadb
~ lunchy start mariadb
Bronius Motekaitis says
Right you are, Sam– Works like a charm, thanks!
Concerned Citizen says
Works great, perfect for multiple sites! One problem I’m having is not being able to work while the wireless is switched off. If this is a local environment, I need this to work without being on the internet!
Any thoughts?
Sam Heuck says
Yeah, I can’t remember which version of Mac OS started this, but it’s really annoying. The way around it is to use dnsmasq to resolve the .dev TLD, but an additional config file is required to tell Mac OS to use localhost for DNS lookups.
First, crate this directory: /etc/resolver
Then create this file: /etc/resolver/dev
To this file add the line: nameserver 127.0.0.1
That should solve your “no internet when the wireless is off” problem.
Concerned Citizen says
Hey Sam,
Thanks for the quick reply! Everything is working perfectly now. Thank you for the information and great site :).
Bronius Motekaitis says
Hi Sam- Just back from a system restore, I see that I was missing this one config. Please add it to your stock steps in the article above, bc I think it’s pretty fundamental to life, liberty, and the avoidance of 404.
Dovy Paukstys says
I love your tutorial! Sometimes I’m getting this HORRIBLE bug where if I get a PHP error suddenly the site will never show up again. I then have to change the directory name and suddenly I can access it again. Any ideas?
Ronald Diemicke says
Is there a way to use this to do local HTTPS?
gad2103 says
i don’t really understand apache very well. if i would like to have documents served from ~/Sites/project/ instead of placing them inside of ~/Sites/project/htdocs, how would i alter the virtual hosts file accordingly? i tried the obvious changes (removing the virtual document root and changing directory to just “/Users/username/Sites/”) but i kept getting 403.
otherwise, thanks for the awesome tutorial! i was going to install linux just to do everything you described here but it looks like i can develop on my new shiny macbook. 🙂
Delicate Petals says
If you are doing local Drupal you should checkout Kalabox!
Winn Jewett says
Thanks for pulling this all together. It’s been a lifesaver. The path for homebrew has changed slightly. The new command to run is:
ruby -e “$(curl -fsSkL raw.github.com/mxcl/homebrew/go/install)”
(note the /install at the end)
disj__loop212 says
This is a lovely post, thanks so much! I had some problems after installing Homebrew and trying to get PHP5.3 to install:
checking for gcc… clang
checking whether the C compiler works… no
configure: error: in `/private/tmp/unixodbc-9esL/unixODBC-2.3.1′:
configure: error: C compiler cannot create executables
It may be obvious for those who do a lot of compiling, but for Homebrew to work you need a compiler installed. This means that you need to have Xcode installed from Apple. Maybe there’s an easier way, but it was a 2GB install from the App Store from Apple. After I got that running, I came across another error:
checking for krb5-config… /usr/bin/krb5-config
checking for DSA_get_default_method in -lssl… no
checking for X509_free in -lcrypto… yes
checking for pkg-config… no
configure: error: Cannot find OpenSSL’s
Googling this I saw I had to open Xcode and accept the ToS. Finally I had to go into xcode prefs and under downloads, re-download command line tools. No more errors, everything downloads and makes, yay!
After getting it all installed and adding the .so lib to Apache’s conf, the .php files wouldn’t parse, they’d just serve up raw PHP (and raw HTML!). I had to add the handler to my Apache conf:
AddType application/x-httpd-php .php
As well, if you want Apache to look for index.php automatically, you have to add that to the index config:
DirectoryIndex index.html index.php
I’m not sure if previous attempts by me to MAMP (but not that MAMP) working natively had bungled my configs, but I figured I’d share my pains in case some one else bumped into them.
gad2103 says
does this affect connecting to the computer over the same network to view files from another machine? i can ping my machine, but i can’t seem to see any of my Sites/mysites.dev.
for example if my local machine’s ip is 192.168.3.5, i can ping it but i can’t see 192.168.3.4/mysite.dev
any advice?
Sam Heuck says
Because Apache is set up to use VirtualDocumentRoot and NameVirtualHosts, going directly to the ipaddress to access the site won’t work. So you’ll have to set up DNS, or use a hosts file to map domain names to the IP address of the machine hosting the sites.
You also might need to check your firewall settings on the machine hosting the sites. If port 80 is being blocked on the IP address 192.168.3.5, then your browser won’t be able to see the sites.
I think by default, apache will be bound to all interfaces, so it should be serving up sites for requests to 127.0.0.1 as well as your other network interfaces.
To check to see that ports are open on a given IP address, I recommend nmap, which can be installed via homebrew.
Cheers!
Concerned Citizen says
So I upgraded my MAC to 10.9 a while back and noticed my command line php -v says it’s on 5.3 but phpinfo() says 5.4. Any thoughts?
Sam Heuck says
Mavericks (OS 10.9) comes with v5.4. My guess is that the apache configuration was changed when you upgraded to 10.9 and it is set to use the system version of PHP.
I would take a look at the apache config and look for a line that looks like this:
~$ LoadModule php5_module /usr/local/opt/php53/libexec/apache2/libphp5.so
And make sure it is set to the Homebrew’d install of PHP (/usr/local/opt/)
Concerned Citizen says
Hey Sam,
I just realized that this morning after posting this! Absolutely love your post and thank you for continued support :). Hope you had a good new year!
Uzair Khan says
‘The server at project.dev can’t be found, because the DNS lookup failed’. For me it has been working on and off. Sometimes it starts pinging and showing the website correctly and then it randomly stops. The problem lies with dnsmasq for if I add the path to host file, it works just fine. Any suggestions as to what might be the issue?
Richard Schulte says
A new, additional step in setting up the josegonzalez php formulae:
brew tap homebrew/versions
because of https://github.com/Homebrew/homebrew-php/commit/3fb59bcc5317fe247f8999136ba76030a2840909
otherwise, you’ll get this error upon `brew install php53`:
No available formula for bison27 (dependency of php53)
Winn Jewett says
Thank you! I just ran into this error. Your solution fixed it for me.
chris luther says
This has been my goto article for setting up Native Lamp Stack under OS X for sometime now. But I just upgraded to Yosemite (OS X 10.10) and discovered some adjustments which need to be made to this configuration guide.
HOMEBREW UPDATE
( http://jcvangent.com/fixing-homebrew-os-x-10-10-yosemite/ )
I wish I would have known, but before you upgrade to Yosemite you should first update Homebrew:
$ brew update
If you’ve already upgraded you can fix your brew installation using GIT:
$ cd /usr/local
$ git pull
APACHE 2.4
Apache 2.4 and PHP 5.5.14 are included with Yosemite. After upgrading I started having issues with Apache and PHP configuration. Testing the Apache configuration:
$ apache -t
resulted in the following error:
httpd: Syntax error on line 527 of /private/etc/apache2/httpd.conf: Syntax error on line 8 of /private/etc/apache2/other/+php-osx.conf: Cannot load /usr/local/php5/libphp5.so into server: dlopen(/usr/local/php5/libphp5.so, 10): Symbol not found: _unixd_confign Referenced from: /usr/local/php5/libphp5.son Expected in: /usr/sbin/httpdn in /usr/local/php5/libphp5.so
Is seems that the file which was being referenced by my brewed php install, “unixd_config” has been renamed to “ap_unixd_config” in Apache 2.4 ( see http://httpd.apache.org/docs/2.4/developer/new_api_2_4.html ).
One solution is simply to refer to the native PHP 5.5 install, which can be turned on by uncommenting the following line in the httpd.conf file.
LoadModule php5_module libexec/apache2/libphp5.so
I also needed to uncomment in the httpd.conf file:
AddType application/x-httpd-php .php
VIRTUAL HOSTS
In Apache 2.4 the directives Order, Allow, Deny and Satisfy have been deprecated (see http://httpd.apache.org/docs/2.4/upgrading.html ). I’ve modified the Vhost file to be:
ServerName dev
DocumentRoot /Users/username/Sites
VirtualDocumentRoot /Users/username/Sites/%-2/htdocs
UseCanonicalName Off
AllowOverride All
Require all granted
Robb Lee says
Chris – thanks for sharing this update regarding Homebrew & Yosemite – Very helpful!
gad2103 says
On OS X 10.10+ this does not seem to work when wifi is turned off. Is there a way to get this running without a network connection? This was working perfectly before the update to Yosemite…THanks!
xelber says
Just for the benefit of the others, homebrew installation should be updated as
ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”
texas-bronius says
This page is still a gem!
Little tip on *.dev domains: Don’t do it [anymore] (unless you have local ssl set up). Google owns the gTLD, and the Chrome browser is now forcing *.dev domain local development sites to https. For local development you’ll want something else like *.local or *.localdev.
texas-bronius says
This page is still a gem!
Little tip on *.dev domains: Don’t do it [anymore] (unless you have local ssl set up). Google owns the gTLD, and the Chrome browser is now forcing *.dev domain local development sites to https. For local development you’ll want something else like *.localdev (note: At least for me, *.local didn’t work at all, but *.localdev does)