├── .gitignore ├── images ├── nginx-proxy │ ├── certs │ │ └── .gitignore │ └── vhost.d │ │ ├── .gitignore │ │ └── proxy.conf ├── php-fpm │ ├── php.ini │ └── Dockerfile └── nginx │ └── vhost.conf ├── .env ├── LICENSE ├── docker-compose.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /images/nginx-proxy/certs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /images/nginx-proxy/vhost.d/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /images/nginx-proxy/vhost.d/proxy.conf: -------------------------------------------------------------------------------- 1 | client_max_body_size 50m; 2 | proxy_read_timeout 1800; 3 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VIRTUAL_HOST=website.localhost 2 | LETSENCRYPT_HOST= 3 | LETSENCRYPT_EMAIL= 4 | 5 | MYSQL_ROOT_PASSWORD=123456 6 | MYSQL_USER=user 7 | MYSQL_PASSWORD=password 8 | MYSQL_DATABASE=database 9 | -------------------------------------------------------------------------------- /images/php-fpm/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | short_open_tag = off 3 | 4 | [Date] 5 | date.timezone = "Europe/Amsterdam" 6 | 7 | [Zend] 8 | xdebug.remote_enable = 1 9 | xdebug.remote_host = "host.docker.internal" 10 | xdebug.remote_port = 9000 11 | -------------------------------------------------------------------------------- /images/php-fpm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.3-fpm 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y mariadb-client git zip unzip && \ 5 | pecl install xdebug && \ 6 | docker-php-ext-install pdo_mysql && \ 7 | docker-php-ext-enable xdebug 8 | 9 | RUN php -r "copy('https://raw.githubusercontent.com/composer/getcomposer.org/55e3ac0516cf01802649468315cd863bcd46a73f/web/installer', 'composer-setup.php');" 10 | RUN php composer-setup.php --install-dir=/usr/bin --filename=composer --version=1.10.17 11 | RUN php -r "unlink('composer-setup.php');" 12 | 13 | CMD ["php-fpm"] 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Martijn van Beek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | nginx-proxy: 5 | image: jwilder/nginx-proxy 6 | ports: 7 | - 80:80 8 | volumes: 9 | - ./images/nginx-proxy/certs:/etc/nginx/certs:ro 10 | - ./images/nginx-proxy/vhost.d:/etc/nginx/vhost.d:ro 11 | - /var/run/docker.sock:/tmp/docker.sock:ro 12 | 13 | php-fpm: 14 | build: ./images/php-fpm 15 | volumes: 16 | - ./images/php-fpm/php.ini:/usr/local/etc/php/php.ini 17 | - ../:/var/www/html:delegated 18 | environment: 19 | DB_CONNECTION: mysql 20 | DB_HOST: db 21 | DB_PORT: 3306 22 | DB_DATABASE: $MYSQL_DATABASE 23 | DB_USERNAME: $MYSQL_USER 24 | DB_PASSWORD: $MYSQL_PASSWORD 25 | 26 | nginx-app: 27 | image: nginx 28 | links: 29 | - php-fpm 30 | volumes: 31 | - ./images/nginx/vhost.conf:/etc/nginx/conf.d/default.conf 32 | - ../:/var/www/html:delegated 33 | environment: 34 | VIRTUAL_HOST: $VIRTUAL_HOST 35 | LETSENCRYPT_HOST: $LETSENCRYPT_HOST 36 | LETSENCRYPT_EMAIL: $LETSENCRYPT_EMAIL 37 | 38 | db: 39 | image: mysql:8.0 40 | ports: 41 | # We need to expose this port to allow to connect to the database via DBMS 42 | - 3306:3306 43 | # see https://github.com/laravel/framework/issues/23961#issuecomment-385446928 44 | command: --default-authentication-plugin=mysql_native_password 45 | environment: 46 | MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD 47 | MYSQL_USER: $MYSQL_USER 48 | MYSQL_PASSWORD: $MYSQL_PASSWORD 49 | MYSQL_DATABASE: $MYSQL_DATABASE 50 | volumes: 51 | - ./data/mysql/:/docker-entrypoint-initdb.d 52 | - data_volume:/var/lib/mysql 53 | 54 | composer: 55 | build: ./images/php-fpm 56 | command: composer install --optimize-autoloader --no-interaction 57 | working_dir: /var/www/html 58 | volumes: 59 | - ../:/var/www/html:delegated 60 | 61 | volumes: 62 | data_volume: 63 | -------------------------------------------------------------------------------- /images/nginx/vhost.conf: -------------------------------------------------------------------------------- 1 | access_log /var/log/nginx/access.log; 2 | error_log /var/log/nginx/error.log; 3 | 4 | server { 5 | listen 0.0.0.0:80; 6 | server_name localhost; 7 | root /var/www/html/public; 8 | 9 | index index.html index.htm index.php; 10 | 11 | charset utf-8; 12 | 13 | location / { 14 | try_files $uri $uri/ /index.php?$query_string; 15 | } 16 | 17 | location = /favicon.ico { access_log off; log_not_found off; } 18 | location = /robots.txt { access_log off; log_not_found off; } 19 | 20 | error_page 404 /index.php; 21 | 22 | sendfile off; 23 | 24 | location ~ \.php$ { 25 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 26 | fastcgi_param HTTPS off; 27 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 28 | include /etc/nginx/fastcgi_params; 29 | 30 | fastcgi_index index.php; 31 | send_timeout 1800; 32 | fastcgi_read_timeout 1800; 33 | fastcgi_pass php-fpm:9000; 34 | } 35 | 36 | location ~ /\.ht { 37 | deny all; 38 | } 39 | 40 | location /php/fpm/status { 41 | fastcgi_pass php-fpm:9000; 42 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 43 | include /etc/nginx/fastcgi_params; 44 | } 45 | 46 | location /php/fpm/ping { 47 | fastcgi_pass php-fpm:9000; 48 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 49 | include /etc/nginx/fastcgi_params; 50 | } 51 | 52 | # GZIP 53 | # Enable gzip compression. 54 | gzip on; 55 | 56 | # Compression level (1-9). 57 | # 5 is a perfect compromise between size and CPU usage, offering about 58 | # 75% reduction for most ASCII files (almost identical to level 9). 59 | gzip_comp_level 5; 60 | 61 | # Don't compress anything that's already small and unlikely to shrink much 62 | # if at all (the default is 20 bytes, which is bad as that usually leads to 63 | # larger files after gzipping). 64 | gzip_min_length 256; 65 | 66 | # Compress data even for clients that are connecting to us via proxies, 67 | # identified by the "Via" header (required for CloudFront). 68 | gzip_proxied any; 69 | 70 | # Tell proxies to cache both the gzipped and regular version of a resource 71 | # whenever the client's Accept-Encoding capabilities header varies; 72 | # Avoids the issue where a non-gzip capable client (which is extremely rare 73 | # today) would display gibberish if their proxy gave them the gzipped version. 74 | gzip_vary on; 75 | 76 | # Compress all output labeled with one of the following MIME-types. 77 | gzip_types 78 | application/atom+xml 79 | application/javascript 80 | application/json 81 | application/ld+json 82 | application/manifest+json 83 | application/rss+xml 84 | application/vnd.geo+json 85 | application/vnd.ms-fontobject 86 | application/x-font-ttf 87 | application/x-web-app-manifest+json 88 | application/xhtml+xml 89 | application/xml 90 | font/opentype 91 | image/bmp 92 | image/svg+xml 93 | image/x-icon 94 | text/cache-manifest 95 | text/css 96 | text/plain 97 | text/vcard 98 | text/vnd.rim.location.xloc 99 | text/vtt 100 | text/x-component 101 | text/x-cross-domain-policy; 102 | # text/html is always compressed by gzip module 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A docker-compose configuration to set up a ready-to-use Laravel environment. 2 | - PHP 7.3, Xdebug, Composer 3 | - MySQL 8.0 4 | - Nginx 5 | 6 | ### Usage 7 | - Clone the repository inside your project root: `git clone .docker` 8 | - Remove from your .env file the following variables: 9 | - DB_CONNECTION 10 | - DB_HOST 11 | - DB_PORT 12 | - DB_DATABASE 13 | - DB_USERNAME 14 | - DB_PASSWORD 15 | - Optional: Update the VIRTUAL_HOST value in `.docker/.env` 16 | - From within the .docker folder, run `docker-compose -p $(basename $(dirname $(pwd))) up -d` 17 | 18 | You should be able to view your Laravel project at http://website.localhost or 19 | at http://$VIRTUAL_HOST 20 | 21 | ##### Note about docker-compose -p 22 | To avoid volume collision always remember to pass in a unique project name when 23 | running docker-compose. I am currently using the parent folder name as project 24 | name. Given this folder structure: `.../my-project/.docker`, running 25 | `docker-compose -p $(basename $(dirname $(pwd)))` from within the `.docker` 26 | folder will use `my-project` as project name. 27 | 28 | Add this function to your bash profile or .zshenv to be able to use `dcp` as a 29 | shortcut: 30 | ```bash 31 | function dcp() { 32 | docker-compose -p $(basename $(dirname $(pwd))) $@ 33 | } 34 | ``` 35 | This will set the --project-name argument for you. 36 | 37 | ### 1. Reverse proxy and running multiple applications 38 | As of right now it's not possible to run multiple Laravel applications at the 39 | same time. This is because both the nginx-proxy and database services publish 40 | hard coded ports. I don't currently see this as a deal-breaker and could be 41 | fixed by running nginx-proxy either externally or with a different port and by 42 | setting a custom port for the database service on a per-project basis. 43 | 44 | ### 2. HTTPS 45 | To enable HTTPS you simply need to: 46 | - Generate a .key and .crt file into the .docker/images/nginx-proxy/certs folder 47 | by running the following command from the docker folder. The variable 48 | VIRTUAL_HOST must be the same to the one you set in the .docker/.env file. 49 | ``` 50 | VIRTUAL_HOST=website.localhost bash -c 'openssl req -new -x509 -nodes -sha256 -days 365 -newkey rsa:2048 -keyout images/nginx-proxy/certs/$VIRTUAL_HOST.key -out images/nginx-proxy/certs/$VIRTUAL_HOST.crt -subj "/C=US/ST=Oregon/L=Portland/O=Localhost/OU=Development/CN=$VIRTUAL_HOST"' 51 | ``` 52 | - Update the nginx-proxy service to expose port 443: 53 | ```yml 54 | 3 services: 55 | 4 nginx-proxy: 56 | ... 57 | 6 ports: 58 | 7 - 80:80 59 | + 8 - 443:443 60 | ``` 61 | - If present, update your Trusted Proxy configuration to trust the caller IP 62 | ```php 63 | // app/Http/Middleware/TrustProxies.php 64 | - 15 protected $proxies; 65 | + 15 protected $proxies = '*'; 66 | ``` 67 | Your browser will complain about your self-signed SSL certificate. 68 | 69 | ### 3. Xdebug 70 | Xdebug is installed and enabled by default and uses port 9000. You just need to 71 | configure your IDE. 72 | 73 | For PHPStorm, all you need to do is: 74 | - Install the Browser extension: https://www.jetbrains.com/help/phpstorm/browser-debugging-extensions.html 75 | - Start listening, from the Menu: Run > Start Listening for PHP Debug Connections 76 | 77 | ### 4. Node (NPM, Yarn) 78 | You'll probably need npm/yarn, I'm currently using the "serafinomb/node" 79 | docker image. Usage is as follows: 80 | - `docker run -it --rm -v $(PWD):/ws:delegated -w /ws serafinomb/node node -v` 81 | - `docker run -it --rm -v $(PWD):/ws:delegated -w /ws serafinomb/node npm -v` 82 | - `docker run -it --rm -v $(PWD):/ws:delegated -w /ws serafinomb/node yarn -v` 83 | 84 | You can add an alias or function to your bash profile/.zshenv for each one of 85 | them. For example: 86 | ```bash 87 | function node() { 88 | docker run -it --rm -e "TERM=xterm-256color" -v $(PWD):/ws:delegated -w /ws serafinomb/node node $@ 89 | } 90 | 91 | function npm() { 92 | node npm $@ 93 | } 94 | 95 | function npx() { 96 | node npx $@ 97 | } 98 | 99 | function yarn() { 100 | node yarn $@ 101 | } 102 | ``` 103 | 104 | And if you need to run "npm run start" consider using the following command which should have better performances: 105 | ```bash 106 | docker run -it --rm -v $PWD:/ws:delegated,ro -v $PWD/node_modules -w /ws -p 3000:3000 -e CHOKIDAR_USEPOLLING=true -e CHOKIDAR_INTERVAL=250 serafinomb/node npm run start 107 | 108 | ``` 109 | 110 | ### 5. Database tunneling 111 | If you are working with a remote database, example Amazon RDS, and you need to 112 | establish a tunnel connection to connect to it: 113 | 114 | - Copy your PEM certificate into `/app/storage` 115 | - Add the following line to your `.gitignore` to make sure not to commit your 116 | PEM file by mistake: `/storage/*.pem` 117 | - Edit the `docker-compose.yml` to expose the port 33060 from the php-fpm service: 118 | ```yml 119 | 3 services: 120 | ... 121 | 12 php-fpm: 122 | 13 build: ./images/php-fpm 123 | + 14 ports: 124 | + 15 - 33060 125 | ``` 126 | - Edit the php-fpm Dockerfile in `.docker/images/php-fpm/Dockerfile` to install 127 | `openssh-client`: 128 | ``` 129 | 3 RUN apt-get update && \ 130 | - 4 apt-get install -y mysql-client && \ 131 | + 4 apt-get install -y mysql-client openssh-client && \ 132 | ``` 133 | - (Optional) Remove the entire "db" section from the docker-compose.yml file 134 | - Update your `docker-compose.yml` php-fpm service environments to replace DB_HOST: 135 | ``` 136 | - 19 DB_HOST: db 137 | + 19 DB_HOST: 127.0.0.1 138 | ``` 139 | - Restart the containers with `dcp up -d --force-recreate` 140 | - Establish the SSH tunnel with `dcp exec php-fpm ssh -i storage/.pem -4 -o ServerAliveInterval=30 -f @ -L 33060::3306 -N` 141 | --- 142 | 143 | The following docker-compose configuration has been used as a starting point. 144 | 145 | --- 146 | 147 | ## Troubleshooting 148 | 149 | While creating a new environment I encountered the following error while connecting to MySQL through Sequel Pro: 150 | ``` 151 | MySQL said: Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(/usr/local/lib/plugin/caching_sha2_password.so, 2): image not found 152 | ``` 153 | 154 | I solved this by logging in into the mysql docker-compose container and running: 155 | 1. docker exec -it _db_1 bash 156 | 2. mysql -u root -p 123456 157 | 3. `ALTER USER 'user' IDENTIFIED WITH mysql_native_password BY 'password';` 158 | 159 | From 160 | --------------------------------------------------------------------------------