How To Install and Set Up Laravel, Nginx, and MySQL With Docker Compose on Ubuntu 20.04
GOALS:
- How to create a new user with sudo privileges
- How to install docker & Install docker-compose
- Cloning the laravel repo
- How to create a Dockerfile & docker-compose.yml file
- How to configure NGINX, PHP & MySQL
- Installing composer & running the container
Holla!!!
in this blog, you are going to learn how to set up Laravel, Nginx,PHP and MySQL with docker-compose
this blog will teach you the fundamentals of how to “Set Up Laravel, Nginx, and MySQL With Docker Compose” by yourself
but if you want a well-detailed video explanation of a zoom mastermind class.
then you should CLICK HERE to reach me. see you soon!
prerequisite:
- One Ubuntu 20.04 server, it can be created on any cloud provider like digital ocean. you can open an account and learn how to create a VM HERE
when you are done creating the server. you can come back here to continue.
but if you already have these things ready then let’s go!!
1. CREATE A NON-ROOT USER
in this section, we are going to create a non-root user with sudo privileges. we need to switch to our root user. do that using this command:
$ sudo su
NOTE: if you are using digital ocean. you are by default the root user.
now that you are on your root user. you need to create a new user with this command:
$ adduser teddi
the user can be named anything but in this blog, we name it teddi.
NOTE: After creating a user you need to add a password that you must not forget.
and also add the user name then you can leave the remaining path blank if you wish.
also when writing the password it won’t show on the screen.
Add User To Sudo Group
now we will need to add our user to sudo group using this command:
$ usermod -aG sudo teddi
Switch To Newly Created User
$ su teddi
NOTE: make sure you switch to the user created before going into the INSTALLING DOCKER section of this blog
2. INSTALLING DOCKER
in this section which is the second section, we will be installing docker.
First, update your existing list of packages:
$ sudo apt update
Next, install a few prerequisite packages which let apt
use packages over HTTPS:
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common
Then add the GPG key for the official Docker repository to your system:
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Add the Docker repository to APT sources:
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
This will also update our package database with the Docker packages from the newly added repo.
Make sure you are about to install from the Docker repo instead of the default Ubuntu repo:
$ apt-cache policy docker-ce
Finally, install Docker:
$ sudo apt install docker-ce
Docker should now be installed, the daemon started, and the process enabled to start on boot. Check that it’s running:
$ sudo systemctl status docker
you should get an output like this:
Executing the Docker Command Without Sudo (Optional)
If you want to avoid typing sudo
whenever you run the docker
command, add your username to the docker
group:
$ sudo usermod -aG docker ${USER}
To apply the new group membership, log out of the server and back in, or type the following:
$ su - ${USER}
You will be prompted to enter your user’s password to continue.
Confirm that your user is now added to the docker group by typing:
$ groups
If you need to add a user to the docker
group that you’re not logged in as, declare that username explicitly using:
$ sudo usermod -aG docker username
3. INSTALLING DOCKER-COMPOSE
in this section we are going to be using a few commands to install docker-compose.
Verify the most recent version in their releases website first.
The most stable version is 1.29.2 as of this writing.
To make this program publicly accessible as docker-compose, run the command below to download the 1.29.2 version and store the executable file to /usr/local/bin/docker-compose:
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Next, set the correct permissions so that the docker-compose
command is executable:
$ sudo chmod +x /usr/local/bin/docker-compose
To verify that the installation was successful, you can run:
$ docker-compose --version
You should see output similar to this:
Output
docker-compose version 2.16.0, build 6aecbg4cl
4. CLONING THE LARAVEL REPO
in this section, we will be cloning the laravel repository.
first, you need to check that you are in the home directory to clone laravel and you can do that with this command:
$ cd ~
$ git clone https://github.com/laravel/laravel.git laravel-app
Move into the laravel-app
directory:
$ cd ~/laravel-app
As a final step, set permissions on the project directory so that it is owned by your non-root user:
$ sudo chown -R teddi:teddi ~/laravel-app
5. CREATING DOCKER COMPOSE & DOCKERFILE
in this section you will write a docker-compose
file that defines your web server, database, and application services.
Open the file:
$ nano ~/laravel-app/docker-compose.yml
You must define three services in the docker-compose file: app, webserver, and database.
Replace the environment variable MYSQL ROOT PASSWORD under the db service with a secure password of your choice before adding the following code to the file:
version: '3'
services:
#PHP Service
app:
build:
args:
user: teddi
uid: 1000
context: .
dockerfile: Dockerfile
image: teddi/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/
- ./nginx-logs:/var/log/nginx
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: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
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
NOTE: in the app section where the user is set as teddi. yours should also be set as the name of the user we created in this blog.
save this file and move to the next section.
DOCKERFILE
Your Dockerfile
will be located in your ~/laravel-app
directory. Create the file:
$ nano ~/laravel-app/Dockerfile
The Laravel application image will be created using the base image specified in this Dockerfile along with the required tasks and instructions.
the file with the following code:
FROM php:8.0.2-fpm
# Arguments defined in docker-compose.yml
ARG user
ARG uid
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
chown -R $user:$user /home/$user
# Set working directory
WORKDIR /var/www
USER $user
6. CONFIGURING PHP, NGINX & MYSQL
PHP
You can set up the PHP service to operate as a PHP processor for incoming requests from Nginx now that your infrastructure has been established in the docker-compose file.
The local.ini file must be created inside the php folder in order to configure PHP.
In Step 3, you bind-mounted this file to the container’s /usr/local/etc/php/conf.d/local.ini directory.
You can override the default php.ini file that PHP reads at startup by creating this file.
setting the PHP directory:
$ mkdir ~/laravel-app/php
Next, open the local.ini
file:
$ nano ~/laravel-app/php/local.ini
You’ll add the following code to set size restrictions for uploaded files as an example of how to setup PHP:
#add this to the file
upload_max_filesize=40M
post_max_size=40M
Save the file, then close your editor.
After creating your PHP local.ini file, you may proceed to set up Nginx.
NGINX
You can change the Nginx service to utilize PHP-FPM as the FastCGI server to serve dynamic content after configuring the PHP service.
You must create an app.conf file with the service configuration in the /laravel-app/nginx/conf.d/ folder in order to configure Nginx.
Make the nginx/conf.d/ directory first:
$ mkdir -p ~/laravel-app/nginx/conf.d
Next, create the app.conf
configuration file:
$ nano ~/laravel-app/nginx/conf.d/app.conf
Add the following code to the file to specify your 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;
}
}
When you’re done editing, save the file and close your editor.
Any modifications you make inside the nginx/conf.d/ subdirectory will instantly take effect inside the webserver container because of the bind mount you set in Step 3.
You will then examine and set up your MySQL settings.
MYSQL
You can enable MySQL to function as the database for your application once PHP and Nginx have been set up.
To configure MySQL, you will create the my.cnf
file in the mysql
folder. This is the file that you bind-mounted to /etc/mysql/my.cnf
inside the container in Step 2.
This bind mount allows you to override the my.cnf
settings as and when required.
First, create the mysql
directory:
$ mkdir ~/laravel-app/mysql
Next, make the my.cnf
file:
$ nano ~/laravel-app/mysql/my.cnf
In the file, 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
Save the file, then close your editor.
next, we move to containers.
7. Running the Containers and Changing Environment Parameters
You may now start the containers after defining all of your services in your docker-compose file and producing the configuration files for each.
Making a copy of the.env.example file that Laravel includes by default and renaming it to.env — the file Laravel expects to describe its environment — is the last step, though:
$ cp .env.example .env
You may now add specific information about your configuration to the.env file on the app container.
Open the file in nano or your preferred text editor:
$ nano .env
Locate the block that defines DB_CONNECTION and make the necessary changes to match your configuration. The following fields will be changed by you:
DB_HOST
will be yourdb
database container.DB_DATABASE
will be thelaravel
database.DB_USERNAME
will be the username you will use for your database. In this case, you will useteddi
.DB_PASSWORD
will be the secure password you would like to use for this user account.
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=teddi
DB_PASSWORD=your_laravel_db_password
Save your changes and exit your editor.
Running the Application with Docker Compose
To create the application image and launch the services we specified in our configuration, we’ll now utilize docker-compose instructions.
Use the command below to create the app image:
$ docker-compose build app
This command might take a few minutes to complete. You’ll see output similar to this:
Building app
Sending build context to Docker daemon 377.3kB
Step 1/11 : FROM php:7.4-fpm
---> 8c08d993542f
Step 2/11 : ARG user
---> e3ce3af04d87
Step 3/11 : ARG uid
---> 30cb921ef7df
Step 4/11 : RUN apt-get update && apt-get install -y git curl libpng-dev libonig-dev libxml2-dev zip unzip
. . .
---> b6dbc7a02e95
Step 5/11 : RUN apt-get clean && rm -rf /var/lib/apt/lists/*
---> 10ef9dde45ad
. . .
Step 6/11 : RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
. . .
---> 920e4f09ec75
Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
---> dbbcd44e44af
Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user
---> db98e899a69a
Step 9/11 : RUN mkdir -p /home/$user/.composer && chown -R $user:$user /home/$user
---> 5119e26ebfea
Step 10/11 : WORKDIR /var/www
---> 699c491611c0
Step 11/11 : USER $user
---> cf250fe8f1af
Successfully built cf250fe8f1af
Successfully tagged travellist:latest
When the build is finished, you can run the environment in background mode with:
$ docker-compose up -d
Output
Creating db ... done
Creating app ... done
Creating webserver... done
This will run your containers in the background. To show information about the state of your active services, run:
$ docker-compose ps
You should see outputs like this:
Name Command State Ports
-----------------------------------------------------------------------------------------------------------------
app docker-php-entrypoint php-fpm Up 9000/tcp
db docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp,:::3306->3306/tcp
webserver /docker-entrypoint.sh ngin ... Up 0.0.0.0:443->443/tcp,:::443->443/tcp,
0.0.0.0:80->80/tcp,:::80->80/tcp
Your environment is now operational, but we still need to perform a few tasks to complete the application setup.
To run tasks in the service containers, such as a ls -l to display comprehensive information about files in the application directory, use the docker-compose exec command:
$ docker-compose exec app ls -l
-rw-rw-r-- 1 james james 10452 Feb 10 01:22 CHANGELOG.md
-rw-rw-r-- 1 james james 739 Feb 10 01:46 Dockerfile
-rw-rw-r-- 1 james james 4158 Feb 10 01:22 README.md
drwxrwxr-x 7 james james 4096 Feb 10 01:22 app
-rwxrwxr-x 1 james james 1686 Feb 10 01:22 artisan
drwxrwxr-x 3 james james 4096 Feb 10 01:22 bootstrap
-rw-rw-r-- 1 james james 1817 Feb 10 01:22 composer.json
-rw-r--r-- 1 james james 294130 Feb 10 01:51 composer.lock
drwxrwxr-x 2 james james 4096 Feb 10 01:22 config
drwxrwxr-x 5 james james 4096 Feb 10 01:22 database
-rw-rw-r-- 1 james james 1254 Feb 10 01:29 docker-compose.yml
drwxrwxr-x 3 james james 4096 Feb 10 01:22 lang
drwxrwxr-x 2 james james 4096 Feb 10 01:27 mysql
drwxrwxr-x 3 james james 4096 Feb 10 01:26 nginx
-rw-rw-r-- 1 james james 286 Feb 10 01:22 package.json
drwxrwxr-x 2 james james 4096 Feb 10 01:26 php
-rw-rw-r-- 1 james james 1175 Feb 10 01:22 phpunit.xml
drwxrwxr-x 2 james james 4096 Feb 10 01:22 public
drwxrwxr-x 5 james james 4096 Feb 10 01:22 resources
drwxrwxr-x 2 james james 4096 Feb 10 01:22 routes
drwxrwxr-x 5 james james 4096 Feb 10 01:22 storage
drwxrwxr-x 4 james james 4096 Feb 10 01:22 tests
drwxr-xr-x 39 james james 4096 Feb 10 01:52 vendor
-rw-rw-r-- 1 james james 263 Feb 10 01:22 vite.config.js
now, check if you see anything like composer.lock in the outputs. it there isn’t any then you can move on with the next process.
but in this case, there is so we run this command to remove it:
$ docker-compose exec app rm -rf vendor composer.lock
We’ll now run composer install
to install the application dependencies:
$ docker-compose exec app composer install
You should see outputs like this:
Output
No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
. . .
Lock file operations: 89 installs, 0 updates, 0 removals
- Locking doctrine/inflector (2.0.4)
- Locking doctrine/instantiator (1.4.1)
- Locking doctrine/lexer (1.2.3)
- Locking dragonmantank/cron-expression (v2.3.1)
- Locking egulias/email-validator (2.1.25)
- Locking facade/flare-client-php (1.9.1)
- Locking facade/ignition (1.18.1)
- Locking facade/ignition-contracts (1.0.2)
- Locking fideloper/proxy (4.4.1)
- Locking filp/whoops (2.14.5)
. . .
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 89 installs, 0 updates, 0 removals
- Downloading doctrine/inflector (2.0.4)
- Downloading doctrine/lexer (1.2.3)
- Downloading dragonmantank/cron-expression (v2.3.1)
- Downloading symfony/polyfill-php80 (v1.25.0)
- Downloading symfony/polyfill-php72 (v1.25.0)
- Downloading symfony/polyfill-mbstring (v1.25.0)
- Downloading symfony/var-dumper (v4.4.39)
- Downloading symfony/deprecation-contracts (v2.5.1)
. . .
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
Before testing the application, the last step is to create a special application key using the artisan Laravel command-line tool.
Users’ sessions and other sensitive data are encrypted using this key:
$ docker-compose exec app php artisan key:generate
Output
Application key set successfully.
As a final step, visit http://your_server_ip in the browser. The home page for your Laravel application will look like this:
it works!!!!!! you don’t need to add any port to it because we already specified a port 80 in the docker-compose.yml file.
PRO TIPS:
It's better to use a virtual machine provided by any cloud provider like digital ocean.
you can open an account and learn how to create a VM HERE
Reminder
if you want a well-detailed video explanation of the zoom mastermind class.
Resources
- How To Install and Set Up Laravel with Docker Compose on Ubuntu 22.04
- How To Set Up Laravel, Nginx, and MySQL With Docker Compose on Ubuntu 20.04
- Initial Server Setup with Ubuntu 20.04
- How To Install and Use Docker on Ubuntu 20.04
- How To Install and Use Docker Compose on Ubuntu 20.04
NOTE: if you have any questions or want to add to this blog. you can message me through E-mail.
I reply faster to people that are subscribed to my newsletter!!
Conclusion
If you loved this blog post give it a like, comment, and don’t forget to click on the follow button.
And if you would love to get an update on the two interesting blogs I will be posting this week then you should sign up for my newsletter right here!!