├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── docker-compose.yml ├── dockerfiles ├── nginx.dockerfile ├── nginx │ └── default.conf ├── php.dockerfile └── php.root.dockerfile └── src └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: aschmelyun 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | mysql 2 | .DS_Store 3 | .idea 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2021 Andrew Schmelyun 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-compose-laravel 2 | A pretty simplified Docker Compose workflow that sets up a LEMP network of containers for local Laravel development. You can view the full article that inspired this repo [here](https://dev.to/aschmelyun/the-beauty-of-docker-for-local-laravel-development-13c0). 3 | 4 | ## Usage 5 | 6 | To get started, make sure you have [Docker installed](https://docs.docker.com/docker-for-mac/install/) on your system, and then clone this repository. 7 | 8 | Next, navigate in your terminal to the directory you cloned this, and spin up the containers for the web server by running `docker-compose up -d --build app`. 9 | 10 | After that completes, follow the steps from the [src/README.md](src/README.md) file to get your Laravel project added in (or create a new blank one). 11 | 12 | **Note**: Your MySQL database host name should be `mysql`, **not** `localhost`. The username and database should both be `homestead` with a password of `secret`. 13 | 14 | Bringing up the Docker Compose network with `app` instead of just using `up`, ensures that only our site's containers are brought up at the start, instead of all of the command containers as well. The following are built for our web server, with their exposed ports detailed: 15 | 16 | - **nginx** - `:80` 17 | - **mysql** - `:3306` 18 | - **php** - `:9000` 19 | - **redis** - `:6379` 20 | - **mailhog** - `:8025` 21 | 22 | Three additional containers are included that handle Composer, NPM, and Artisan commands *without* having to have these platforms installed on your local computer. Use the following command examples from your project root, modifying them to fit your particular use case. 23 | 24 | - `docker-compose run --rm composer update` 25 | - `docker-compose run --rm npm run dev` 26 | - `docker-compose run --rm artisan migrate` 27 | 28 | ## Permissions Issues 29 | 30 | If you encounter any issues with filesystem permissions while visiting your application or running a container command, try completing one of the sets of steps below. 31 | 32 | **If you are using your server or local environment as the root user:** 33 | 34 | - Bring any container(s) down with `docker-compose down` 35 | - Replace any instance of `php.dockerfile` in the docker-compose.yml file with `php.root.dockerfile` 36 | - Re-build the containers by running `docker-compose build --no-cache` 37 | 38 | **If you are using your server or local environment as a user that is not root:** 39 | 40 | - Bring any container(s) down with `docker-compose down` 41 | - In your terminal, run `export UID=$(id -u)` and then `export GID=$(id -g)` 42 | - If you see any errors about readonly variables from the above step, you can ignore them and continue 43 | - Re-build the containers by running `docker-compose build --no-cache` 44 | 45 | Then, either bring back up your container network or re-run the command you were trying before, and see if that fixes it. 46 | 47 | ## Persistent MySQL Storage 48 | 49 | By default, whenever you bring down the Docker network, your MySQL data will be removed after the containers are destroyed. If you would like to have persistent data that remains after bringing containers down and back up, do the following: 50 | 51 | 1. Create a `mysql` folder in the project root, alongside the `nginx` and `src` folders. 52 | 2. Under the mysql service in your `docker-compose.yml` file, add the following lines: 53 | 54 | ``` 55 | volumes: 56 | - ./mysql:/var/lib/mysql 57 | ``` 58 | 59 | ## Usage in Production 60 | 61 | While I originally created this template for local development, it's robust enough to be used in basic Laravel application deployments. The biggest recommendation would be to ensure that HTTPS is enabled by making additions to the `nginx/default.conf` file and utilizing something like [Let's Encrypt](https://hub.docker.com/r/linuxserver/letsencrypt) to produce an SSL certificate. 62 | 63 | ## Compiling Assets 64 | 65 | This configuration should be able to compile assets with both [laravel mix](https://laravel-mix.com/) and [vite](https://vitejs.dev/). In order to get started, you first need to add ` --host 0.0.0.0` after the end of your relevant dev command in `package.json`. So for example, with a Laravel project using Vite, you should see: 66 | 67 | ```json 68 | "scripts": { 69 | "dev": "vite --host 0.0.0.0", 70 | "build": "vite build" 71 | }, 72 | ``` 73 | 74 | Then, run the following commands to install your dependencies and start the dev server: 75 | 76 | - `docker-compose run --rm npm install` 77 | - `docker-compose run --rm --service-ports npm run dev` 78 | 79 | After that, you should be able to use `@vite` directives to enable hot-module reloading on your local Laravel application. 80 | 81 | Want to build for production? Simply run `docker-compose run --rm npm run build`. 82 | 83 | ## MailHog 84 | 85 | The current version of Laravel (9 as of today) uses MailHog as the default application for testing email sending and general SMTP work during local development. Using the provided Docker Hub image, getting an instance set up and ready is simple and straight-forward. The service is included in the `docker-compose.yml` file, and spins up alongside the webserver and database services. 86 | 87 | To see the dashboard and view any emails coming through the system, visit [localhost:8025](http://localhost:8025) after running `docker-compose up -d site`. 88 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | networks: 4 | laravel: 5 | 6 | 7 | services: 8 | app: 9 | build: 10 | context: ./dockerfiles 11 | dockerfile: nginx.dockerfile 12 | args: 13 | - UID=${UID:-1000} 14 | - GID=${GID:-1000} 15 | ports: 16 | - "80:80" 17 | volumes: 18 | - ./src:/var/www/html:delegated 19 | depends_on: 20 | - php 21 | - redis 22 | - mysql 23 | - mailhog 24 | networks: 25 | - laravel 26 | 27 | mysql: 28 | image: mariadb:10.6 29 | restart: unless-stopped 30 | tty: true 31 | ports: 32 | - "3306:3306" 33 | environment: 34 | MYSQL_DATABASE: homestead 35 | MYSQL_USER: homestead 36 | MYSQL_PASSWORD: secret 37 | MYSQL_ROOT_PASSWORD: secret 38 | SERVICE_TAGS: dev 39 | SERVICE_NAME: mysql 40 | networks: 41 | - laravel 42 | 43 | php: 44 | build: 45 | context: ./dockerfiles 46 | dockerfile: php.dockerfile 47 | args: 48 | - UID=${UID:-1000} 49 | - GID=${GID:-1000} 50 | ports: 51 | - "9000:9000" 52 | volumes: 53 | - ./src:/var/www/html:delegated 54 | networks: 55 | - laravel 56 | 57 | redis: 58 | image: redis:alpine 59 | restart: unless-stopped 60 | ports: 61 | - "6379:6379" 62 | networks: 63 | - laravel 64 | 65 | composer: 66 | build: 67 | context: ./dockerfiles 68 | dockerfile: php.dockerfile 69 | args: 70 | - UID=${UID:-1000} 71 | - GID=${GID:-1000} 72 | volumes: 73 | - ./src:/var/www/html 74 | depends_on: 75 | - php 76 | entrypoint: [ 'composer', '--ignore-platform-reqs' ] 77 | networks: 78 | - laravel 79 | 80 | npm: 81 | image: node:current-alpine 82 | volumes: 83 | - ./src:/var/www/html 84 | ports: 85 | - "3000:3000" 86 | - "3001:3001" 87 | - "5173:5173" 88 | working_dir: /var/www/html 89 | entrypoint: [ 'npm' ] 90 | networks: 91 | - laravel 92 | 93 | artisan: 94 | build: 95 | context: ./dockerfiles 96 | dockerfile: php.dockerfile 97 | args: 98 | - UID=${UID:-1000} 99 | - GID=${GID:-1000} 100 | volumes: 101 | - ./src:/var/www/html:delegated 102 | depends_on: 103 | - mysql 104 | entrypoint: [ 'php', '/var/www/html/artisan' ] 105 | networks: 106 | - laravel 107 | 108 | mailhog: 109 | image: mailhog/mailhog:latest 110 | ports: 111 | - "1025:1025" 112 | - "8025:8025" 113 | networks: 114 | - laravel 115 | -------------------------------------------------------------------------------- /dockerfiles/nginx.dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:stable-alpine 2 | 3 | ARG UID 4 | ARG GID 5 | 6 | ENV UID=${UID} 7 | ENV GID=${GID} 8 | 9 | # MacOS staff group's gid is 20, so is the dialout group in alpine linux. We're not using it, let's just remove it. 10 | RUN delgroup dialout 11 | 12 | RUN addgroup -g ${GID} --system laravel 13 | RUN adduser -G laravel --system -D -s /bin/sh -u ${UID} laravel 14 | RUN sed -i "s/user nginx/user laravel/g" /etc/nginx/nginx.conf 15 | 16 | ADD ./nginx/default.conf /etc/nginx/conf.d/ 17 | 18 | RUN mkdir -p /var/www/html -------------------------------------------------------------------------------- /dockerfiles/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | index index.php index.html; 4 | server_name _; 5 | root /var/www/html/public; 6 | 7 | location / { 8 | try_files $uri $uri/ /index.php?$query_string; 9 | } 10 | 11 | location ~ \.php$ { 12 | try_files $uri =404; 13 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 14 | fastcgi_pass php:9000; 15 | fastcgi_index index.php; 16 | include fastcgi_params; 17 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 18 | fastcgi_param PATH_INFO $fastcgi_path_info; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dockerfiles/php.dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8-fpm-alpine 2 | 3 | ARG UID 4 | ARG GID 5 | 6 | ENV UID=${UID} 7 | ENV GID=${GID} 8 | 9 | RUN mkdir -p /var/www/html 10 | 11 | WORKDIR /var/www/html 12 | 13 | COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer 14 | 15 | # MacOS staff group's gid is 20, so is the dialout group in alpine linux. We're not using it, let's just remove it. 16 | RUN delgroup dialout 17 | 18 | RUN addgroup -g ${GID} --system laravel 19 | RUN adduser -G laravel --system -D -s /bin/sh -u ${UID} laravel 20 | 21 | RUN sed -i "s/user = www-data/user = laravel/g" /usr/local/etc/php-fpm.d/www.conf 22 | RUN sed -i "s/group = www-data/group = laravel/g" /usr/local/etc/php-fpm.d/www.conf 23 | RUN echo "php_admin_flag[log_errors] = on" >> /usr/local/etc/php-fpm.d/www.conf 24 | 25 | RUN docker-php-ext-install pdo pdo_mysql 26 | 27 | RUN mkdir -p /usr/src/php/ext/redis \ 28 | && curl -L https://github.com/phpredis/phpredis/archive/5.3.4.tar.gz | tar xvz -C /usr/src/php/ext/redis --strip 1 \ 29 | && echo 'redis' >> /usr/src/php-available-exts \ 30 | && docker-php-ext-install redis 31 | 32 | USER laravel 33 | 34 | CMD ["php-fpm", "-y", "/usr/local/etc/php-fpm.conf", "-R"] 35 | -------------------------------------------------------------------------------- /dockerfiles/php.root.dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8-fpm-alpine 2 | 3 | RUN mkdir -p /var/www/html 4 | 5 | WORKDIR /var/www/html 6 | 7 | COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer 8 | 9 | RUN sed -i "s/user = www-data/user = root/g" /usr/local/etc/php-fpm.d/www.conf 10 | RUN sed -i "s/group = www-data/group = root/g" /usr/local/etc/php-fpm.d/www.conf 11 | RUN echo "php_admin_flag[log_errors] = on" >> /usr/local/etc/php-fpm.d/www.conf 12 | 13 | RUN docker-php-ext-install pdo pdo_mysql 14 | 15 | RUN mkdir -p /usr/src/php/ext/redis \ 16 | && curl -L https://github.com/phpredis/phpredis/archive/5.3.4.tar.gz | tar xvz -C /usr/src/php/ext/redis --strip 1 \ 17 | && echo 'redis' >> /usr/src/php-available-exts \ 18 | && docker-php-ext-install redis 19 | 20 | USER root 21 | 22 | CMD ["php-fpm", "-y", "/usr/local/etc/php-fpm.conf", "-R"] 23 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | ## This is where your Laravel app goes 2 | 3 | To get started, **delete this file** and then do one of the following: 4 | 5 | - Clone your project or copy all of the files directly into this `src` directory. 6 | - Spin up the Docker network by following the instructions on the main [README.md](../README.md), and install a brand new Laravel project by running `docker-compose run --rm composer create-project laravel/laravel .` in your terminal. 7 | --------------------------------------------------------------------------------