Are you trying to set up Laravel, Nginx, and MySQL with Docker Compose?
This guide is for you.
Docker has become a frequently used solution for deploying applications thanks to how it simplifies running and deploying applications in ephemeral containers.
Here at Ibmi Media, we regularly help our Customers to perform Docker related installations as part of our Server Management Services.
In this context, we shall look into how to set up Laravel, Nginx, and MySQL with Docker Compose.
In order to set up Laravel, Nginx, and MySQL with Docker Compose, simply follow the steps provided below;
1. Downloading Laravel and Installing Dependencies
First, we get the latest version of Laravel and install the dependencies.
We make sure that we are in the home directory and clone the latest Laravel release to a directory called laravel-app:
$ cd ~
$ git clone https://github.com/laravel/laravel.git laravel-app
Then we move into the laravel-app directory:
$ cd ~/laravel-app
Next, we use Docker’s composer image to mount the directories that you will need for your Laravel project.
$ docker run –rm -v $(pwd):/app composer install
Finally, we set permissions on the project directory so that it is owned by your non-root user:
$ sudo chown -R $USER:$USER ~/laravel-app
2. Creating the Docker Compose File
If we build our application with docker compose file then it simplifies the process of setting up and versioning the infrastructure. So to set up a Laravel application, we will write a docker-compose file that defines our web server, database, and application services.
First, we open the file.
$ nano ~/laravel-app/docker-compose.yml
Here, in the docker-compose file, we will define three services: app, web server, and DB. For that, we add the following code.
Note: Make sure to replace the root password for MYSQL_ROOT_PASSWORD.
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
networks:
- app-network
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: your_mysql_root_password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
Each container_name property defines a name for the container that corresponds to the name of the service. So we need to define this property.
For providing communication between containers, the services are connected to a bridge network called app-network.
3. Persisting Data
Docker has suitable features for persisting data. Here, we will make use of volumes and bind mounts for persisting the database, and application and configuration files.
In the docker-compose file, we define a volume called dbdata under the db service definition to persist the MySQL database:
...
#MySQL Service
db:
...
volumes:
- dbdata:/var/lib/mysql
networks:
- app-network
...
The named volume dbdata persists in the contents of the /var/lib/mysql folder that is present inside the container. This will allow us to stop and restart the DB service without losing data.
We add the definition for the dbdata volume at the bottom of the file.
...
#Volumes
volumes:
dbdata:
driver: local
Then we will add a bind mount to the DB service for the MySQL configuration files we will create in Step 7.
...
#MySQL Service
db:
...
volumes:
- dbdata:/var/lib/mysql
- ./mysql/my.cnf:/etc/mysql/my.cnf
...
This bind mount will bind ~/laravel-app/mysql/my.cnf to /etc/mysql/my.cnf in the container.
After that, we add bind mounts to the webserver service.
#Nginx Service
webserver:
...
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
Finally, we add the below bind mounts to the app service for the application code and configuration files:
#PHP Service
app:
...
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
Now, our docker-compose file will now look like this:
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: your_mysql_root_password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- dbdata:/var/lib/mysql/
- ./mysql/my.cnf:/etc/mysql/my.cnf
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
#Volumes
volumes:
dbdata:
driver: local
Finally, we save the file and exit the editor after making changes.
4. Creating the Dockerfile
In Docker, we can specify the environment inside of individual containers with a Dockerfile. Also, Dockerfile helps in creating custom images that we can use to install the software required by our application and configure settings.
Our Dockerfile will be located in our ~/laravel-app directory. Create the file:
$ nano ~/laravel-app/Dockerfile
This Dockerfile will set the base image. Also, it will specify the necessary commands and instructions to build the Laravel application image. Then we add the following code to the file:
FROM php:7.2-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Copy existing application directory contents
COPY . /var/www
# Copy existing application directory permissions
COPY --chown=www:www . /var/www
# Change current user to www
USER www
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
Finally, we save the file and exit the editor after making the changes.
5. Configuring PHP
In order to configure PHP, we will first create a local.ini file within the PHP folder. This is the file that we bind-mounted to /usr/local/etc/php/conf.d/local.ini inside the container in Step 2.
We run the below command to create the PHP directory.
$ mkdir ~/laravel-app/php
Next, we open the local.ini file:
$ nano ~/laravel-app/php/local.ini
In order to demonstrate the configuration of PHP, we’ll add the following code to set size limitations for uploaded files:
upload_max_filesize=40M
post_max_size=40M
Finally, we save the file and exit the editor.
6. Configuring Nginx
In order to configure Nginx, we will create an app.conf file with the service configuration in the ~/laravel-app/nginx/conf.d/ folder.
First, we will create the nginx/conf.d/ directory:
$ mkdir -p ~/laravel-app/nginx/conf.d
Next, we create the app.conf configuration file.
$ nano ~/laravel-app/nginx/conf.d/app.conf
After that, we add the following code to the file to specify the Nginx configuration:
server {
listen 80;
index index.php index.html;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /var/www/public;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
gzip_static on;
}
}
Finally, we save the file and exit the editor.
7. Configuring MySQL
In order to configure MySQL, we will create my.cnf file in the MySQL folder. This is the file that we bind-mounted to /etc/mysql/my.cnf inside the container in Step 2.
First, we will create the MySQL directory by running the below command.
$ mkdir ~/laravel-app/mysql
Next, we make my.cnf file.
$ nano ~/laravel-app/mysql/my.cnf
In the above file, we add the following code to enable the query log and set the log file location:
[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log
Finally, we save the file and exit the editor.
8. Modifying Environment Settings and Running the Containers
Now, we have defined all of our services in the docker-compose file and created the configuration files for these services.
We will make a copy of the .env.example file that Laravel includes by default and name the copy .env.
$ cp .env.example .env
Now, we shall modify the .env file on the app container. In the file. we will add some specific details about our setup. We run the below command to open a text editor.
$ nano .env
Then we will modify the fields such as DB_HOST, DB_DATABASE, DB_USERNAME, and DB_PASSWORD.
The code will look as below.
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laraveluser
DB_PASSWORD=your_laravel_db_password
We save the changes and exit the editor.
We start all of the containers that are defined in our docker-compose file.
$ docker-compose up -d
The above command will download all of the necessary Docker images, which might take a while. Once the process completes, we run the below command to list all of the running containers:
$ docker ps
Now, we will run the below command to set the application key for the Laravel application. This command will generate a key and copy it to the .env file. Also, it ensures that the user sessions and encrypted data remain secure.
$ docker-compose exec app php artisan key:generate
After that, we cache the settings into a file which will boost the application’s load speed.
$ docker-compose exec app php artisan config:cache
The configuration settings will be loaded into /var/www/bootstrap/cache/config.php on the container.
Finally, we visit http://your_server_ip in the browser.
9. Creating a User for MySQL
Now, we will create a new user. For that, we execute an interactive bash shell on the db container with docker-compose exec:
$ docker-compose exec db bash
Within the container, we log into the MySQL root administrative account:
mysql -u root -p
We enter the password when prompted. We then check for existing databases by running the below command.
mysql> show databases;
As a result, we see the laravel database listed in the output.
Next, we create the user account that will be allowed to access this database. The username will be ‘laraveluser’. Also, we ensure that the username and password here match the details we set in our .env file in the previous step.
mysql> GRANT ALL ON laravel.* TO 'laraveluser'@'%' IDENTIFIED BY 'your_laravel_db_password';
Then we flush the privileges to notify the MySQL server of the changes.
mysqL> FLUSH PRIVILEGES;
Then we exit MySQL.
mysqL> EXIT;
Finally, we exit the container.
exit
10. Migrating Data and Working with the Tinker Console
With the application running, we can migrate the data and experiment with the tinker command. Using the tinker command we can interact with the Laravel application from the command line in an interactive shell.
First, we test the connection to MySQL by running the below Laravel artisan migrate command.
$ docker-compose exec app php artisan migrate
This command will migrate the default Laravel tables.
After the migration completes, we run a query to check if we are properly connected to the database using the tinker command:
$ docker-compose exec app php artisan tinker
Then we test the MySQL connection by getting the data we just migrated:
\DB::table('migrations')->get();
As a result, we will see the below output.
Output
=> Illuminate\Support\Collection {#2856
all: [
{#2862
+"id": 1,
+"migration": "2020_12_12_000000_create_users_table",
+"batch": 1,
},
{#2865
+"id": 2,
+"migration": "2020_12_12_100000_create_password_resets_table",
+"batch": 1,
},
],
}
This article will guide you on how to set up #Laravel, #Nginx, and #MySQL with #Docker Compose. When using a #LEMP application stack, for example, with PHP, Nginx, MySQL and the Laravel framework, Docker can significantly streamline the setup process.
Docker Compose has further simplified the development process by allowing developers to define their #infrastructure, including application services, #networks, and volumes, in a single file. Docker Compose offers an efficient alternative to running multiple docker container create and docker container run #commands.