This notes is simplify version of long series of Docker Setup for local web development project write by tech.osteel.me. Adjustments were made based on my habit and repetitive step when starting new laravel new project.
- Ensure Docker Desktop with Docker Compose has been installed.
- Create project folder (for example: /Users/macto/Development/laravel-project).
- Change project name inside this notes with yours.
- For folder: laravel-project.
- For inside config files: laravelproject.
 
- For folder: 
- In this notes I use only below services:
- laravel (as backend)
- mysql (as database)
- phpmyadmin (as database management ui)
 
final folder structure for this setup as per below:
laravel-project/
├── .docker/
│   ├── mysql/
│   │   └── my.cnf
│   └── nginx/
│       └── conf.d/
│           └── backend.conf
│           └── phpmyadmin.conf
├── src/
│   └── backend/
│       └── Dockerfile
├── .env
├── .env.example
├── .gitignore
└── docker-compose.yml
- Copy, paste and save as docker-compose.yml for below minimal configuration:
version: '3.8'
# Services
services:
  # Nginx Service
  nginx:
    build: ./.docker/nginx
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./src/backend:/var/www/backend
      - ./.docker/nginx/conf.d:/etc/nginx/conf.d
      - phpmyadmindata:/var/www/phpmyadmin
      - ./.docker/nginx/certs:/etc/nginx/certs
    depends_on:
      - backend
      - phpmyadmin
  # Backend Service
  backend:
    build:
      context: ./src/backend
      target: backend
    working_dir: /var/www/backend
    volumes:
      - ./src/bend:/var/www/backend
      - ./.docker/backend/init:/opt/files/init
      - ./.docker/nginx/certs:/usr/local/share/ca-certificates
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
  # MySQL Service
  mysql:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: laravelproject
    volumes:
      - ./.docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
      - mysqldata:/var/lib/mysql
    healthcheck:
      test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD
      interval: 5s
      retries: 10
  # PhpMyAdmin Service
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:5-fpm-alpine
    environment:
      PMA_HOST: mysql
    volumes:
      - phpmyadmindata:/var/www/html
    depends_on:
      mysql:
        condition: service_healthy
# Volumes
volumes:
  mysqldata:
  phpmyadmindata:
