We can containerize our applications using Docker to have a separate installation of the required packages with the application-specific versions independent of the underlying operating system. We can use Docker Containers to make our application portable so that we can simply move it to another system having docker. This tutorial provides all the steps to containerize a Laravel application using Apache Web Server and either MySQL or MongoDB or both as the database server.
Prerequisites
Windows - How To Install WSL 2 (Windows Subsystem for Linux) with Ubuntu On Windows 10, How To Install Docker Desktop On Windows 10, and How To Install Composer As PHP Dependency Manager On Windows. Optionally you may follow How To Change Docker Data Path On Windows 10.
Ubuntu - How To Install Docker Engine on Ubuntu 20.04 LTS and How To Install Composer On Ubuntu 20.04
macOS - How To Install Docker Desktop On Mac
Install PHP, Laravel, and Apache Web Server
Create a directory to store your project-specific files. I have created the directory helloworld to store all the project files. Now create the file docker-compose.yml at the root of your project directory as shown below.
# docker-compose.yml version: "3.8" services: apache: container_name: apache build: ./docker/apache links: - php ports: - "80:80" volumes: - ./logs/apache:/var/log/apache2 - ./src/helloworld:/usr/local/apache2/htdocs/helloworld php: container_name: php build: ./docker/php ports: - "9000:9000" volumes: - ./src/helloworld:/usr/local/apache2/htdocs/helloworld working_dir: /usr/local/apache2/htdocs/helloworld composer: container_name: composer image: composer/composer volumes: - ./src/helloworld:/usr/local/apache2/htdocs/helloworld working_dir: /usr/local/apache2/htdocs/helloworld command: install
Now create the directories - docker and src within the project root directory. Also, create two directories within the docker directory i.e. php and apache. Create the directories <project path>/logs/apache to store the logs.
Create the Dockerfile within the PHP directory as shown below.
# docker/php/Dockerfile FROM php:8.1-fpm
RUN apt-get update RUN apt-get install -y openssl zip unzip git curl RUN apt-get install -y libzip-dev libonig-dev libicu-dev RUN apt-get install -y autoconf pkg-config libssl-dev RUN docker-php-ext-install bcmath mbstring intl opcache
Create the Dockerfile within the Apache directory as shown below.
# docker/apache/Dockerfile FROM httpd:2.4.51
We also need to configure the Virtual Host to pass the PHP requests to PHP-FPM via port 9000. Now, create the default configuration file as shown below.
# docker/apache/helloworld.apache.conf LoadModule deflate_module /usr/local/apache2/modules/mod_deflate.so LoadModule proxy_module /usr/local/apache2/modules/mod_proxy.so LoadModule proxy_fcgi_module /usr/local/apache2/modules/mod_proxy_fcgi.so
<VirtualHost *:80> ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://php:9000/usr/local/apache2/htdocs/helloworld/public/$1 DocumentRoot /usr/local/apache2/htdocs/helloworld/public <Directory /usr/local/apache2/htdocs/helloworld> Options -Indexes +FollowSymLinks DirectoryIndex index.php AllowOverride All Require all granted </Directory> </VirtualHost>
Now, update the Dockerfile within the Apache directory as shown below.
# docker/apache/Dockerfile FROM httpd:2.4.51
COPY helloworld.apache.conf /usr/local/apache2/conf/helloworld.apache.conf RUN echo "Include /usr/local/apache2/conf/helloworld.apache.conf" \ >> /usr/local/apache2/conf/httpd.conf
After creating all the directories and files, the directory structure should be similar as shown below.
helloworld -- docker -- php -- Dockerfile -- apache -- Dockerfile -- helloworld.apache.conf -- src -- <laravel app directories and files> -- logs -- apache -- docker-compose.yml
Next, we will install the Laravel project template files in the src directory using the command shown below. It's required for the first time for the fresh installation of Laravel.
# Clear Directory cd <path to project>/src
# Install Laravel composer create-project --prefer-dist laravel/laravel:^8.0 helloworld
# Output Creating a "laravel/laravel:^8.0" project at "./helloworld" Installing laravel/laravel (v8.77.1) - Downloading laravel/laravel (v8.77.1) - Installing laravel/laravel (v8.77.1): Extracting archive Created project in E:\development\projects-study\php\docker\helloworld\src\helloworld > @php -r "file_exists('.env') || copy('.env.example', '.env');" Loading composer repositories with package information Updating dependencies .... .... Package manifest generated successfully. 77 packages you are using are looking for funding. Use the `composer fund` command to find out more! > @php artisan vendor:publish --tag=laravel-assets --ansi --force No publishable resources for tag [laravel-assets]. Publishing complete.
> @php artisan key:generate --ansi Application key set successfully.
The above command installs Laravel at <path to project>/src/helloworld.
Now, run the command docker-compose build to build the images for PHP and Apache Web Server.
# Build cd <path to project> docker-compose build
# Output Building php [+] Building 3.6s (6/6) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 31B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/php:8.1-fpm 3.3s => [auth] library/php:pull token for registry-1.docker.io 0.0s => CACHED [1/1] FROM docker.io/library/php:8.1-fpm@sha256:c884ad419ce99d3136cd02a56f7ca84009fa5a50637e96d20acdd7 0.0s => exporting to image 0.1s => => exporting layers 0.0s => => writing image sha256:dd8e9ace979d52b810014bd90565f9bf60ffdbb754af263b1eb3b2bb5cc88648 0.0s => => naming to docker.io/library/helloworld_php 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them Building apache [+] Building 4.3s (9/9) FINISHED => [internal] load build definition from Dockerfile 0.1s => => transferring dockerfile: 250B 0.0s => [internal] load .dockerignore 0.1s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/httpd:2.4.51 3.9s => [auth] library/httpd:pull token for registry-1.docker.io 0.0s => [internal] load build context 0.1s => => transferring context: 586B 0.0s => [1/3] FROM docker.io/library/httpd:2.4.51@sha256:fba8a9f4290180ceee5c74638bb85ff21fd15961e6fdfa4def48e1882051 0.0s => CACHED [2/3] COPY helloworld.apache.conf /usr/local/apache2/conf/helloworld.apache.conf 0.0s => CACHED [3/3] RUN echo "Include /usr/local/apache2/conf/helloworld.apache.conf" >> /usr/local/apache2/conf 0.0s => exporting to image 0.1s => => exporting layers 0.0s => => writing image sha256:163fe8f15637311df7bb05aab39101a1505723c105461d253539939da9a14c36 0.0s => => naming to docker.io/library/helloworld_apache 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
After completing the build, we can run the application using the command shown below.
# Launch Application docker-compose up
# Output Creating network "helloworld_default" with the default driver Creating php ... done Creating apache ... done Attaching to php, apache
Now open the browser and enter the URL - http://localhost. It should show Laravel's default, Welcome Page, as shown in Fig 1.
Press Ctrl + C to stop the containers.
Install MySQL and phpMyAdmin
In this step, we will continue with our previous step and install MySQL and phpMyAdmin. We will also configure our laravel application to access the MySQL database. Now, update the docker-compose.yml as shown below.
# docker-compose.yml version: "3.8" services: apache: container_name: apache build: ./docker/apache links: - php ports: - "80:80" volumes: - ./logs/apache:/var/log/apache2 - ./src/helloworld:/usr/local/apache2/htdocs/helloworld php: container_name: php build: ./docker/php links: - mysql ports: - "9000:9000" volumes: - ./src/helloworld:/usr/local/apache2/htdocs/helloworld working_dir: /usr/local/apache2/htdocs/helloworld composer: container_name: composer image: composer/composer volumes: - ./src/helloworld:/usr/local/apache2/htdocs/helloworld working_dir: /usr/local/apache2/htdocs/helloworld command: install mysql: image: mysql:8.0.27 container_name: mysql environment: MYSQL_ROOT_PASSWORD: 'password' MYSQL_DATABASE: helloworld MYSQL_USER: helloworld MYSQL_PASSWORD: 'password' ports: - "3306:3306" volumes: - ./database/mysql:/var/lib/mysql phpmyadmin: image: phpmyadmin/phpmyadmin container_name: pma links: - mysql environment: PMA_HOST: mysql PMA_PORT: 3306 PMA_ARBITRARY: 1 restart: always ports: - 8085:80
Now, install the PHP extensions to access MySQL from the PHP source files by updating the Dockerfile as shown below.
# docker/php/Dockerfile FROM php:8.1-fpm
RUN apt-get update RUN apt-get install -y openssl zip unzip git curl RUN apt-get install -y libzip-dev libonig-dev libicu-dev RUN apt-get install -y autoconf pkg-config libssl-dev RUN docker-php-ext-install bcmath mbstring intl opcache RUN docker-php-ext-install pdo pdo_mysql mysqli
Now configure the Laravel application to use the MySQL database by updating the files as shown below. You can also follow The Complete Guide To Perform CRUD Operations In Laravel Framework Using MySQL.
# Laravel Configurations cd <path to project>/src/helloworld
# Update .env .... .... DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=helloworld DB_USERNAME=root DB_PASSWORD= '<root-password>' .... ....
We can also create other users and databases using phpMyAdmin. Now, run the command docker-compose build to build the application.
# Build cd <path to project> docker-compose build
# Output mysql uses an image, skipping phpmyadmin uses an image, skipping Building php .... .... => => naming to docker.io/library/helloworld_apache 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
We also need to run the command docker-compose up to launch the application as shown below. It will pull the MySQL and phpMyAdmin images and take time for the first time. The subsequent launches will be faster.
# Launch Application docker-compose up
# Output Pulling mysql (mysql:8.0.27)... 8.0.27: Pulling from library/mysql ffbb094f4f9e: Pull complete df186527fc46: Pull complete fa362a6aa7bd: Pull complete 5af7cb1a200e: Pull complete .... .... Status: Downloaded newer image for mysql:8.0.27 Pulling phpmyadmin (phpmyadmin/phpmyadmin:)... latest: Pulling from phpmyadmin/phpmyadmin 69692152171a: Pull complete 2040822db325: Pull complete .... .... 2e982de2b8e5: Pull complete Digest: sha256:382dedf6b43bf3b6c6c90f355b4dda660beb3e099dqw1bb3241170e54fca6d59 Status: Downloaded newer image for phpmyadmin/phpmyadmin:latest .... .... Creating mysql ... done Recreating php ... done Creating pma ... done Recreating apache ... done Attaching to mysql, pma, php, apache .... ....
Now, try to access phpMyAdmin from the Browser using the URL http://localhost:8085. It should show the phpMyAdmin home page as shown in Fig 2.
Now, login to phpMyAdmin using the username as root and the root password configured in the docker-compose.yml. Also, leave the server blank. It should show the phpMyAdmin home page as shown in Fig 3.
Now create the database tables using the artisan command as shown below.
# CD to Project Path cd <path to project>
# List Files docker-compose exec <php service name> ls -l
# Example docker-compose exec php ls -l
# Output total 300 -rwxrwxrwx 1 root root 4051 Dec 23 03:03 README.md drwxrwxrwx 1 root root 4096 Dec 23 03:03 app -rwxrwxrwx 1 root root 1686 Dec 23 03:03 artisan drwxrwxrwx 1 root root 4096 Dec 23 03:03 bootstrap .... .... # Migration Command docker-compose exec <php service name> php artisan migrate
# Example docker-compose exec php php artisan migrate
# Output Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (509.81ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (499.07ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (501.43ms) Migrating: 2019_12_14_000001_create_personal_access_tokens_table Migrated: 2019_12_14_000001_create_personal_access_tokens_table (992.00ms)
This completes the installation and configuration of MySQL and phpMyAdmin for the Laravel application.
Press Ctrl + C to stop the containers.
# Press Ctrl + C Gracefully stopping... (press Ctrl+C again to force) Stopping apache ... done Stopping php ... done Stopping pma ... done Stopping mysql ... done
Install MongoDB and MongoDB Express
In this step, we will continue with our previous step and install MongoDB and MongoDB Express. Now, update the docker-compose.yml as shown below.
# docker-compose.yml version: "3.8" services: apache: container_name: apache build: ./docker/apache links: - php ports: - "80:80" volumes: - ./logs/apache:/var/log/apache2 - ./src/helloworld:/usr/local/apache2/htdocs/helloworld php: container_name: php build: ./docker/php links: - mysql - mongo ports: - "9000:9000" volumes: - ./src/helloworld:/usr/local/apache2/htdocs/helloworld working_dir: /usr/local/apache2/htdocs/helloworld composer: container_name: composer image: composer/composer volumes: - ./src/helloworld:/usr/loca/apache2/htdocs/helloworld working_dir: /usr/local/apache2/htdocs/helloworld command: install mysql: image: mysql:8.0.27 container_name: mysql environment: MYSQL_ROOT_PASSWORD: 'password' MYSQL_DATABASE: helloworld MYSQL_USER: helloworld MYSQL_PASSWORD: 'password' ports: - "3306:3306" volumes: - ./database/mysql:/var/lib/mysql phpmyadmin: image: phpmyadmin/phpmyadmin container_name: pma links: - mysql environment: PMA_HOST: mysql PMA_PORT: 3306 PMA_ARBITRARY: 1 restart: always ports: - 8085:80 mongo: image: mongo:5.0 container_name: mongo environment: - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=password restart: unless-stopped ports: - "27017:27017" volumes: - ./database/mongodb/db:/data/db - ./database/mongodb/dev.archive:/Databases/dev.archive - ./database/mongodb/production:/Databases/production command: [--auth] mongo-express: image: mongo-express container_name: mexpress environment: - ME_CONFIG_MONGODB_ADMINUSERNAME=root - ME_CONFIG_MONGODB_ADMINPASSWORD=password - ME_CONFIG_MONGODB_URL=mongodb://root:password@mongo:27017/?authSource=admin - ME_CONFIG_BASICAUTH_USERNAME=mexpress - ME_CONFIG_BASICAUTH_PASSWORD=mexpress links: - mongo restart: unless-stopped ports: - "8081:8081"
Replace the root password while configuring the docker-compose.yml. Also, install the PHP extensions to access MongoDB from the PHP source files by updating the Dockerfile as shown below.
# docker/php/Dockerfile FROM php:8.1-fpm
RUN apt-get update RUN apt-get install -y openssl zip unzip git curl RUN apt-get install -y libzip-dev libonig-dev libicu-dev RUN apt-get install -y autoconf pkg-config libssl-dev RUN docker-php-ext-install bcmath mbstring intl opcache RUN docker-php-ext-install pdo pdo_mysql mysqli RUN pecl install mongodb RUN echo "extension=mongodb.so" >> /usr/local/etc/php/conf.d/mongodb.ini
Also, configure the Laravel application to access the MongoDB database along with MySQL. You may skip MySQL and configure MongoDB as the only server depending on your requirements. You can also follow The Complete Guide To Perform CRUD Operations In Laravel Framework Using MongoDB.
# Laravel Configurations cd <path to project>/src/helloworld
# MongoDB Extension for Laravel composer require jenssegers/mongodb
# Update .env
....
.... MONGO_CONNECTION=mongodb MONGO_HOST=mongo MONGO_PORT=27017 MONGO_AUTH_DATABASE=admin MONGO_DATABASE=helloworld MONGO_USERNAME=root MONGO_PASSWORD= '<password>' .... ....
Also, update the database configuration by updating config/database.php as shown below.
.... .... 'default' => env('DB_CONNECTION', 'mysql'), //'default' => env('DB_CONNECTION', 'mongodb'), .... .... 'connections' => [ .... .... 'mongodb' => [ 'driver' => 'mongodb', 'host' => env('MONGO_HOST'), 'port' => env('MONGO_PORT'), 'database' => env('MONGO_DATABASE'), 'username' => env('MONGO_USERNAME'), 'password' => env('MONGO_PASSWORD), 'options' => [ 'database' => env('MONGO_AUTH_DATABASE') ] ], .... ....
Now, configure the providers to use the MongoDB extension provided by Jens Segers by updating the config/app.php file as shown below.
.... .... 'providers' => [ .... .... Jenssegers\Mongodb\MongodbServiceProvider::class ], .... ....
Now, run the build and up commands to again build the application and launch it. We can access MongoDB using the URL - http://localhost:8081. It will ask for the basic authentication configured by us. The home page should be similar as shown below.
This completes the installation and configuration of MongoDB for the Laravel application.
Summary
This tutorial provided all the steps to containerize Laravel with Apache Web Server, MySQL, phpMyAdmin, MongoDB, and MongoDB Express using Docker containers.