├── .github └── workflows │ └── build.yml ├── CHANGELOG.md ├── README.md └── install.sh /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | release-osx: 6 | name: macOS Homebrew 7 | runs-on: macos-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Run installer 11 | run: | 12 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/digitoimistodude/macos-lemp-setup/master/install.sh)" 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.0.9: 2024-06-26 2 | 3 | * Make sure folders exist 4 | * More verbose output for the install script 5 | 6 | ### 1.0.8: 2024-04-12 7 | 8 | * Add unit tests (GitHub actions) for macOS, Homebrew and install.sh 9 | * Add build status badge 10 | * Rewrite install.sh for PHP 8.3, use variables instead of hardcoded paths and versions 11 | * PHP 8.3 support 12 | 13 | ### 1.0.7: 2023-05-12 14 | 15 | * Fix duplicate symlink for nginx folder 16 | * Add php-fpm user, group and listen to PHP post installation instructions 17 | 18 | ### 1.0.6: 2022-11-28 19 | 20 | * Remove derepcated homebrew/dupes and homebrew/versions taps (part of homebrew/core) 21 | * Remove deprecated LaunchAgents 22 | * Change deprecated homebrew/homebrew-php to shivammathur/php 23 | * Fix install commands for php@7.4 24 | 25 | ### 1.0.5: 2022-08-16 26 | 27 | * Change xdebug.mode from debug to develop so var_dump will be shown correctly 28 | * Fixes and clean ups for macOS M1 29 | 30 | ### 1.0.4: 2021-11-13 31 | 32 | * Fix installer oneliner syntax, change to curl for better reliability 33 | 34 | ### 1.0.3: 2021-09-23 35 | 36 | * Add MailHog for local email testing 37 | 38 | ### 1.0.2: 2021-03-24 39 | 40 | * Start public versioning 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LEMP: Local Environment Made Powerful 2 | [![Build](https://github.com/digitoimistodude/macos-lemp-setup/actions/workflows/build.yml/badge.svg)](https://github.com/digitoimistodude/macos-lemp-setup/actions/workflows/build.yml) [![GitHub release](https://img.shields.io/github/tag/digitoimistodude/macos-lemp-setup.svg)](https://github.com/digitoimistodude/macos-lemp-setup/releases) ![GitHub contributors](https://img.shields.io/github/contributors/digitoimistodude/macos-lemp-setup.svg) ![PHP](https://img.shields.io/badge/PHP-8.3-7a86b8) 3 | 4 | Just kidding, it's really **Linux + nginx [engine x] + MySQL + PHP-FPM**, that's where the LEMP comes from. MacOS LEMP Setup is just like Local by Flywheel or XAMPP, WAMP, Hammer, Anvil etc. tools like this, but it concentrates on the native way of running a web server just by using native Linux packages ported for macOS via Homebrew. It's always fast, always on. 5 | 6 | ## LEMP on other systems 7 | 8 | - [Windows version](https://github.com/digitoimistodude/windows-lemp-setup) 9 | - [Linux version](https://github.com/raikasdev/pop-lemp-setup) 10 | 11 | ## Still the best way to locally develop WordPress. 12 | 13 | ![macos-lemp](https://user-images.githubusercontent.com/1534150/159696251-1b8adbee-f752-4107-9183-78107ffb4969.png) 14 | 15 | **macOS LEMP Setup is Mac only!** 16 | 17 | ### Other platforms 18 | 19 | - Windows: [Setting up a local server on Windows 10 for WordPress theme development (or any web development for that matter)](https://rolle.design/local-server-on-windows-10-for-wordpress-theme-development). 20 | - Linux: [Pop!_OS LEMP setup instructions](https://github.com/raikasdev/pop-lemp-setup) by [raikasdev](https://github.com/raikasdev). 21 | 22 | ## Minimum requirements 23 | 24 | * Latest [Homebrew](https://brew.sh/) 25 | * MacBook Pro M1 (if you like to install this on Intel mac, refer to [this tutorial](https://kevdees.com/macos-12-monterey-nginx-setup-multiple-php-versions/)) 26 | * macOS Monterey 12.3 or later 27 | 28 | ## Install local LEMP for macOS 29 | 30 | For *Front End development*, a full Vagrant box, docker container per site or Local by Flywheel is not really needed. If you have a Macbook Pro, you can install local LEMP (Linux, nginx, MariaDB and PHP) with this single liner below. 31 | 32 | Please see [installation steps](#installation) instructions first. 33 | 34 | ```` bash 35 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/digitoimistodude/macos-lemp-setup/master/install.sh)" 36 | ```` 37 | 38 | Oneliner may not go through in macOS Big Sur and macOS Monterey, in that caes you need to copy and paste commands manually from [install.sh](https://raw.githubusercontent.com/digitoimistodude/macos-lemp-setup/master/install.sh). 39 | 40 | **Please note:** Don't trust blindly to the script, use only if you know what you are doing. You can view the file [here](https://github.com/digitoimistodude/osx-lemp-setup/blob/master/install.sh) if having doubts what commands are being run. However, script is tested working many times and should be safe to run even if you have some or all of the components already installed. 41 | 42 | ## Table of contents 43 | 44 | 1. [Background](#background) 45 | 2. [Features](#features) 46 | 3. [Requirements](#requirements) 47 | 4. [Installation](#installation) 48 | 5. [Post installations](#post-installations) 49 | 1. [Mailhog](#MailHog) 50 | 6. [Use Linux-style aliases](#use-linux-style-aliases) 51 | 7. [File sizes](#file-sizes) 52 | 8. [XDebug](#xdebug) 53 | 9. [Redis](#redis) 54 | 10. [Troubleshooting](#troubleshooting) 55 | 56 | ### Background 57 | 58 | Read the full story by [@ronilaukkarinen](https://github.com/ronilaukkarinen): **[Moving from Vagrant to a LEMP stack directly on a Macbook Pro (for WordPress development)](https://medium.com/@rolle/moving-from-vagrant-to-a-lemp-stack-directly-on-a-macbook-pro-e935b1bc5a38)** 59 | 60 | ### Features 61 | 62 | - PHP 8.3 63 | - nginx 1.25.3 64 | - Super lightweight 65 | - Native packages 66 | - Always on system service 67 | - HTTPS support 68 | - Consistent with production setup 69 | 70 | ### Requirements 71 | 72 | - [Homebrew](https://brew.sh/) 73 | - macOS, preferably 14.2.1 (Sonoma) 74 | - wget 75 | - [mkcert](https://github.com/FiloSottile/mkcert) 76 | 77 | ### Installation 78 | 79 | 1. Run oneliner installation script `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/digitoimistodude/macos-lemp-setup/master/install.sh)"` 80 | 2. Link PHP executable like this: **Run:** `sudo find / -name 'php'`. When you spot link that looks like this (yours might be different version) */usr/local/Cellar/php@8.3/8.3.3/bin/php*, symlink it to correct location to override MacOS's own file: `sudo ln -s /usr/local/Cellar/php@8.3/8.3.3/bin/php /usr/local/bin/php` 81 | 3. Use PHP path from correct location by adding to your ~/.bash_profile file, `sudo nano ~/.bash_profile` (change your PHP version accordingly) 82 | ``` shell 83 | export PATH="$(brew --prefix php@8.3)/bin:$PATH" 84 | ``` 85 | 4. Check the version with `php --version`, it should match the linked file. 86 | 5. Brew should have already handled other links, you can test the correct versions with `sudo mysql --version` (if it's something like _mysql Ver 15.1 Distrib 10.5.5-MariaDB, for osx10.15 (x86_64) using readline 5.1_ it's the correct one) and `sudo nginx -v` (if it's something like nginx version: nginx/1.19.3 it's the correct one) 87 | 6. Add `export PATH="$(brew --prefix php@8.3)/bin:$PATH"` to .bash_profile (or to your zsh profile or to whatever term profile you are currently using) 88 | 7. Go through [post installations](#post-installations) 89 | 8. Enjoy! If you use [dudestack](https://github.com/digitoimistodude/dudestack), please check instructions from [its own repo](https://github.com/digitoimistodude/dudestack). 90 | 91 | ### Post installations 92 | 93 | #### PHP config 94 | 95 | You may want to add your user and group correctly to `/opt/homebrew/etc/php/8.3/php-fpm.d/www.conf` (or wherever your www.conf is, find with `sudo find / -name 'www.conf'`) and set these to the bottom: 96 | 97 | ```` 98 | catch_workers_output = yes 99 | php_flag[display_errors] = On 100 | php_admin_value[error_log] = /var/log/fpm-php.www.log 101 | slowlog = /var/log/fpm-php.slow.log 102 | php_admin_flag[log_errors] = On 103 | php_admin_value[memory_limit] = 1024M 104 | request_slowlog_timeout = 10 105 | php_admin_value[upload_max_filesize] = 100M 106 | php_admin_value[post_max_size] = 100M 107 | ```` 108 | 109 | Please note, if the file is not found (as the location may also be something like `/System/Volumes/Data/opt/homebrew/etc/php/8.3/php-fpm.d/www.conf`), you can find the correct location with: 110 | 111 | ``` 112 | sudo find / -name 'www.conf' 113 | ``` 114 | 115 | #### Make sure the PHP runs on correct permissions 116 | 117 | Make sure you have your user and group defined, use these as base (only change `rolle` to your own Mac username): 118 | 119 | ````config 120 | ; Unix user/group of processes 121 | ; Note: The user is mandatory. If the group is not set, the default user's group 122 | ; will be used. 123 | user = rolle 124 | group = admin 125 | ```` 126 | 127 | Also make sure you have listen set up properly 128 | 129 | ````config 130 | ; The address on which to accept FastCGI requests. 131 | ; Valid syntaxes are: 132 | ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on 133 | ; a specific port; 134 | ; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on 135 | ; a specific port; 136 | ; 'port' - to listen on a TCP socket to all addresses 137 | ; (IPv6 and IPv4-mapped) on a specific port; 138 | ; '/path/to/unix/socket' - to listen on a unix socket. 139 | ; Note: This value is mandatory. 140 | listen = 127.0.0.1:9000 141 | ```` 142 | 143 | #### Default nginx config 144 | 145 | Make sure you have default vhost for your site (`/etc/nginx/sites-enabled/sitename.test`) could be something like: 146 | 147 | ```` nginx 148 | server { 149 | listen 80; 150 | root /var/www/example; 151 | index index.html index.htm index.php; 152 | server_name example.test www.example.test; 153 | include php.conf; 154 | include global/wordpress.conf; 155 | } 156 | ```` 157 | 158 | #### Default MySQL my.cnf 159 | 160 | Default my.cnf would be something like this (already added by install.sh in `/usr/local/etc/my.cnf`: 161 | 162 | ```` 163 | # 164 | # This group is read both both by the client and the server 165 | # use it for options that affect everything 166 | # 167 | [client-server] 168 | 169 | # 170 | # include all files from the config directory 171 | # 172 | !includedir /usr/local/etc/my.cnf.d 173 | 174 | [mysqld] 175 | innodb_log_file_size = 32M 176 | innodb_buffer_pool_size = 1024M 177 | innodb_log_buffer_size = 4M 178 | slow_query_log = 1 179 | query_cache_limit = 512K 180 | query_cache_size = 128M 181 | skip-name-resolve 182 | ```` 183 | 184 | Again, if the correct file cannot be found, you can find it with: 185 | 186 | ``` 187 | sudo find / -name 'my.cnf' 188 | ``` 189 | 190 | For mysql, remember to run `sudo mysql_secure_installation`, answer as suggested, add/change root password, remove test users etc. Only exception! Answer with n to the question Disallow root login remotely? [Y/n]. Your logs can be found at `/usr/local/var/mysql/yourcomputername.err` (where yourcomputername is obviously your hostname). 191 | 192 | After that, get to know [dudestack](https://github.com/digitoimistodude/dudestack) to get everything up and running smoothly. Current version of dudestack supports macOS LEMP stack. 193 | 194 | You should remember to add vhosts to your /etc/hosts file, for example: `127.0.0.1 site.test`. 195 | 196 | ### Symlinks 197 | 198 | You should find the correct files and link them like in Linux. This helps you to remember the correct paths. 199 | 200 | For example (please note, this is just an example): 201 | 202 | ```bash 203 | sudo mkdir -p /usr/local/bin 204 | sudo ln -s /opt/homebrew/Cellar/php@8.3/8.3.3/bin/php /usr/local/bin/php 205 | sudo ln -s /opt/homebrew/Cellar/php@8.3/8.3.3/sbin/php-fpm /usr/local/bin/php-fpm 206 | sudo ln -s /opt/homebrew/Cellar/php@8.3/8.3.3/sbin/php-fpm /usr/local/bin/php-fpm 207 | sudo ln -s /opt/homebrew/etc/php /etc/php 208 | sudo ln -s /opt/homebrew/etc/nginx /etc/nginx 209 | sudo ln -s /opt/homebrew/etc/my.cnf /etc/my.cnf 210 | ``` 211 | 212 | ### Use Linux-style aliases 213 | 214 | Add this to */usr/local/bin/service* and chmod it +x: 215 | 216 | ```` bash 217 | #!/bin/bash 218 | # Alias for unix type of commands 219 | brew services "$2" "$1"; 220 | ```` 221 | 222 | Now you are able to restart nginx and mysql unix style like this: 223 | 224 | ```` bash 225 | sudo service nginx restart 226 | sudo service mariadb restart 227 | ```` 228 | 229 | #### MailHog 230 | 231 | E-mails won't be sent on local environment because there is no email server configured. This is where [MailHog](https://github.com/mailhog/MailHog) comes in. 232 | 233 | MailHog should be pre-installed but if not, run following: 234 | 235 | ``` bash 236 | brew update && brew install mailhog 237 | ``` 238 | 239 | Ensure you have the latest [air-helper](https://github.com/digitoimistodude/air-helper) or [MailHog for WordPress](https://wordpress.org/plugins/wp-mailhog-smtp/) activated to enable MailHog routing for local environment. 240 | 241 | Then just run: 242 | 243 | ``` bash 244 | mailhog 245 | ``` 246 | 247 | You should now get a log in command line and web interface is available in http://0.0.0.0:8025/. 248 | 249 | ### File sizes 250 | 251 | You might want to increase file sizes for development environment in case you need to test compression plugins and other stuff in WordPress. To do so, edit `/usr/local/etc/php/8.3/php-fpm.d/www.conf` and `/usr/local/etc/php/8.3/php.ini` and change all **memory_limit**, **post_max_size** and **upload_max_filesize** to something that is not so limited, for example **500M**. 252 | 253 | Please note, you also need to change **client_max_body_size** to the same amount in `/etc/nginx/nginx.conf`. After this, restart php-fpm with `sudo brew services restart php@8.3` and nginx with `sudo brew services restart nginx`. 254 | 255 | ### Certificates for localhost 256 | 257 | First things first, if you haven't done it yet, generate general dhparam: 258 | 259 | ```` bash 260 | sudo su - 261 | cd /etc/ssl/certs 262 | sudo openssl dhparam -dsaparam -out dhparam.pem 4096 263 | ```` 264 | 265 | Generating certificates for dev environment is easiest with [mkcert](https://github.com/FiloSottile/mkcert). After installing mkcert, just run: 266 | 267 | ```` bash 268 | mkdir -p /var/www/certs && cd /var/www/certs && mkcert "project.test" 269 | ```` 270 | 271 | Then edit your vhost as following (change all from *project* to your project name): 272 | 273 | ```` nginx 274 | server { 275 | listen 443 ssl; 276 | http2 on; 277 | root /var/www/project; 278 | index index.php; 279 | server_name project.test; 280 | 281 | include php.conf; 282 | include global/wordpress.conf; 283 | 284 | ssl_certificate /var/www/certs/project.test.pem; 285 | ssl_certificate_key /var/www/certs/project.test-key.pem; 286 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 287 | ssl_prefer_server_ciphers on; 288 | ssl_dhparam /etc/ssl/certs/dhparam.pem; 289 | ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; 290 | ssl_session_timeout 1d; 291 | ssl_session_cache shared:SSL:50m; 292 | ssl_stapling_verify on; 293 | add_header Strict-Transport-Security max-age=15768000; 294 | } 295 | 296 | server { 297 | listen 80; 298 | server_name project.test; 299 | return 301 https://$host$request_uri; 300 | } 301 | ```` 302 | 303 | Test with `sudo nginx -t` and if everything is OK, restart nginx. 304 | 305 | ### XDebug 306 | 307 | 1. Check your PHP version with `php --version` and location with `which php`. If the location points to `/usr/bin/php`, you are mistakenly using macOS built-in PHP. Change PHP path to correct location by adding to your ~/.bash_profile file, `sudo nano ~/.bash_profile` (change your PHP version accordingly): 308 | ``` shell 309 | export PATH="$(brew --prefix php@8.3)/bin:$PATH" 310 | ``` 311 | 2. Install xdebug 312 | 313 | ```bash 314 | brew install shivammathur/extensions/xdebug@8.3 315 | ``` 316 | 317 | ``` shell 318 | $ php --version 319 | PHP 8.3.3 (cli) (built: Feb 13 2024 15:41:14) (NTS) 320 | Copyright (c) The PHP Group 321 | Zend Engine v4.3.3, Copyright (c) Zend Technologies 322 | with Xdebug v3.3.1, Copyright (c) 2002-2023, by Derick Rethans 323 | with Zend OPcache v8.3.3, Copyright (c), by Zend Technologies 324 | ``` 325 | 326 | 7. Check where your php.ini file is with `php --ini` 327 | 8. Edit php.ini, for example `sudo nano ` 328 | 9. Make sure these are on the first lines: 329 | 330 | ``` 331 | xdebug.mode=develop 332 | xdebug.client_port=9003 333 | xdebug.client_host=127.0.0.1 334 | xdebug.remote_handler=dbgp 335 | xdebug.start_with_request=yes 336 | xdebug.discover_client_host=0 337 | xdebug.show_error_trace = 1 338 | xdebug.max_nesting_level=250 339 | xdebug.var_display_max_depth=10 340 | xdebug.log=/var/log/xdebug.log 341 | ``` 342 | 343 | 10. Save and close with ctrl + O and ctrl + X 344 | 11. Make sure the log exists `sudo touch /var/log/xdebug.log && sudo chmod 777 /var/log/xdebug.log` 345 | 12. Restart services (requires [Linux-style aliases](#use-linux-style-aliases)) `sudo service php@8.3 restart && sudo service nginx restart` 346 | 13. Install [PHP Debug VSCode plugin](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug) 347 | 14. Add following to launch.json (cmd + + shift + P, "Open launch.json"): 348 | 349 | ``` json 350 | { 351 | "version": "0.2.0", 352 | "configurations": [ 353 | { 354 | //"debugServer": 4711, // Uncomment for debugging the adapter 355 | "name": "Listen for Xdebug", 356 | "type": "php", 357 | "request": "launch", 358 | "port": 9003, 359 | "log": true 360 | }, 361 | { 362 | //"debugServer": 4711, // Uncomment for debugging the adapter 363 | "name": "Launch", 364 | "request": "launch", 365 | "type": "php", 366 | "program": "${file}", 367 | "cwd": "${workspaceRoot}", 368 | "externalConsole": false 369 | } 370 | ] 371 | } 372 | ``` 373 | 15. Xdebug should now work on your editor 374 | 16. PHPCS doesn't need xdebug but will warn about it not working... this causes error in [phpcs-vscode](https://marketplace.visualstudio.com/items?itemName=ikappas.phpcs) because it depends on outputted phpcs json that is not valid with the warning _"Xdebug: [Step Debug] Could not connect to debugging client. Tried: 127.0.0.1:9003 (through xdebug.client_host/xdebug.client_port) :-(_". This can be easily fixed by installing a bash "wrapper": 375 | 17. Rename current phpcs with `sudo mv /usr/local/bin/phpcs /usr/local/bin/phpcs.bak` 376 | 18. Install new with `sudo nano /usr/local/bin/phpcs`: 377 | 378 | ``` bash 379 | #!/bin/bash 380 | XDEBUG_MODE=off /Users/rolle/Projects/phpcs/bin/phpcs "$@" 381 | ``` 382 | 383 | 19. Add permissions `sudo chmod +x /usr/local/bin/phpcs` 384 | 20. Make sure VSCode settings.json has this setting: 385 | 386 | ``` json 387 | "phpcs.executablePath": "/usr/local/bin/phpcs", 388 | ``` 389 | 390 | ### Redis 391 | 392 | Redis is an open source, in-memory data structure store, used as a database, cache. We are going to install Redis and php-redis. 393 | 394 | Before installation, make sure you do not use PHP provided by macOS. You should be using PHP installed by homebrew. If you are having problems with testing php-redis after installation, it is most probably caused bacuse of using wrong PHP. See [Troubleshooting: Testing which version of PHP you run](#testing-which-version-of-php-you-run) for more information. 395 | 396 | 1. Check that `pecl` command works 397 | 2. Run `brew update` first 398 | 3. Install Redis, `brew install redis` 399 | 4. Start Redis `brew services start redis`, this will also make sure that Redis is always started on reboot 400 | 5. Test if Redis server is running `redis-cli ping`, expected response is `PONG` 401 | 6. Install PHP Redis extension `pecl install redis`. 402 | 7. Restart nginx and php-redis should be available, you can test it with `php -r "if (new Redis() == true){ echo \"\r\n OK \r\n\"; }"` command, expected response is `OK` 403 | 404 | ### Troubleshooting 405 | 406 | #### PHP Warning: PHP Startup: Unable to load dynamic library 'redis.so 407 | 408 | If you get something like this: 409 | 410 | ```shell 411 | PHP Warning: PHP Startup: Unable to load dynamic library 'redis.so' (tried: /opt/homebrew/lib/php/pecl/20190902/redis.so (dlopen(/opt/homebrew/lib/php/pecl/20190902/redis.so, 0x0009): tried: '/opt/homebrew/lib/php/pecl/20190902/redis.so' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/lib/php/pecl/20190902/redis.so' (no such file), '/opt/homebrew/lib/php/pecl/20190902/redis.so' (no such file)), /opt/homebrew/lib/php/pecl/20190902/redis.so.so (dlopen(/opt/homebrew/lib/php/pecl/20190902/redis.so.so, 0x0009): tried: '/opt/homebrew/lib/php/pecl/20190902/redis.so.so' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/lib/php/pecl/20190902/redis.so.so' (no such file), '/opt/homebrew/lib/php/pecl/20190902/redis.so.so' (no such file))) in Unknown on line 0 412 | ``` 413 | 414 | Install [phpredis](https://github.com/phpredis/phpredis) from source: 415 | 416 | ``` 417 | git clone https://www.github.com/phpredis/phpredis.git 418 | cd phpredis 419 | phpize && ./configure && make && sudo make install 420 | ``` 421 | 422 | Then take copy the outputted library path, it will be something like this: `/opt/homebrew/Cellar/php@8.3/8.3.3/pecl/20190902/`. 423 | 424 | Run `php --ini` and modify your php.ini with `nano -w /path/to/php.ini`. 425 | 426 | Replace `extension="redis.so"` with `extension="/opt/homebrew/Cellar/php@8.3/8.3.3/pecl/20190902/redis.so"` where the path is the one you copied. Restart nginx just in case. After this phpredis should work. 427 | 428 | #### Testing which version of PHP you run 429 | 430 | Test with `php --version` what version of PHP you are using, if the command returns something like `PHP is included in macOS for compatibility with legacy software` and especially when `which php` is showing /usr/bin/php then you are using macOS built-in version (which will be removed in the future anyway) and things most probably won't work as expected. 431 | 432 | To fix this, first find the PHP: 433 | 434 | ```bash 435 | sudo find / -name 'php' 436 | ``` 437 | 438 | Look for the bin under Cellar or homebrew dirs. Then run command with your php bin, for example: `sudo ln -s /usr/local/Cellar/php@8.3/8.3.3/bin/php /usr/local/bin/php`. This symlinks the homebrew version to be used instead of macOS version OR use bashrc export as defined [here in step 4](https://github.com/digitoimistodude/macos-lemp-setup#installation). 439 | 440 | #### PHP or mysql not working at all 441 | 442 | If you have something like this in your /var/log/nginx/error.log: 443 | 444 | ``` 445 | 2019/08/12 14:09:04 [crit] 639#0: *129 open() "/usr/local/var/run/nginx/client_body_temp/0000000005" failed (13: Permission denied), client: 127.0.0.1, server: project.test, request: "POST /wp/wp-admin/async-upload.php HTTP/1.1", host: "project.test", referrer: "http://project.test/wp/wp-admin/upload.php" 446 | ``` 447 | 448 | If you cannot login to mysql from other than localhost, please answer with n to the question Disallow root login remotely? [Y/n] when running sudo mysql_secure_installation. 449 | 450 | **Make sure you run nginx and php-fpm on your root user and mariadb on your regular user**. This is important. Stop nginx from running on your default user by `brew services stop nginx` and run it with sudo `sudo brew services start nginx`. 451 | 452 | sudo brew services list should look like this: 453 | 454 | ``` shell 455 | ~ sudo brew services list 456 | Name Status User Plist 457 | dnsmasq started root /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist 458 | mariadb started rolle /Users/rolle/Library/LaunchAgents/homebrew.mxcl.mariadb.plist 459 | nginx started root /Library/LaunchDaemons/homebrew.mxcl.nginx.plist 460 | php started root /Library/LaunchDaemons/homebrew.mxcl.php@8.3.plist 461 | ``` 462 | 463 | You may have "unknown" or "error" as status or different PHP version, that is not a problem if ther server runs. **User** should be like in the list above. Then everything should work. 464 | 465 | #### MySQL/MariaDb issues 466 | 467 | If you get problems like: 468 | 469 | ``` 470 | ERROR 2002 (HY000): Can't connect to MySQL server on '127.0.0.1' (36) 471 | ``` 472 | 473 | It seems you have messed up with your root password. Try resetting root password with by adding this to your home directory (for example /Users/rolle/mysql-init): 474 | 475 | Try resetting root password with (add new password in place of _YOUR_NEW_PASSWORD_): 476 | 477 | ``` 478 | ALTER USER 'root'@'localhost' IDENTIFIED BY 'YOUR_NEW_PASSWORD'; 479 | ``` 480 | 481 | Then kill all mysql processes: 482 | 483 | 484 | ``` 485 | sudo ps xa |grep mysql 486 | kill -9 487 | ``` 488 | 489 | 490 | Then run: 491 | 492 | ``` 493 | mysqld --init-file=/Users/rolle/mysql-init & 494 | ``` 495 | 496 | After this: 497 | 498 | ``` 499 | sudo mysql_secure_installation 500 | ``` 501 | 502 | Answer: 503 | 504 | ``` 505 | NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB 506 | SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY! 507 | 508 | In order to log into MariaDB to secure it, we'll need the current 509 | password for the root user. If you've just installed MariaDB, and 510 | haven't set the root password yet, you should just press enter here. 511 | 512 | Enter current password for root (enter for none): 513 | OK, successfully used password, moving on... 514 | 515 | Setting the root password or using the unix_socket ensures that nobody 516 | can log into the MariaDB root user without the proper authorisation. 517 | 518 | You already have your root account protected, so you can safely answer 'n'. 519 | 520 | Switch to unix_socket authentication [Y/n] n 521 | ... skipping. 522 | 523 | You already have your root account protected, so you can safely answer 'n'. 524 | 525 | Change the root password? [Y/n] n 526 | ... skipping. 527 | 528 | By default, a MariaDB installation has an anonymous user, allowing anyone 529 | to log into MariaDB without having to have a user account created for 530 | them. This is intended only for testing, and to make the installation 531 | go a bit smoother. You should remove them before moving into a 532 | production environment. 533 | 534 | Remove anonymous users? [Y/n] y 535 | ... Success! 536 | 537 | Normally, root should only be allowed to connect from 'localhost'. This 538 | ensures that someone cannot guess at the root password from the network. 539 | 540 | Disallow root login remotely? [Y/n] n 541 | ... skipping. 542 | 543 | By default, MariaDB comes with a database named 'test' that anyone can 544 | access. This is also intended only for testing, and should be removed 545 | before moving into a production environment. 546 | 547 | Remove test database and access to it? [Y/n] y 548 | - Dropping test database... 549 | ... Success! 550 | - Removing privileges on test database... 551 | ... Success! 552 | 553 | Reloading the privilege tables will ensure that all changes made so far 554 | will take effect immediately. 555 | 556 | Reload privilege tables now? [Y/n] y 557 | ... Success! 558 | 559 | Cleaning up... 560 | 561 | All done! If you've completed all of the above steps, your MariaDB 562 | installation should now be secure. 563 | 564 | Thanks for using MariaDB! 565 | ``` 566 | 567 | If you are still having problems connecting with WordPress and prompting `Access denied for user 'root'@'127.0.0.1'`, try this in `mysql -u root -p`: 568 | 569 | ``` sql 570 | GRANT ALL PRIVILEGES ON *.* TO root@localhost IDENTIFIED BY 'YOUR_MYSQL_ROOT_PASSWORD' WITH GRANT OPTION; 571 | GRANT ALL PRIVILEGES ON *.* TO root@127.0.0.1 IDENTIFIED BY 'YOUR_MYSQL_ROOT_PASSWORD' WITH GRANT OPTION; 572 | ``` 573 | 574 | #### *2 open() "/var/www/test.txt" failed (13: Permission denied), client: 127.0.0.1 575 | 576 | If you are getting permission denied by nginx, you need to make sure your php-fpm and nginx are running on the same user. This is stricter on MacBook Pro M1. 577 | 578 | Open `/opt/homebrew/etc/php/8.3/php-fpm.d/www.conf` and change the user, group and listen to following: 579 | 580 | ```ini 581 | user = your_username 582 | group = staff 583 | listen = 127.0.0.1:9074 584 | ``` 585 | 586 | Open `/opt/homebrew/etc/nginx/nginx.conf` and add to first line: 587 | 588 | ```ini 589 | user your_username staff; 590 | ``` 591 | 592 | #### "Primary script unknown" error in nginx log or "File not found." in browser 593 | 594 | This is caused by php-fpm not running properly. Please [make sure the PHP runs on correct permissions](#make-sure-the-php-runs-on-correct-permissions) section. 595 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | # Crucial paths 2 | HOMEBREW_PATH='/opt/homebrew' 3 | PHP_VERSION='8.3' 4 | 5 | # Helper variables 6 | TXTBOLD=$(tput bold) 7 | BOLDGREEN=${TXTBOLD}$(tput setaf 2) 8 | YELLOW=$(tput setaf 3) 9 | GREEN=$(tput setaf 2) 10 | TXTRESET=$(tput sgr0) 11 | 12 | echo "${YELLOW}Getting dependencies...${TXTRESET}" 13 | 14 | # Attempt to install Xcode Command Line Tools 15 | xcode-select --install 16 | 17 | # Get Homebrew 18 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 19 | 20 | # Inspect Homebrew installation 21 | brew doctor 22 | 23 | # Update Homebrew 24 | brew update && brew upgrade 25 | echo "${BOLDGREEN}Dependencies installed and up to date.${TXTRESET}" 26 | 27 | # Install the latest nginx version 28 | echo "${YELLOW}Installing nginx.${TXTRESET}" 29 | brew tap homebrew/nginx 30 | brew install nginx 31 | sudo brew services start nginx 32 | curl -IL http://127.0.0.1:80 33 | echo "${BOLDGREEN}nginx installed and running.${TXTRESET}" 34 | 35 | # Set up nginx and paths 36 | echo "${YELLOW}Setting up nginx...${TXTRESET}" 37 | 38 | # Chec if /etc/nginx exists 39 | if [ -d "/etc/nginx" ]; then 40 | echo "${YELLOW}Directory /etc/nginx exists.${TXTRESET}" 41 | else 42 | echo "${YELLOW}Directory /etc/nginx does not exist. Symlinking homebrew installation to it.${TXTRESET}" 43 | sudo ln -sfnv "${HOMEBREW_PATH}/etc/nginx" "/etc/nginx" 44 | 45 | # Creating the necessary directories 46 | sudo mkdir -p "${HOMEBREW_PATH}/etc/nginx/global" 47 | sudo mkdir -p "${HOMEBREW_PATH}/etc/nginx/sites-enabled" 48 | sudo mkdir -p "${HOMEBREW_PATH}/etc/nginx/sites-available" 49 | sudo mkdir -p "${HOMEBREW_PATH}/etc/nginx/global" 50 | 51 | # Setting permissions 52 | sudo chmod -R 775 "${HOMEBREW_PATH}/etc/nginx/global" 53 | sudo chmod -R 775 "${HOMEBREW_PATH}/etc/nginx/sites-enabled" 54 | sudo chmod -R 775 "${HOMEBREW_PATH}/etc/nginx/sites-available" 55 | sudo chmod -R 775 "${HOMEBREW_PATH}/etc/nginx/global" 56 | fi 57 | 58 | echo "${YELLOW}Creating default nginx configurations...${TXTRESET}" 59 | 60 | # Create default nginx configuration 61 | sudo echo "user $(whoami) staff; 62 | 63 | worker_processes 18; 64 | 65 | events { 66 | multi_accept on; 67 | accept_mutex on; 68 | worker_connections 1024; 69 | } 70 | 71 | http { 72 | ## 73 | # Optimization 74 | ## 75 | 76 | sendfile on; 77 | sendfile_max_chunk 512k; 78 | tcp_nopush on; 79 | tcp_nodelay on; 80 | keepalive_timeout 120; 81 | keepalive_requests 100000; 82 | types_hash_max_size 2048; 83 | server_tokens off; 84 | client_body_buffer_size 128k; 85 | client_max_body_size 10m; 86 | client_header_buffer_size 1k; 87 | large_client_header_buffers 4 32k; 88 | output_buffers 1 32k; 89 | postpone_output 1460; 90 | 91 | server_names_hash_max_size 1024; 92 | #server_names_hash_bucket_size 64; 93 | # server_name_in_redirect off; 94 | 95 | include ${HOMEBREW_PATH}/etc/nginx/mime.types; 96 | default_type application/octet-stream; 97 | 98 | ## 99 | # Logging Settings 100 | ## 101 | access_log off; 102 | access_log /var/log/nginx/access.log combined; 103 | error_log /var/log/nginx/error.log; 104 | 105 | ## 106 | # Virtual Host Configs 107 | ## 108 | 109 | include ${HOMEBREW_PATH}/etc/nginx/sites-enabled/*; 110 | }" > "${HOMEBREW_PATH}/etc/nginx/nginx.conf" 111 | 112 | echo "${YELLOW}Creating locations for logs...${TXTRESET}" 113 | 114 | # Check if nginx log dir exists 115 | if [ -d "/var/log/nginx" ]; then 116 | echo "${YELLOW}Directory /var/log/nginx exists.${TXTRESET}" 117 | else 118 | echo "${YELLOW}Directory /var/log/nginx does not exist. Creating it and the access.log and error.log files.${TXTRESET}" 119 | sudo mkdir -p /var/log/nginx 120 | sudo touch /var/log/nginx/access.log 121 | sudo chmod 777 /var/log/nginx/access.log 122 | sudo touch /var/log/nginx/error.log 123 | sudo chmod 777 /var/log/nginx/error.log 124 | fi 125 | 126 | echo "${YELLOW}Creating default PHP/nginx configurations...${TXTRESET}" 127 | 128 | # Create default php configuration 129 | sudo echo "location ~ \.php\$ { 130 | proxy_intercept_errors on; 131 | try_files \$uri /index.php; 132 | fastcgi_split_path_info ^(.+\.php)(/.+)\$; 133 | include fastcgi_params; 134 | fastcgi_read_timeout 300; 135 | fastcgi_buffer_size 128k; 136 | fastcgi_buffers 8 128k; 137 | fastcgi_busy_buffers_size 128k; 138 | fastcgi_temp_file_write_size 128k; 139 | fastcgi_index index.php; 140 | fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name; 141 | fastcgi_pass 127.0.0.1:9000; 142 | }" > "${HOMEBREW_PATH}/etc/nginx/php.conf" 143 | 144 | echo "${YELLOW}Creating default WordPress-related nginx configurations...${TXTRESET}" 145 | 146 | # Create default WordPress-related nginx configuration 147 | sudo echo "# WordPress single site rules. 148 | # Designed to be included in any server {} block. 149 | # Upstream to abstract backend connection(s) for php 150 | location = /favicon.ico { 151 | log_not_found off; 152 | access_log off; 153 | } 154 | 155 | location = /robots.txt { 156 | allow all; 157 | log_not_found off; 158 | access_log off; 159 | } 160 | 161 | location / { 162 | # This is cool because no php is touched for static content. 163 | # include the "?\$args" part so non-default permalinks doesn't break when using query string 164 | try_files \$uri \$uri/ /index.php?\$args; 165 | } 166 | 167 | # Add trailing slash to */wp-admin requests. 168 | rewrite /wp-admin\$ \$scheme://\$host\$uri/ permanent; 169 | 170 | # Directives to send expires headers and turn off 404 error logging. 171 | location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)\$ { 172 | access_log off; 173 | log_not_found off; 174 | expires max; 175 | }" > "${HOMEBREW_PATH}/etc/nginx/global/wordpress.conf" 176 | 177 | echo "${YELLOW}Creating default site vhost...${TXTRESET}" 178 | 179 | # Create default site configuration 180 | sudo echo "server { 181 | listen 80 default_server; 182 | root /var/www; 183 | index index.html index.htm index.php; 184 | server_name localhost; 185 | include php.conf; 186 | include global/wordpress.conf; 187 | }" > "${HOMEBREW_PATH}/etc/nginx/sites-available/default" 188 | 189 | echo "${YELLOW}Symlinking the default vhost...${TXTRESET}" 190 | 191 | # Symlink the default site configuration 192 | sudo ln -sfnv ${HOMEBREW_PATH}/etc/nginx/sites-available/default ${HOMEBREW_PATH}/etc/nginx/sites-enabled/default 193 | 194 | echo "${YELLOW}Making sure /var/www exists and is linked to project directory...${TXTRESET}" 195 | 196 | # Check if /var/www exists 197 | if [ -d "/var/www" ]; then 198 | echo "${YELLOW}Directory /var/www exists.${TXTRESET}" 199 | else 200 | echo "${YELLOW}Directory /var/www does not exist. Symlinking $HOME/Projects to it.${TXTRESET}" 201 | sudo ln -sfnv $HOME/Projects /var/www 202 | fi 203 | 204 | echo "${YELLOW}Copying default nginx index to the project dir...${TXTRESET}" 205 | 206 | # Figure out the nginx correct installation path 207 | NGINX_VERSION=$(nginx -v 2>&1 | grep -o '[0-9.]*') 208 | NGINX_HOMEBREW_PATH=$(find $HOMEBREW_PATH -name "nginx" -type d -maxdepth 3) 209 | NGINX_HOMEBREW_VERSION_PATH=$(find $NGINX_HOMEBREW_PATH -name $NGINX_VERSION -type d -maxdepth 1) 210 | 211 | # Copy the default index.html to the nginx root 212 | sudo cp "${NGINX_HOMEBREW_VERSION_PATH}/html/index.html /var/www/" 213 | 214 | echo "${YELLOW}Setting permissions...${TXTRESET}" 215 | 216 | # Set the correct permissions 217 | sudo chown -R $(whoami):staff /var/www 218 | 219 | # Install PHP 220 | echo "${YELLOW}Installing PHP...${TXTRESET}" 221 | 222 | # This part is the same than in here: 223 | # Install and use PHP $PHP_VERSION in your local macos-lemp-setup environment 224 | # https://app.gitbook.com/o/PedExJWZmbCiZe4gDwKC/s/VVikkYgIZ9miBzwYDCYh/how-tos/install-and-use-php-8.3-in-your-local-macos-lemp-setup-environment 225 | 226 | # Add the PHP and PHP Extension taps 227 | brew tap shivammathur/php 228 | brew tap shivammathur/extensions 229 | 230 | # Install the PHP we need 231 | brew install shivammathur/php/php@${PHP_VERSION} 232 | 233 | # Pin package to avoid accidental upgrades later 234 | brew pin php@${PHP_VERSION} 235 | 236 | # Link PHP CLI executable as `php` 237 | brew link --overwrite --force shivammathur/php/php@${PHP_VERSION} 238 | 239 | # Test installation, should display PHP 8.3.x (cli) 240 | php -v 241 | 242 | # Test php-fpm 243 | lsof -Pni4 | grep LISTEN | grep php 244 | 245 | # Symlink the PHP configuration 246 | sudo ln -s $HOMEBREW_PATH/etc/php /etc/php 247 | 248 | # Add PHP path to the PATH 249 | sudo echo "export PATH=\"\$(brew --prefix php@${PHP_VERSION})/bin:\$PATH\"" >> ~/.bash_profile 250 | 251 | # Restart 252 | sudo brew services stop php@${PHP_VERSION} 253 | sudo brew services start php@${PHP_VERSION} 254 | echo "${BOLDGREEN}PHP installed and running.${TXTRESET}" 255 | 256 | # Install MariaDB 257 | echo "${YELLOW}Installing MariaDB...${TXTRESET}" 258 | brew install mariadb 259 | brew services start mariadb 260 | echo "${BOLDGREEN}MariaDB installed and running.${TXTRESET}" 261 | 262 | # Install MailHog 263 | echo "${YELLOW}Installing MailHog...${TXTRESET}" 264 | brew update && brew install mailhog 265 | echo "${BOLDGREEN}MailHog installed (run mailhog to start mail server).${TXTRESET}" 266 | 267 | # Install DNSmasq 268 | echo "${YELLOW}Installing DNSmasq...${TXTRESET}" 269 | brew install dnsmasq 270 | curl -L https://gist.githubusercontent.com/dtomasi/ab76d14338db82ec24a1fc137caff75b/raw/550c84393c4c1eef8a3e68bb720df561b5d3f175/dnsmasq.conf -o /usr/local/etc/dnsmasq.conf 271 | sudo curl -L https://gist.githubusercontent.com/dtomasi/ab76d14338db82ec24a1fc137caff75b/raw/550c84393c4c1eef8a3e68bb720df561b5d3f175/dev -o /etc/resolver/dev 272 | sudo brew services stop dnsmasq 273 | sudo brew services start dnsmasq 274 | echo "${BOLDGREEN}DNSmasq installed and configured.${TXTRESET}" 275 | 276 | # Restart all services 277 | echo "${YELLOW}Restarting services....${TXTRESET}" 278 | 279 | # These need to be running as root, because of the port 80 and other privileges. 280 | sudo brew services stop dnsmasq 281 | sudo brew services start dnsmasq 282 | sudo brew services stop nginx 283 | sudo brew services start nginx 284 | sudo brew services stop php@${PHP_VERSION} 285 | sudo brew services start php@${PHP_VERSION} 286 | brew services stop mariadb 287 | brew services start mariadb 288 | sudo brew services list 289 | 290 | # All done 291 | echo "${BOLDGREEN}You should now be able to use http://localhost. If not, test with commands sudo nginx -t and sudo php-fpm -t and fix errors. Add new vhosts to /opt/homebrew/etc/nginx/sites-available and symlink them just like you would do in production. Have fun!${TXTRESET}" 292 | --------------------------------------------------------------------------------