How this Blog was created !
About this tutorial
This article will provide you all the needed steps to create from scratch a public Website based on WordPress with security SSL enabled. It is based on my own experience with this Blog and I hope it will be helpful for those who need to troubleshoot their own installation.
Main Steps for this Blog creation
Step 1: Buy a virtual private Server
If you want to have your own blog on internet like me, first step will be to buy a small virtual private server in the cloud. There are many cloud providers (AWS, Microsoft Azure, Google Cloud, …). The one I chose for this Blog is OVH as it is the cheapest solution for my usage (6$ per month).
Note:
You can choose the location of your server by clicking on the World map icon:
Step 2: Buy a domain name
You will need also to buy a domain name which will allow people to visit your website. I also used OVH to buy my domain name for this blog. Just be careful with the name and the extension you will chose as the prices can vary a lot (from 10$ to 5000$ per year).
Step 3: Install a linux OS on your virtual private Server
It’s an easy task as it is done by your cloud provider during the configuration of your own virtual private server.
For this Blog, I chosed Ubuntu but you can use another OS system like Fedora, Centos or Debian depending on your own preferences.
Step 4: Install docker and docker-compose on your virtual server
The only software you will need to install and run on your server is Docker and Docker-compose.
Docker is an open platform for developing, shipping, and running applications very quickly inside linux containers.
Docker-Compose is an orchestrator which manages, scales and maintains containerized applications.
Please refer to the following official documentation for the installation of Docker :
- Ubuntu : https://docs.docker.com/engine/install/ubuntu/
- Debian : https://docs.docker.com/engine/install/debian/
- Centos : https://docs.docker.com/engine/install/centos/
- Fedora : https://docs.docker.com/engine/install/fedora/
The following example provides the linux commands to run to install docker and docker-compose tools on your Ubuntu OS :
# Setup Docker Repository
$ sudo apt update
$ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# Install Docker Engine
$ sudo apt update
$ sudo apt-get install docker-ce
$ sudo systemctl start docker
$ sudo systemctl enable docker
$ sudo systemctl status docker
$ docker -v
==> Docker version 20.10.10, build b485636
# installation docker-compose
$ sudo curl -L "https://github.com/docker/compose/releases/download/v2.0.1/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose version
==> Docker Compose version v2.0.1
# Allow Non-root users to run Docker Commands
$ sudo groupadd docker
$ sudo usermod -aG docker admin
Step 5 : Create nginx container
Nginx is a Web Server which can run as a proxy server to enable https connection between a client browser and your Web site. It needs SSL certificates to establish a secure encrypted connection. They can be generated automatically by Let’s Encrypt tool which is an open source, completely free Certificate Authority (CA). The SSL certificates are valid for 90 days and need to be renewed after this delay (renewal is also free).
The following schema shows how Nginx manage a secure connection with Let’s Encrypt :
To create a nginx container, you can follow the official nginx docker installation here :
In my case, I created my own nginx container in order to install both nginx and certbot tools in the same container.
You can download all the needed files for your website creation directly from GitHub :
After downloading all the needed files on your server, you should get the following tree :
# command to run from your private virtual server after downloading all the files
# (the root directory is /lab/projet01 but you can change it)
$ cd /lab/projet01
$ tree -L 5
+-- docker-code
¦ +-- nginx
¦ ¦ +-- 1.21
¦ ¦ +-- config
¦ ¦ ¦ +-- website-index.html
¦ ¦ ¦ +-- website-nginx-http.conf
¦ ¦ ¦ +-- website-nginx-https.conf
¦ ¦ ¦ +-- website-nginx-https-proxypass-wordpress.conf
¦ ¦ ¦ +-- website-sites-available.domain
¦ ¦ +-- Dockerfile
¦ ¦ +-- README.md
¦ ¦ +-- scripts
¦ ¦ ¦ +-- docker-create-user.sh
¦ ¦ ¦ +-- docker-entrypoint.sh
¦ ¦ ¦ +-- docker-healthcheck.sh
¦ ¦ ¦ +-- docker-install-software.sh
¦ ¦ +-- software
¦ +-- nginx-http-docker-compose.yml
¦ +-- nginx-https-docker-compose.yml
¦ +-- wordpress-mysql-docker-compose.yml
¦ +-- wordpress-mysql-nginx-docker-compose.yml
+-- docker-data
+-- data-mysql
¦ +-- myblog.fr
+-- data-nginx
¦ +-- myblog.fr
+-- data-shared
+-- data-wordpress
+-- myblog.fr
Files / Directories description :
- nginx-http-docker-compose.yml: compose file to start nginx container in unsecure http mode
- nginx-https-docker-compose.yml: compose file to start nginx container in secure http mode
- wordpress-mysql-docker-compose.yml: compose file to start wordpress in insecure mode
- wordpress-mysql-nginx-docker-compose.yml: docker file to start wordpress in secure mode
- Dockerfile: docker file to define the content of nginx container
- docker-create-user.sh: bash script to create a none-root user
- docker-entrypoint.sh: bash script to start the container
- docker-healthcheck.sh: bash script to check the heath of the container
- docker-install-software.sh: bash script to install nginx and certbot tools
- website-index.html: nginx html file to test the container
- website-nginx-http.conf: nginx config file
- website-sites-available.domain: nginx virtual host config file
- data-shared: directory to share data between your nginx container and the server
- data-mysql: directory to store mysql database
- data-nginx: directory to store nginx data
- data-wordpress: directory to store wordpress data
Here is the content of the nginx-http-docker-compose.yml
version: '3.8'
services:
# ----------------------------------------------
# nginx 1.21 : container creation
# ----------------------------------------------
nginx:
build:
context: nginx/1.21
args:
USER_UID: 1010
GROUP_GID: 1010
UNAME: 'webadmin'
image: lab/nginx:1.21
hostname: nginx
container_name: 'nginx'
environment:
NGINX_SETUP: 'http'
NGINX_WEBSITE: 'myblog.fr'
DOCKER_DEBUG_FLAG: 'no'
healthcheck:
test: ['CMD', '/opt/nginx/scripts/docker-healthcheck.sh']
timeout: 5s
interval: 30s
start_period: 10s
retries: 3
tty: true
networks:
- lab
restart: 'always'
ports:
- '80:80'
volumes:
- volume-data-shared:/opt/projet01/data-shared
- /lab/projet01/docker-data/data-nginx/myblog.fr:/var/www/html
# ----------------------------------------------
# network definition
# ----------------------------------------------
networks:
lab:
driver: bridge
# ----------------------------------------------
# volume creation for persistant data
# ----------------------------------------------
volumes:
volume-data-shared:
driver: local
driver_opts:
o: bind
type: none
device: /lab/projet01/docker-data/data-shared
To create and start your nginx container, update first the above compose file and change the variable “NGINX_WEBSITE” to point to your own domain name. Then you will have to run the following docker commands :
# commands to run from the directory you created on your own server :
# start nginx container listening to port 80
$ cd /lab/projet01/docker-code
$ docker-compose -f nginx-http-docker-compose.yml up -d --build
# check that your nginx is working (status=healthy)
$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
-------------------------------------------------------------------------------------
c321637b40c6 lab/nginx:1.21 Up 4 hours (healthy) 0.0.0.0:80->80/tcp,... nginx
# another check to confirm that you can run commands inside your nginx container
$ docker exec -it nginx bash
$ sudo netstat -a|grep 80
Proto Recv-Q Send-Q Local Address Foreign Address State
--------------------------------------------------------------------------
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp6 0 0 [::]:80 [::]:* LISTEN
If the above checks confirm that your nginx container is running correctly, next step will be to update your domain name to point to the IP address of your virtual private server. If you are using OVH cloud provider, you can set it up by going to your OVH Dashboard, clicking on your domain name and updating the DNS zone (A rows) as follow :
You can now test that your own website can be reached everywhere by opening a browser with the following URL based on the domain name you bought :
- http://myblog.fr
You should get the following screen :
Step 6 : Setup Let’s Encrypt (https) with nginx
To switch your existing none secure HTTP Web site (ex: http://myblog.fr) to a secure HTTPS website (ex: https://myblog.fr), the easiest way is to use Certbot tool which will fetch automatically the needed SSL certificates from Let’s Encrypt. As a reminder, Let’s Encrypt is a non-profit certificate authority that provides X. 509 certificates for Transport Layer Security (TLS) encryption at no charge.
To run successfully certbot, you will need :
- Your HTTP website online with an open port 80
- your Domain name pointing to your Virtual Private Server
- An access to the nginx container command line
- The ability to run sudo command
If all these prerequisites are met, you will be able to implement https as follow :
# commands to run from your private virtual server :
$ docker exec -it nginx bash
$ sudo certbot --nginx -d myblog.fr -d www.myblog.fr --rsa-key-size 4096 --agree-tos --no-eff-email
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): youremail@gmail.com
Account registered.
Requesting a certificate for myblog.fr and www.myblog.fr
Performing the following challenges:
http-01 challenge for myblog.fr
http-01 challenge for www.myblog.fr
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/nginx.conf
Deploying Certificate to VirtualHost /etc/nginx/nginx.conf
Redirecting all traffic on port 80 to ssl in /etc/nginx/nginx.conf
Redirecting all traffic on port 80 to ssl in /etc/nginx/nginx.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://myblog.fr and
L’info selon Myblog
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/myblog.fr/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/myblog.fr/privkey.pem
Your certificate will expire on 2022-02-16. To obtain a new or
tweaked version of this certificate in the future, simply run
certbot again with the "certonly" option. To non-interactively
renew *all* of your certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
# Copy SSL certificates to nginx "config" directory
$ cp /etc/nginx/nginx.conf /opt/projet01/data-shared
$ sudo cp -r /etc/letsencrypt /opt/projet01/data-shared
$ exit
$ cd /lab/projet01/docker-data/data-shared
$ sudo tar cvzf letsencrypt-myblog-fr.tar.gz letsencrypt
$ cp letsencrypt-myblog-fr.tar.gz /lab/projet01/docker-code/nginx/1.21/config/
$ cp nginx.conf /lab/projet01/docker-code/nginx/1.21/config/
Note:
To avoid permission issues during copy commands, it is recommended to map the container’s user/group to an existing user/group on your server. This can be done by creating a specific user on your server and to update your docker-compose files accordingly with the following arguments:
- USER_UID
- GROUP_GID
- UNAME
To create a user (ex: webadmin, UID=1010, GID=1010) on your Ubuntu or Debian server, you can use the following commands :
# commands to run on your virtual private server to create a user
# (on Centos, use "useradd" command instead of "adduser")
$ sudo groupadd -g 1010 webadmin
$ cat /etc/group|grep 1010
$ sudo adduser --gid 1010 --uid 1010 webadmin
# check that the user has been created
$ cat /etc/passwd|grep 1010
==> webadmin:x:1010:1010:webadmin,,,:/home/webadmin:/bin/bash
To enable https for your web site, you just need to stop the current nginx container listening to port 80 and to restart it with the needed SSL certificates and with the opened port 443. This can be done by using the following docker-compose file “nginx-https-docker-compose.yml”
version: '3.8'
services:
# ----------------------------------------------
# nginx 1.21 : container creation
# ----------------------------------------------
nginx:
build:
context: nginx/1.21
args:
USER_UID: 1010
GROUP_GID: 1010
UNAME: 'webadmin'
image: lab/nginx:1.21
hostname: nginx
container_name: 'nginx'
environment:
NGINX_SETUP: 'https'
NGINX_WEBSITE: 'myblog.fr'
DOCKER_DEBUG_FLAG: 'no'
healthcheck:
test: ['CMD', '/opt/nginx/scripts/docker-healthcheck.sh']
timeout: 5s
interval: 30s
start_period: 10s
retries: 3
tty: true
networks:
- lab
restart: 'always'
ports:
- '443:443'
volumes:
- volume-data-shared:/opt/projet01/data-shared
- /lab/projet01/docker-data/data-nginx/myblog.fr:/var/www/html
# ----------------------------------------------
# networks
# ----------------------------------------------
networks:
lab:
driver: bridge
# ----------------------------------------------
# volume creation for persistent data
# ----------------------------------------------
volumes:
volume-data-shared:
driver: local
driver_opts:
o: bind
type: none
device: /lab/projet01/docker-data/data-shared
Here are the commands to restart your nginx container in secure https mode :
# commands to run from the directory you created on your own private virtual server :
# stop previous running nginx container listening to port 80
$ cd /lab/projet01/docker-code
$ docker-compose -f nginx-http-docker-compose.yml down
# start nginx container listening to port 443
$ docker-compose -f nginx-https-docker-compose.yml up -d --build
# check that nginx is listening to port 443
$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
-------------------------------------------------------------------------
6d981412e8be lab/nginx:1.21 healthy 0.0.0.0:443->443/tcp,... nginx
$ docker exec -it nginx bash
$ netstat -a|grep 443
Proto Recv-Q Send-Q Local Address Foreign Address State
--------------------------------------------------------------------------
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN
tcp6 0 0 [::]:443 [::]:* LISTEN
After this last command, you can test that your Web site can be reached from https via the following URL based on the domain name you bought :
- https://myblog.fr
You can also test the security of your Web Site by using a free online analysis tool like “SSL Labs” :
Note:
If you just need to create a static web site, you can stop here and add all your html pages in the following shared nginx directory:
- /lab/projet01/docker-data/data-nginx/myblog.fr
Step 7 Install Mysql + WordPress
An easy way to install mysql and wordpress docker containers is to use the official wordpress docker-compose file :
Here is the content of the compose file “‘wordpress-mysql-docker-compose.yml” you can use it to create your own mysql and wordpress containers :
version: '3.8'
services:
# ----------------------------------------------
# mysql 5.7 : container creation
# ----------------------------------------------
mysql:
image: mysql:5.7
hostname: mysql
container_name: 'mysql'
environment:
MYSQL_ROOT_PASSWORD: passwordtochange01
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: passwordtochange02
links:
- debian
depends_on:
- debian
tty: true
networks:
- lab
restart: 'always'
ports:
- '3306:3306'
volumes:
- /lab/projet01/docker-data/data-shared:/opt
# ----------------------------------------------
# wordpress 5 : container creation
# ----------------------------------------------
wordpress:
image: wordpress:latest
hostname: wordpress
container_name: 'wordpress'
links:
- mysql
depends_on:
- mysql
tty: true
networks:
- lab
restart: always
ports:
- '8000:80'
environment:
WORDPRESS_DB_HOST: mysql:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: passwordtochange02
WORDPRESS_DB_NAME: wordpress
volumes:
- /lab/projet01/docker-data/data-wordpress/myblogfr:/var/www/html
- /lab/projet01/docker-data/data-shared:/opt
# ----------------------------------------------
# networks
# ----------------------------------------------
networks:
lab:
driver: bridge
Notes :
- I have customized the official docker compose file by adding some permanent volumes. This will allow you to quickly backup your own website by just saving the content of these volumes.
- you can do more customizations by using your own Dockerfile and docker-entrepoint.sh files for wordpress and mysql containers. You can get the official content of these files on the wordpress docker hub by clicking on the version of the wanted version of wordpress.
- if needed, you can also add a phpmyadmin container to manage your mysql database. this can be done by using the official docker compose file you can get here : https://hub.docker.com/r/phpmyadmin/phpmyadmin/
Step 8 Start WordPress
First step is to create and start the mysql and wordpress containers without your secure nginx container in order to check that they are working alone correctly :
# commands to run from the directory you created on your own server :
# creation of permanent volumes for mysql and wordpress
# (change myblogfr directory name by your own domain name)
$ cd /lab
$ mkdir -p projet01/docker-data/data-mysql/db
$ mkdir -p projet01/docker-data/data-wordpress/myblogfr
# start containers in insecure mode
$ cd /lab/projet01/docker-code
$ docker-compose -f wordpress-mysql-docker-compose.yml up -d --build
# check that your wordpress and mysql are working
$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
-------------------------------------------------------------------------------------
bbf5cfa68e03 wordpress:latest Up 11 minutes 0.0.0.0:8000->80/tcp,... wordpress
45efb23e3910 mysql:5.7 Up 11 minutes 0.0.0.0:3306->3306/tcp,... mysql
To ensure that mysql and wordpress are running successfully, another check is to open a browser with your own domain name on port 8000 :
You should get the following wordpress setup screen :
Next step is to link your nginx container to your wordpress container in a secure way. This can be done by creating a dedicated nginx config file “website-nginx-https-proxypass-wordpress.conf” with proxypass feature as follow :
events {
worker_connections 4096; ## Default: 1024
}
http {
server {
server_name XXX www.XXX;
# location / {
# root /var/www/html;
# index index.html index.htm;
# }
index index.php index.html index.htm;
location / {
proxy_pass http://wordpress;
proxy_redirect off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_read_timeout 86400;
proxy_buffering off;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/XXX/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/XXX/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.XXX) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = XXX) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name XXX www.XXX;
return 404; # managed by Certbot
}}
Last step is to merge all your docker compose files in one single docker-compose.yml file as follow
version: '3.8'
services:
# ----------------------------------------------
# mysql 5.7 : container creation
# ----------------------------------------------
mysql:
image: mysql:5.7
hostname: mysql
container_name: 'mysql'
environment:
MYSQL_ROOT_PASSWORD: passwordtochange01
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: passwordtochange02
links:
- debian
depends_on:
- debian
tty: true
networks:
- lab
restart: 'always'
volumes:
- /lab/projet01/docker-data/data-mysql/db:/var/lib/mysql
- volume-data-shared:/opt
# ----------------------------------------------
# wordpress 5 : container creation
# ----------------------------------------------
wordpress:
image: wordpress:latest
hostname: wordpress
container_name: 'wordpress'
links:
- mysql
depends_on:
- mysql
tty: true
networks:
- lab
restart: always
environment:
WORDPRESS_DB_HOST: mysql:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: passwordtochange02
WORDPRESS_DB_NAME: wordpress
volumes:
- /lab/projet01/docker-data/data-wordpress/myblogfr:/var/www/html
- volume-data-shared:/opt
# ----------------------------------------------
# nginx 1.21 : container creation
# ----------------------------------------------
nginx:
build:
context: nginx/1.21
args:
USER_UID: 1010
GROUP_GID: 1010
UNAME: 'webadmin'
image: lab/nginx:1.21
hostname: nginx
container_name: 'nginx'
environment:
NGINX_SETUP: 'proxypass'
NGINX_WEBSITE: 'myblog.fr'
DOCKER_DEBUG_FLAG: 'no'
healthcheck:
test: ['CMD', '/opt/nginx/scripts/docker-healthcheck.sh']
timeout: 5s
interval: 30s
start_period: 10s
retries: 3
tty: true
links:
- wordpress
- debian
networks:
- lab
restart: 'always'
ports:
- '443:443'
volumes:
- volume-data-shared:/opt/projet01/data-shared
# ----------------------------------------------
# networks
# ----------------------------------------------
networks:
lab:
driver: bridge
# ----------------------------------------------
# volume creation for persistent data
# ----------------------------------------------
volumes:
volume-data-shared:
driver: local
driver_opts:
o: bind
type: none
device: /lab/projet01/docker-data/data-shared
Notes about the above docker compose file :
- The insecure ports directive has been removed from both wordpress and mysql containers definition
- Don’t forget to change the NGINX_WEBSITE variable to your own domain name
- The NGINX_SETUP is set to proxypass to link nginx and wordpress containers
To start wordpress in a securely manner, you just need to run the following linux commands :
# commands to run from the directory you created on your own server :
# stop previous containers
docker-compose -f wordpress-mysql-docker-compose.yml down
docker-compose -f nginx-https-docker-compose.yml down
# start all containers in secure mode
$ cd /lab/projet01/docker-code
$ docker-compose -f docker-compose.yml up -d --build
# check that all your containers are working
$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
-------------------------------------------------------------------------------------
bbf5cfa68e03 wordpress:latest Up 11 minutes 80/tcp wordpress
45efb23e3910 mysql:5.7 Up 11 minutes 3306/tcp mysql
b14a7b5267f7 lab/nginx:1.21 healthy 0.0.0.0:443->443/tcp, ... nginx
You should now be able to start your website based on wordpress in a secure mode https :
You should get the following wordpress setup screen :
It’s the last step: You just need to click on the continue button and setup now your own secure worpress website !
Conclusion
That’s all Folks !
This installation is quite basic. you can go further by adding a local inventory to store all your docker images. You can also add a phpmyadmin container or customize your wordpress/mysql containers.
So, If you need to add some more details, please feel free to comment this article…
Enjoy !