A LEMP Stack for a MacBook Pro M1 localhost development in 2022 (Nginx, MySQL, and PHP-FPM)

Development,Projects,Snippets,WooCommerce,WordPress

Just picked up a new M1 MacBook Pro and I decided it was finally time to document my process for getting a working local LEMP environment up and running. It’s not perfect, but it works for the theme development work I typically do. There’s no SSL (and no plans to enable that at this time).

I just want my environment to be fast and work with as many native applications as possible, while also being extremely easy for me to spin up a new dev environment. My previous setup used a vhost file to make each subdirectory in my ~/Sites folder into its own *.localhost domain and I wanted to keep that up. So, here’s what I came up with. A localhost LEMP stack tailored to a fresh installation of macOS Monterey in 2022. If you follow the tutorial and end up with issues, questions, or suggestions for improvement, please leave a comment and I’ll do my best to reply.

1. Install Homebrew and xcode

Open your terminal app and run:

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

2. Install Nginx, MySQL, phpMyAdmin, and dnsmasq

Open your terminal app and run:

$ brew install nginx mysql phpmyadmin dnsmasq php@7.4

3. Make a dev directory

Open your terminal app and run:

$ mkdir ~/Sites

4. Setup dnsmasq

Open your terminal app and run:

$ echo 'address=/.dev/127.0.0.1' > $(brew --prefix)/etc/dnsmasq.conf
$ echo 'listen-address=127.0.0.1' >> $(brew --prefix)/etc/dnsmasq.conf
$ echo 'port=35353' >> $(brew --prefix)/etc/dnsmasq.conf
$ brew services start dnsmasq
$ sudo mkdir -v /etc/resolver
$ sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/dev'
$ sudo bash -c 'echo "port 35353" >> /etc/resolver/dev'

To test the dnsmasq setup, open your terminal app and run:

$ ping -c 3 fakedomainthatisntreal.localhost

The results should return from the IP address 127.0.0.1. If it doesn’t work right away, try turning WiFi off and on (or unplug/plug your ethernet cable), or reboot your system.

5. Setup mysql

Open your terminal app and run:

$ brew services start mysql
$ mysql_secure_installation

Complete the mysql_secure_installation; the default pass is empty for root, follow instructions (should be able to just hit enter for each prompt).

Now we’ll create an admin user for mysql that can access all of the databases. We’ll call this user admin.

Open your terminal app and run:

$ mysql -uroot -p
$ CREATE USER 'admin'@'localhost' IDENTIFIED BY '';
$ GRANT ALL PRIVILEGES ON * . * TO 'admin'@'localhost';
$ FLUSH PRIVILEGES;

You can now use the mysql user admin to connect to any database.

Edit /opt/homebrew/etc/my.cnf and add these two lines to the bottom:

character-set-server=utf8
collation-server=utf8_general_ci

6. Setup nginx

First, let’s check to make sure the default config is working properly. Open your terminal app and run:

$ sudo nginx -t

If you see any error messages or warnings then you should Google them and adjust your nginx config file accordingly (by default it’s @ /opt/homebrew/etc/nginx/nginx.conf). If everything looks good then run:

$ brew services start nginx

Now use your web browser to visit http://localhost:8080 and you should see the “Welcome to nginx!” message.

Using your favorite text editor, we now need to setup the nginx config for our system, for phpmyadmin, and for each of our development sites. To save time, I pasted the files I use to configure these here: https://gist.github.com/cfxd/a6e5d29aa5f5b60403f4eb1e73e78a6d

The top two comments in each file there include instructions. The files should be saved to /opt/homebrew/etc/nginx/nginx.conf, /opt/homebrew/etc/nginx/servers/phpmyadmin.conf, and /opt/homebrew/etc/nginx/servers/php-fpm.conf.

After doing that, restart nginx:

$ sudo brew services restart nginx

If you encounter any errors, you can always run $ sudo nginx -t to test the nginx config and Google any errors or warnings (or post in the comments below).

7. Configure php-fpm

First, let’s double check to see where the php-fpm.conf default file is located. Open your terminal app and run:

$ php-fpm -t

We should see the location of the default config file which we will copy and then edit.

$ sudo cp /opt/homebrew/etc/php/7.4/php-fpm.conf /private/etc

In the error log section of the new file, add error_log = /private/var/log/php-fpm.log and create that file.

Now do:

$ sudo mkdir /private/etc/php-fpm.d
$ sudo cp /opt/homebrew/etc/php/7.4/php-fpm.d/www.conf /private/etc/php-fpm.d

Now let’s edit /private/etc/php-fpm.d/www.conf. Under the section that says “; Unix user/group of processes” make sure to have these two lines:

user = YOUR_USERNAME
group = staff

Replace YOUR_USERNAME with your local username.

8. Configure phpmyadmin

Edit /opt/homebrew/etc/phpmyadmin.config.inc.php.

On line 16, set a blowfish secret (use the generator at https://phpsolved.com/phpmyadmin-blowfish-secret-generator/).

Change this line 30:

$cfg['Servers'][$i]['host'] = 'localhost';

Into this:

$cfg['Servers'][$i]['host'] = '127.0.0.1';

Now do this in terminal:

$ sudo cp /opt/homebrew/etc/php/7.4/php.ini /etc/php.ini

Let’s edit that new file at /etc/php.ini and add this to the very bottom:

max_execution_time = 300
max_input_time = 600
max_input_vars = 1000
memory_limit = 512M
error_log = /Users/YOUR_USERNAME/Sites/php-error_log.log
post_max_size = 200M
upload_max_filesize = 100M
default_socket_timeout = 600
date.timezone = 'America/New_York'

Replace YOUR_USERNAME with your local username.

Now run this from your terminal:

$ brew services stop php@7.4 && brew services start php@7.4
$ sudo brew services restart nginx

You should now be able to access phpmyadmin through your web browser by visiting http://phpmyadmin.localhost and logging in with the admin username and password we created above in Step 5. You may see a warning at the bottom of the phpmyadmin screen—just click “Find out why” and then in that page’s warning click “Create.”

9. Setup port forwarding

Save the file at https://gist.github.com/cfxd/a5f177386ce01bf96f3d4c2b803bb792 into /Library/LaunchDaemons.

Now run this from terminal:

$ sudo chown root:admin /Library/LaunchDaemons/httpdfwd.plist && sudo launchctl load -Fw /Library/LaunchDaemons/httpdfwd.plist

10. Conclusion

You should now have a working LEMP stack on your M1 MacBook. If you want to create a new WordPress site, you can simply add a new subdirectory to ~/Sites and then within that directory you can install a local copy of WordPress into the wp/ subdirectory. This way, you can put documents or design assets in the dir at ~/Sites/example and then the local WordPress install should be installed in ~/Sites/example/wp and you can access the frontend at http://example.localhost in your browser.

If you experience any issues, you can use this command to stop the LEMP stack:

$ sudo brew services stop nginx && brew services stop mysql && brew services stop php@7.4 && brew services stop dnsmasq

Then restart everything using this command:

$ sudo brew services start nginx && brew services start mysql && brew services start php@7.4 && brew services start dnsmasq

Let me know if you run into any issues and I’ll do my best to help.

Thanks to Nicholas K. Dionysopoulos and his article “Set up NginX and PHP for development on Mac OS X” and thanks to this gist https://gist.github.com/angrycoffeemonster/5944826.


Leave a Comment