Note: It is recommended to visit my previous articles about Docker and Bash Scripting for a better understanding of this task.
Agenda
In this article, we are going to see how we can easily set up a full fledge LEMP (Linux, Apache, Mysql, PHP) stack WordPress site in a local machine or browser.
It will be a LEMP stack each one running in separate docker containers.
We are going to write a Bash shell script to make the task easy for the end user, the user will be able to run the LEMP stack with just one command.
This bash script will contain the following features,
Check if Docker and Docker-compose are installed on your machine or not. If not installed, install it.
Create a /etc/hosts entry for 'site_name' (provided as argument) pointing to localhost.
Create required files (eg. Docker-compose.yml, Nginx/default.conf, public/index.php, etc)
Create a WordPress site using the latest WordPress Version. This will be a LEMP (Linux, Nginx, Mysql, PHP) stack running inside Docker containers.
perform subcommands to, stop/start, deleting containers.
Writing The Script
creating files
To create the script firstly, we have to create a file with any name for ex, script.sh (it should have .sh extention to be able to get executed as bash script)
touch script.sh
# use any editor of your choice
nano script.sh
Checking Docker
After opening the file, let's start witing our script. Initially, we have to check if docker and docker-compose are installed or not and if not present, install them.
#!/bin/bash
# Check if docker-compose is installed
if ! [ -x "$(command -v docker-compose)" ]; then
# Install docker-compose if not present
echo "Installing Docker Compose..."
apt-get update
apt-get install -y docker-compose
fi
This snippet of code will check for the above condition, here first we have to write #!/bin/bash.
Making Entry in /etc/hosts
To make an entry of the site name in host file, firstly we have to check if the site name is provided as an argument or not. if not, make the user do it.
# Check if site name argument was provided
if [ -z "$1" ]; then
echo "Please provide a site name as an argument."
exit 1
fi
# Entry in /etc/hosts
site_name="$1"
echo "127.0.0.1:8000 $site_name" >> /etc/hosts
the above code snippet will perform the check and entry in the hosts' file.
Creating files
There are some required files for this LEMP stack to be operable and live, to create this either you can do it manually or just add it to the script and it will do it for you. I am going to add these to my script but you can do what you wish.
#creating required files
mkdir wordpress-docker
cd wordpress-docker
# Creating public and nginx
echo "Creating nginx configuration file"
mkdir public nginx
cd nginx
nano default.conf
Nginx config file
events {}
http{
server {
listen 80;
server_name $host;
root /usr/share/nginx/html;
index index.php index.html index.html;
location / {
try_files $uri $uri/ /index.php?$is_args$args;
}
location ~ \.php$ {
# try_files $uri =404;
# fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
PHP file
This PHP file just provides info about the page, you can add your own PHP file here if you want.
<?php
phpinfo();
Docker Compose file
This is the quite long Docker-compose file that I have written it took me around 1 hour to do so but in the end, it is worth it.
services:
#databse
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: password
networks:
- wpsite
#php
phpfpm:
image: php:fpm
depends_on:
- db
ports:
- '9000:9000'
volumes: ['./public:/usr/share/nginx/html']
networks:
- wpsite
#phpmyadmin
phpmyadmin:
depends_on:
- db
image: phpmyadmin/phpmyadmin
restart: always
ports:
- '8080:80'
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: password
networks:
- wpsite
#wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
restart: always
ports:
- '8000:80'
volumes: ['./:/var/www/html']
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
networks:
- wpsite
#nginx
proxy:
image: nginx:1.17.10
depends_on:
- db
- wordpress
- phpmyadmin
- phpfpm
ports:
- '8001:80'
volumes:
- ./:/var/www/html
- ./nginx/default.conf:/etc/nginx/nginx.conf
networks:
- wpsite
networks:
wpsite:
volumes:
db_data:
This Docker Compose file is for setting up a WordPress website with a MySQL database, PHP, PHPMyAdmin, Nginx, and WordPress itself.
Let's go through it section by section,
Services: This is where we define the services that we want to run as part of our application.
Database: This service defines the MySQL database that we will be using to store our website data. We specify the version of the MySQL image we want to use, and we also set up some environment variables to create a new database, user, and password. We also set up a volume to store the data in the
/var/lib/mysql
directory. This allows us to persist our database data even if the container is destroyed. We also set therestart
policy toalways
, which means that if the container crashes or is stopped, it will be automatically restarted.PHP: This service sets up the PHP-FPM container, which is a fast and efficient way to run PHP scripts. We specify the version of the PHP-FPM image we want to use, and we also set up a dependency on the
db
service, since our PHP scripts will need to connect to the MySQL database. We expose port 9000 so that other services can communicate with PHP-FPM. We also set up a volume to mount our./public
directory into the container so that PHP scripts can be served from there.PHPMyAdmin: This service sets up PHPMyAdmin, which is a web-based database administration tool for MySQL. We specify a dependency on the
db
service since we need to connect to the MySQL database. We expose port 8080 so that we can access PHPMyAdmin in our web browser. We also set up an environment variable to specify the database host and root password.WordPress: This service sets up the WordPress container, which is where our website will run. We specify a dependency on the
db
service since WordPress needs to connect to the MySQL database. We expose port 8000 so that we can access our website in our web browser.Nginx: Here is the configuration of nginx server which depends on all the above services. This server runs on 8001 of the local system and 80 of the container.
Adding Sub commands
# Adding subcommands to enbale/disable
if [ "$2" == "enable" ]; then
docker-compose strat
elif [ "$2" == "disable" ]; then
docker-compose stop
fi
# Adding subcommands to delete site
if [ "$2" == "delete" ]; then
docker-compose down -v
#removing hosts entry
sed -i "/$site_name/d" /etc/hosts
#removing all local files
rm -rf ./
fi
These subcommands will help users to perform other actions easily.
Final Result
The final script will look something like this,
#!/bin/bash
# Check if docker-compose is installed
if ! [ -x "$(command -v docker-compose)" ]; then
# Install docker-compose if not present
echo "Installing Docker Compose..."
apt-get update
apt-get install -y docker-compose
fi
# Check if site name argument was provided
if [ -z "$1" ]; then
echo "Please provide a site name as an argument."
exit 1
fi
# Entry in /etc/hosts
site_name="$1"
echo "127.0.0.1:8000 $site_name" >> /etc/hosts
#creating required files
mkdir wordpress-docker
cd wordpress-docker
# Creating public and nginx
echo "Creating nginx configuration file"
mkdir public nginx
cd nginx
cat > default.conf << EOF
events {}
http{
server {
listen 80;
server_name $host;
root /usr/share/nginx/html;
index index.php index.html index.html;
location / {
try_files $uri $uri/ /index.php?$is_args$args;
}
location ~ \.php$ {
# try_files $uri =404;
# fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
EOF
echo "Done"
echo "Creating index.php file in public"
cd ..
cd public
cat > index.php << EOF
<?php
phpinfo();
EOF
echo "Done"
cd ..
echo "Creating docker-compose file ..."
cat > docker-compose.yml << EOF
version: '3'
services:
#databse
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: password
networks:
- wpsite
#php-fpm
phpfpm:
image: php:fpm
depends_on:
- db
ports:
- '9000:9000'
volumes: ['./public:/usr/share/nginx/html']
networks:
- wpsite
#phpmyadmin
phpmyadmin:
depends_on:
- db
image: phpmyadmin/phpmyadmin
restart: always
ports:
- '8080:80'
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: password
networks:
- wpsite
#wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
restart: always
ports:
- '8000:80'
volumes: ['./:/var/www/html']
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
networks:
- wpsite
#nginx
proxy:
image: nginx:1.17.10
depends_on:
- db
- wordpress
- phpmyadmin
- phpfpm
ports:
- '8001:80'
volumes:
- ./:/var/www/html
- ./nginx/default.conf:/etc/nginx/nginx.conf
networks:
- wpsite
networks:
wpsite:
volumes:
db_data:
EOF
echo "Done"
fuser -k 8000/tcp 9000/tcp 8081/tcp 8080/tcp
echo "Creating LEMP stck in docker for wordpress"
docker-compose up -d
echo "Servers created"
# prompting user to open site in browser
echo "Site is up and healthy. Open $site_name in any browser to view it."
echo "Or click on the link -> http://localhost:8000"
# Adding subcommands to enbale/disable
if [ "$2" == "enable" ]; then
docker-compose strat
elif [ "$2" == "disable" ]; then
docker-compose stop
fi
# Adding subcommands to delete site
if [ "$2" == "delete" ]; then
docker-compose down -v
#removing hosts entry
sed -i "/$site_name/d" /etc/hosts
#removing all local files
rm -rf ./
fi
How to run this script
To run any script, we have to make it executable first. We can do this by running the following command in terminal
chmod +x script.sh
After making the script executable, we can run the script for the above operations to perform using the following command,
Note: It is MANDATORY to run the script as an administrator or superuser
sudo ./script.sh SITE_NAME
Note: Here you have to provide the site name as an argument.
By running the script, it will perform the above-mentioned task. It creates 5 different docker containers running the LEMP stack (with phpmyadmin). If everything goes well, you can visit your WordPress site by opening it in the browser using either site_name or Localhost.
Running Subcommands
There are some sub-commands available in the script to perform operations like stopping, starting/restarting, and deleting the containers. To run the sub-commands you need to add it to the main command as an argument for example,
- To start/Restart the containers,
./script.sh SITE_NAME enable
- To Stop the containers,
./script.sh SITE_NAME disable
- To Delete the containers,
./script.sh SITE_NAME delete
Installing WordPress at localhost
After opening the site in a browser, you can see the WordPress installation page with a form requiring your information and login credentials. Fill out the form and create the credentials and note them down for further use. Click on the Install WordPress button at the bottom. Log in with your credentials and you are ready with your WordPress site.
Error handling
The script is already written so that there will be no room for error while execution but, we can't guarantee that the error will not occur. In case any error occurs, you can look at the logs of all the containers and get rid of the error course manually by running,
docker-compose logs CONTAINER_NAME
There are more chances that the error can occur due to the ports on which we are running our stack being already in use. Although I have added a command in the script that makes the required ports free, there can be some services which can restart at the same port. To avoid this error, check for the services running on the following ports 8000, 8080, 9000, 3306, and 8081. If you found any service on one of these ports please try to stop them or you can change the script according to your required ports.
Conclusion & Results
With the above bash script, we can create a sample WordPress site running inside Docker containers. the script itself creates the required files and folders like docker-compose.yml, nginx config files, etc.