- create Dockerfilefile in.docker/nginx/
- copy below code to new created Dockerfile
FROM nginx:1.19-alpine
# Install packages
RUN apk --update --no-cache add openssl
- create backendfolder inlaravel-project/src/
- create Dockerfilefile inlaravel-project/src/backend/
- copy below code:
FROM php:8.0-fpm-alpine as backend
# Import extension installer
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
# Install extensions
RUN install-php-extensions pdo_mysql bcmath opcache redis
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
# Configure PHP
COPY .docker/php.ini $PHP_INI_DIR/conf.d/opcache.ini
# Use the default development configuration
RUN mv $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
# Install extra packages
RUN apk --no-cache add bash mysql-client mariadb-connector-c-dev
FROM backend as worker
# Start worker
CMD ["php", "/var/www/bend/artisan", "queue:work"]
- create .dockerfolder inlaravel-project/src/backend/
- create php.inifile inside newly created.dockerfolder
- copy below code to the php.ini:
[opcache]
opcache.enable=1
opcache.revalidate_freq=0
opcache.validate_timestamps=1
opcache.max_accelerated_files=10000
opcache.memory_consumption=192
opcache.max_wasted_percentage=10
opcache.interned_strings_buffer=16
opcache.fast_shutdown=1
- create backend.conffile in.docker/nginx/conf.d
- copy below code:
server {
    listen      443 ssl http2;
    listen      [::]:443 ssl http2;
    server_name backend.laravelproject.test *.ngrok.io;
    root        /var/www/backend/public;
    ssl_certificate     /etc/nginx/certs/laravelproject.test.crt;
    ssl_certificate_key /etc/nginx/certs/laravelproject.test.key;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    index index.html index.htm index.php;
    charset utf-8;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
    error_page 404 /index.php;
    location ~ \.php$ {
        fastcgi_pass  bend:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include       fastcgi_params;
    }
    location ~ /\.(?!well-known).* {
        deny all;
    }
}
server {
    listen      80;
    listen      [::]:80;
    server_name backend.laravelproject.test;
    return      301 https://$server_name$request_uri;
}
- create phpmyadmin.conffile in.docker/nginx/conf.d
- copy below code:
server {
    listen      443 ssl http2;
    listen      [::]:443 ssl http2;
    server_name phpmyadmin.laravelproject.test;
    root        /var/www/phpmyadmin;
    index       index.php;
    ssl_certificate     /etc/nginx/certs/laravelproject.test.crt;
    ssl_certificate_key /etc/nginx/certs/laravelproject.test.key;
    location ~* \.php$ {
        fastcgi_pass   phpmyadmin:9000;
        root           /var/www/html;
        include        fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param  SCRIPT_NAME     $fastcgi_script_name;
    }
}
server {
    listen      80;
    listen      [::]:80;
    server_name phpmyadmin.laravelproject.test;
    return      301 https://$server_name$request_uri;
}
- create mysqlfolder inlaravel-project/.docker
- create my.cnffile in newly created mysql folder
- copy below code to my.cnffile
[mysqld]
collation-server     = utf8mb4_unicode_ci
character-set-server = utf8mb4
default-authentication-plugin = mysql_native_password
- do this in root folder
- do the same in src/backend
- create initfile inlaravel-project/.docker/backend
- copy below code:
#!/bin/bash
# Install Composer dependencies
composer install -d "/var/www/backend"
# Deal with the .env file if necessary
if [ ! -f "/var/www/backend/.env" ]; then
    # Create .env file
    cat > "/var/www/backend/.env" << EOF
APP_NAME=laravelproject
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://backend.laravelproject.test
LOG_CHANNEL=single
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravelproject
DB_USERNAME=root
DB_PASSWORD=root
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=redis
REDIS_HOST=redis
SESSION_DRIVER=file
EOF
    # Generate application key
    php "/var/www/backend/artisan" key:generate --ansi
