Set up Nginx and Multiple PHP versions on MacOS. In the end, you will have a robust, clean, and fast local web development environment on Mac’s Intel or Apple Silicon Chipsets.
Verified it’s working on MacOS Soloma (14.0)
- Xcode, VS Code & Homebrew (required)
- Multiple PHP Version (Required)
- Xdebug (Optional - But highly recommend)
- Nginx & SSL (Required)
- MySQL (Optional)
- Mailhog (Optional)
Before starting you need a few tools installed to take the stress out of the setup process
You will be using the terminal a lot coming up (I like iTerm2)
1# Install XCode
2xcode-select --install
3
4# Install Homebrew
5/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
6
7# Install VS Code
8brew install --cask visual-studio-code
9
10# Install OpenSSL (If you need configuration https for domain
11brew install openssl
12
13# Install wget
14brew install wget
Some PHP version needs to be included on the default core tab homebrew. Use tap shivammathur/php
1brew tap shivammathur/php
2
3# Install PHP Version
4brew install shivammathur/php/[email protected]
5brew install shivammathur/php/[email protected]
6brew install shivammathur/php/[email protected]
All PHP versions are using port
9000
. We need to change the port for each PHP version to avoid conflict. Path file is "$(brew --prefix)/etc/<php_version>/php-fpm.d/www.conf"
Example for PHP 7.4:
1# Open www.conf
2code "$(brew --prefix)/etc/php/7.4/php-fpm.d/www.conf"
1# default
2user = _www
3group = _www
4listen = 127.0.0.1:9000
5
6# change to
7user = <your_username>
8group = staff
9listen = 127.0.0.1:9074
Before starting
php-fpm
, if you want to make edits to a php.ini
file now in this time. For example, you might want to increase upload_max_file_size
and post_max_file_size
to 10M
1# Backup PHP.ini
2cp "$(brew --prefix)/etc/php/7.4/php.ini" "$(brew --prefix)/etc/php/7.4/php.ini.orig"
3code "$(brew --prefix)/etc/php/7.4/php.ini"
Once you are ready, start up
php-fpm
for each version1brew services start [email protected]
2brew services start [email protected]
3brew services start [email protected]
Check that you have the configuration correctly
1sudo lsof -i -n -P|grep php-fpm
Alias multiple PHP Version
1echo "alias php74=\"$(brew --prefix)/opt/[email protected]/bin/php\"" >> ~/.zshrc
2echo "alias php81=\"$(brew --prefix)/opt/[email protected]/bin/php\"" >> ~/.zshrc
3echo "alias php82=\"$(brew --prefix)/opt/[email protected]/bin/php\"" >> ~/.zshrc
Once you reload your bash file, you can access each alias from the Mac Command line.
1php74 -v
2php81 -v
3php82 -v
Switch between multiple PHP Version
1function phpv() {
2 brew unlink php
3 brew link --overwrite --force "php@$1"
4 php -v
5}
1brew link --overwrite --force [email protected] # Switch to PHP 8.2
2pecl install xdebug
P/S: If you want to install
xdebug
for “PHP” ≥ 8.0. You need to install xdebug
version 3.1.6 due to the new version only supports “PHP” ≥ 8.1. You can see it on the xdebug homepage1pecl install xdebug-3.1.6
Installing Nginx
1# Install nginx
2brew install nginx
3# Start nginx
4sudo nginx
5# Test nginx
6curl http://localhost:8080
Change default Nginx configuration
1code "$(brew --prefix)/etc/nginx/nginx.conf"
1# REPLACE #user nobody;
2user <your-username> staff;
3
4# NEXT, ADD THE FOLLOWING TO THE http {} block
5http {
6 ...
7 server_names_hash_bucket_size 512;
8}
9
10# UPDATE INSIDE server {} block
11
12server {
13 ...
14 listen 80 default_server;
15 server_name _;
16}
17
18# ADD FastCGI gateway to PHP-FPM
19location ~ \.php$ {
20 try_files $uri =404;
21 fastcgi_pass 127.0.0.1:9081; # Using PHP 8.1
22 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
23 include fastcgi_params;
24}
25
26# ADD Some Basic Security Header
27add_header X-Frame-Options "SAMEORIGIN";
28add_header X-XSS-Protection "1; mode=block";
29add_header X-Content-Type-Options "nosniff";
30
31# SET Charset UTF-8
32charset utf-8;
33
34# ALLOW LARGE FILE UPLOAD
35http {
36 ...
37 client_max_body_size 100M;
38}
By Default Nginx root folder is
$(brew --prefix)/var/www/
If you want to change the root folder
1server {
2 ...
3 root <absolute-path>
4}
Check the PHP code to make sure everything is working. In the root document nginx, create a new file
index.php
1<?php echo phpinfo(); ?>
Reload Nginx to apply the new config
1sudo nginx -s reload
Go to
http://localhost
to see the resultTo add more servers you can go to the nginx servers directory:
"$(brew --prefix)/etc/nginx/servers"
Using SSL
Installing
mkcert
1brew install mkcert
2brew install nss # if you use Firefox
3mkdir "$(brew --prefix)/etc/nginx/ssl"
4cd "$(brew --prefix)/etc/nginx/ssl"
5mkcert -install
6mkcert localhost
Change default configuration Nginx
1code "$(brew --prefix)/etc/nginx/nginx.conf"
1server {
2 http2 on;
3 listen 443 ssl;
4 listen [::]:443 ssl;
5 server_name localhost;
6 ssl_certificate /opt/homebrew/etc/nginx/ssl/localhost.pem;
7 ssl_certificate_key /opt/homebrew/etc/nginx/ssl/localhost-key.pem;
8 ssl_ciphers HIGH:!aNULL:!MD5;
9}
Auto redirect HTTPS
1server {
2 listen 80 default_server;
3 server_name _;
4 return 301 https://$host$request_uri;
5}
Installing MySQL
1brew install mysql
2brew services start mysql
3
4# mysql secure installation
5mysql_secure_installation
Configuration MySQL
1code "$(brew --prefix)/etc/my.cnf"
1# Default Homebrew MySQL server config
2[mysqld]
3# Only allow connections from localhost
4bind-address = 127.0.0.1
5mysqlx-bind-address = 127.0.0.1
6
7innodb_buffer_pool_instances=8
8innodb_buffer_pool_size=4G
9innodb_buffer_pool_chunk_size=512M
10max_allowed_packet=1024M
11sort_buffer_size=256M
12max_connections=1024
13default_authentication_plugin=mysql_native_password
14
15innodb_ft_min_token_size=2
16
17# Add mode only if needed
18sql_mode = "ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
19
20# Set Default Charset
21character-set-server=utf8
22collation-server=utf8_general_ci
1brew install phpmyadmin
Configuration Nginx to allow “PHPMyAdmin”
1location /phpmyadmin {
2 alias /opt/homebrew/share/phpmyadmin;
3 index index.php index.html index.htm;
4 location ~ \.php$ {
5 fastcgi_split_path_info ^(.+\.php)(/.+)$;
6 fastcgi_read_timeout 300;
7 fastcgi_pass 127.0.0.1:9081;
8 fastcgi_param SCRIPT_FILENAME $request_filename;
9 include fastcgi_params;
10 }
11}
Go to
https://localhost/phpmyadmin
1brew install mailhog
2brew services start mailhog
3
4# start with
5mailhog
Now, you can access MailHog at
http://localhost:8025
.Send a test email and check MailHog
1echo "Test email from Postfix" | mail -s "Test Email" [email protected]
Next, update each
php.ini
file with the following, if you have multiple versions of PHP, and then restart php-fpm
. Note, test@localhost
should be used but will be overridden by any PHP scripts that run1sendmail_path = /opt/homebrew/opt/mailhog/bin/MailHog sendmail test@localhost
From Magento Version ≥ 2.4, It has changed MySQL search to Elasticsearch.
Install Elasticsearch & OpenJDK
1brew tap elastic/tap
2brew install elastic/tap/elasticsearch-full
3brew install openjdk@17
Symlink OpenJDK so the system Java wrappers can find it. This information is also displayed as a Homebrew “caveat” after installing OpenJDK.
1sudo ln -sfn /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
Change
.zshrc
1export ES_JAVA_HOME=/opt/homebrew/Cellar/openjdk@17/<openjdk-version>/libexec/openjdk.jdk/Contents/Home
Modify
elasticsearch.plist
to use the OpenJDK1# homebrew.mxcl.elasticsearch-full.plist
2<key>EnvironmentVariables</key>
3<dict>
4 <key>ES_JAVA_HOME</key>
5 <string>/opt/homebrew/Cellar/openjdk@17/<openjdk-version>/libexec/openjdk.jdk/Contents/Home</string>
6</dict>
Run & Test Elasticsearch
1elasticsearch # option -d to run detach
2
3curl -s http://localhost:9200
Troubleshooting
Check more on Gist
To save yourself the fuss of editing your
hosts
file constantly you can use dnsmasq
1brew install dnsmasq
1echo 'address=/.test/127.0.0.1' > /opt/homebrew/etc/dnsmasq.conf
2sudo mkdir -v /etc/resolver
3sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/test'
Start or restart “dnsmasq” (Need to sudo)
1sudo brew services start dnsmasq
Confirm “dnsmasq” is working with a ping TLDs, one at a time
1ping -c 1 test.test