├── .docker ├── mysql │ └── my.cnf ├── nginx │ └── conf.d │ │ └── php.conf └── php │ └── Dockerfile ├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── docker-compose.yml └── src └── index.php /.docker/mysql/my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | collation-server = utf8mb4_unicode_ci 3 | character-set-server = utf8mb4 4 | -------------------------------------------------------------------------------- /.docker/nginx/conf.d/php.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | server_name php.test; 5 | root /var/www/php; 6 | index index.php; 7 | 8 | location ~* \.php$ { 9 | fastcgi_pass php:9000; 10 | include fastcgi_params; 11 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 12 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.docker/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.1-fpm 2 | 3 | RUN docker-php-ext-install pdo_mysql -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=demo 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Yannick Chenot 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 for local web development, part 1: a basic LEMP stack 2 | 3 | This repository accompanies a [tutorial series](https://tech.osteel.me/posts/docker-for-local-web-development-why-should-you-care "Docker for local web development, introduction: why should you care?") about leveraging Docker for local web development. 4 | 5 | The current branch covers part 1 of the series, which is about setting up a basic LEMP stack with Docker Compose. Please refer to the [full article](https://tech.osteel.me/posts/docker-for-local-web-development-part-1-a-basic-lemp-stack "Docker for local web development, part 1: a basic LEMP stack") for a detailed explanation. 6 | 7 | ## Content 8 | 9 | This branch contains a basic LEMP stack running on Docker and orchestrated by Docker Compose, including: 10 | 11 | * A container for Nginx; 12 | * A container for PHP-FPM; 13 | * A container for MySQL; 14 | * A container for phpMyAdmin; 15 | * A volume to persist MySQL data. 16 | 17 | ## Prerequisites 18 | 19 | Make sure [Docker Desktop for Mac or PC](https://www.docker.com/products/docker-desktop) is installed and running, or head [over here](https://docs.docker.com/install/) if you are a Linux user. You will also need a terminal running [Git](https://git-scm.com/). 20 | 21 | This setup also uses localhost's port 80, so make sure it is available. 22 | 23 | ## Directions of use 24 | 25 | Add the following domain to your machine's `hosts` file: 26 | 27 | ``` 28 | 127.0.0.1 php.test 29 | ``` 30 | 31 | Clone the repository and change the current directory for the project's root: 32 | 33 | ``` 34 | $ git clone git@github.com:osteel/docker-tutorial.git 35 | $ cd docker-tutorial 36 | ``` 37 | 38 | Copy `.env.example` to `.env`: 39 | 40 | ``` 41 | $ cp .env.example .env 42 | ``` 43 | 44 | Run the following command: 45 | 46 | ``` 47 | $ docker compose up -d 48 | ``` 49 | 50 | This may take a little bit of time, as some Docker images might need downloading. 51 | 52 | Once the script is done, visit [php.test](http://php.test). 53 | 54 | ## Explanation 55 | 56 | The images used by the setup are listed and configured in [`docker-compose.yml`](https://github.com/osteel/docker-tutorial/blob/part-1/docker-compose.yml). 57 | 58 | When building and starting the containers based on the images for the first time, a MySQL database named `demo` is automatically created (you can pick a different name in the MySQL service's description in `docker-compose.yml`). 59 | 60 | A [minimalist Nginx configuration](https://github.com/osteel/docker-tutorial/blob/part-1/.docker/nginx/conf.d/php.conf) for the PHP application is also copied over to Nginx's container, making it available at [php.test](http://php.test). 61 | 62 | The `src/` directory containing the application is mounted onto both Nginx's and the application's containers, meaning any update to the code is immediately available upon refreshing the page, without having to rebuild any container. 63 | 64 | The database data is persisted in its own local directory through the volume `mysqldata`, which is mounted onto MySQL's container. A phpMyAdmin interface is available at [localhost:8080](http://localhost:8080) (the database credentials are root / root). 65 | 66 | Please refer to the [full article](https://tech.osteel.me/posts/docker-for-local-web-development-part-1-a-basic-lemp-stack "Docker for local web development, part 1: a basic LEMP stack") for a detailed explanation. 67 | 68 | ## Cleaning up 69 | 70 | To stop the containers: 71 | 72 | ``` 73 | $ docker compose stop 74 | ``` 75 | 76 | To destroy the containers: 77 | 78 | ``` 79 | $ docker compose down 80 | ``` 81 | 82 | To destroy the containers and the associated volumes: 83 | 84 | ``` 85 | $ docker compose down -v 86 | ``` 87 | 88 | To remove everything, including images and orphan containers: 89 | 90 | ``` 91 | $ docker compose down -v --rmi all --remove-orphans 92 | ``` 93 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | # Services 4 | services: 5 | 6 | # Nginx Service 7 | nginx: 8 | image: nginx:1.21 9 | ports: 10 | - 80:80 11 | volumes: 12 | - ./src:/var/www/php 13 | - ./.docker/nginx/conf.d:/etc/nginx/conf.d 14 | depends_on: 15 | - php 16 | 17 | # PHP Service 18 | php: 19 | build: ./.docker/php 20 | working_dir: /var/www/php 21 | volumes: 22 | - ./src:/var/www/php 23 | depends_on: 24 | mysql: 25 | condition: service_healthy 26 | 27 | # MySQL Service 28 | mysql: 29 | image: mysql/mysql-server:8.0 30 | environment: 31 | MYSQL_ROOT_PASSWORD: root 32 | MYSQL_ROOT_HOST: "%" 33 | MYSQL_DATABASE: demo 34 | volumes: 35 | - ./.docker/mysql/my.cnf:/etc/mysql/my.cnf 36 | - mysqldata:/var/lib/mysql 37 | healthcheck: 38 | test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD 39 | interval: 5s 40 | retries: 10 41 | 42 | # PhpMyAdmin Service 43 | phpmyadmin: 44 | image: phpmyadmin/phpmyadmin:5 45 | ports: 46 | - 8080:80 47 | environment: 48 | PMA_HOST: mysql 49 | depends_on: 50 | mysql: 51 | condition: service_healthy 52 | 53 | # Volumes 54 | volumes: 55 | 56 | mysqldata: 57 | -------------------------------------------------------------------------------- /src/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello there 6 | 19 | 20 | 21 | Hello there 22 | query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'demo'"); 25 | $tables = $query->fetchAll(PDO::FETCH_COLUMN); 26 | 27 | if (empty($tables)) { 28 | echo '

There are no tables in database demo.

'; 29 | } else { 30 | echo '

Database demo contains the following tables:

'; 31 | echo ''; 36 | } 37 | ?> 38 | 39 | --------------------------------------------------------------------------------