fi
# Database
php "/var/www/backend/artisan" migrate
- update initpermission
$ chmod +x .docker/backend/init
- create laravelproject.shinlaravel-projectroot folder
- change permission $ chmod +x laravelproject.sh
- copy below code (to check and edit any github account related setup):
#!/bin/bash
#######################################
# FUNCTIONS
#######################################
# Run an Artisan command
artisan () {
    docker-compose run --rm bend php artisan "${@:1}"
}
# Build all of the images or the specified one
build () {
    docker-compose build "${@:1}"
}
# Run a Composer command
composer () {
    docker-compose run --rm bend composer "${@:1}"
}
# Remove the entire Docker environment
destroy () {
    read -p "This will delete containers, volumes and images. Are you sure? [y/N]: " -r
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit; fi
    docker-compose down -v --rmi all --remove-orphans
}
# Stop and destroy all containers
down () {
    docker-compose down "${@:1}"
}
# Create and start the containers and volumes
start(){
    docker-compose up -d
}
# Stop the containers
stop () {
    docker-compose stop
}
# Display and tail the logs
logs () {
    docker-compose logs -f "${@:1}"
}
# Restart the containers
restart () {
    stop && start
}
# Run a Yarn command
yarn () {
    docker-compose run --rm frontend yarn "${@:1}"
}
# Create .env from .env.example
env () {
    if [ ! -f .env ]; then
        cp .env.example .env
    fi
}
# Initialise the Docker environment and the application
init () {
    env \
        && down -v \
        && build \
        && docker-compose run --rm --entrypoint="//opt/files/init" backend \
        && yarn install
    
    if [ ! -f .docker/nginx/certs/laravelproject.test.crt ]; then
        cert_generate
    fi
    start && cert_install
}
# Update the Docker environment
update () {
    git pull \
        && build \
        && composer install \
        && artisan migrate \
        && yarn install \
        && start
}
# Clone or update the repositories
repositories () {
    repos=(backend)
    cd src
    for repo in "${repos[@]}";
    do
        git clone "[email protected]:username/${repo}.git" "$repo" || (cd "$repo" ; git pull ; cd ..) || true
    done
    cd ..
}
# Generate a wildcard certificate
cert_generate () {
    rm -Rf .docker/nginx/certs/laravelproject.test.*
    docker-compose run --rm nginx sh -c "cd /etc/nginx/certs && touch openssl.cnf && cat /etc/ssl/openssl.cnf > openssl.cnf && echo \"\" >> openssl.cnf && echo \"[ SAN ]\" >> openssl.cnf && echo \"subjectAltName=DNS.1:laravelproject.test,DNS.2:*.laravelproject.test\" >> openssl.cnf && openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout laravelproject.test.key -out heartweb.test.crt -days 3650 -subj \"/CN=*.laravelproject.test\" -config openssl.cnf -extensions SAN && rm openssl.cnf"
}
cert_install () {
    if [[ "$OSTYPE" == "darwin"* ]]; then
        sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain .docker/nginx/certs/laravelproject.test.crt
    elif [[ "$OSTYPE" == "linux-gnu" ]]; then
        sudo ln -s "$(pwd)/.docker/nginx/certs/laravelproject.test.crt" /usr/local/share/ca-certificates/laravelproject.test.crt
        sudo update-ca-certificates
    else
        echo "Could not install the certificate on the host machine, please do it manually"
    fi
    docker-compose exec bend update-ca-certificates
}
#######################################
# MENU
#######################################
case "$1" in
    artisan)
        artisan "${@:2}"
        ;;
    build)
        build "${@:2}"
        ;;
    composer)
        composer "${@:2}"
        ;;
    destroy)
        destroy
        ;;
    down)
        down "${@:2}"
        ;;
    logs)
        logs "${@:2}"
        ;;
    restart)
        restart
        ;;
    start)
        start
        ;;
    stop)
        stop
        ;;
    yarn)
        yarn "${@:2}"
        ;;
    init)
        init
        ;;
    update)
        update
        ;;
    repositories)
        repositories
        ;;
    cert)
        case "$2" in
            generate)
                cert_generate
                ;;
            install)
                cert_install
                ;;
            *)
                cat << EOF
Certificate management commands.
Usage:
    demo cert <command>
Available commands:
    generate .................................. Generate a new certificate
    install ................................... Install the certificate
EOF
                ;;
        esac
        ;;
    *)
        cat << EOF
Command line interface for the Docker-based web development environment demo.
Usage:
    demo <command> [options] [arguments]
Available commands:
    artisan ................................... Run an Artisan command
    build [image] ............................. Build all of the images or the specified one
    composer .................................. Run a Composer command
    cert ...................................... Certificate management commands
        generate .............................. Generate a new certificate
        install ............................... Install the certificate
    destroy ................................... Remove the entire Docker environment
    down [-v] ................................. Stop and destroy all containers
                                                Options:
                                                    -v .................... Destroy the volumes as well
    init ...................................... Initialise the Docker environment and the application
    logs [container] .......................... Display and tail the logs of all containers or the specified one's
    restart ................................... Restart the containers
    start ..................................... Start the containers
    stop ...................................... Stop the containers
    update .................................... Update the Docker environment
    yarn ...................................... Run a Yarn command
EOF
        exit 1
        ;;
esac
- edit ~/.bashrc
- copy below code to last line of .bashrc
function laravelproject {
    cd /PATH/TO/YOUR/PROJECT && bash demo $*
        cd -
}
- save and run command $ source ~/.bashrcto re-load.bashrc
run below command:
$ docker compose up -d
edit /etc/hosts file with below:
127.0.0.1 backend.laravelproject.test phpmyadmin.laravelproject.test
run below command:
$ docker compose images
run below command:
$ docker compose ps
run below command:
$ docker compose stop