Setting up a profile website and a WordPress blog with Docker compose

"I tryied so hard" for this 🥹​

Setting up a profile website and a WordPress blog with Docker compose
English v1

If you are creating your own personal website, take a look to this post 😉

We’ll see together how to create a secure website and serve a wordpress blog on the same domain.

1. The concept

Let’s take a look to the concept behind this method.

project-flow

We’ll use a Nginx server for redirect all api calls where we want.

2. The docker-compose.yaml

Let’s begin seeing what our YAML file for docker compose:

docker-compose.yaml
version: '3'
services:
reverse-proxy:
image: nginx:1.7
container_name: reverse_proxy
depends_on:
- profile_site
- blog
volumes:
- ./reverse_proxy/nginx.conf:/etc/nginx/nginx.conf
- ./ssl/example.com/:/ssl/private/example.com/
ports:
- "80:80"
- "443:443"
profile_site:
image: nginx:1.7
container_name: profile_site
volumes:
- ./profile_site/nginx.conf:/etc/nginx/nginx.conf
- ./profile_site/site/:/var/www/http/profile_site
ports:
- "8003:80"
- "8004:443"
db:
image: mysql:5.7
container_name: db
volumes:
- ./blog/db_data:/var/www/data/
command: --default-authentication-plugin=mysql_native_password --innodb-flush-method=O_DSYNC
environment:
MYSQL_ROOT_PASSWORD: db_password
MYSQL_DATABASE: blog
blog:
depends_on:
- db
container_name: blog
image: wordpress:latest
links:
- db:mysql
volumes:
- ./blog/wordpress_data:/var/www/html
- ./blog/apache_data/000-default.conf:/etc/apache2/sites-available/000-default.conf
- ./blog/apache_data/apache2.conf:/etc/apache2/apache2.conf
- ./blog/apache_data/ports.conf:/etc/apache2/ports.conf
- ./ssl/example.com/:/ssl/example.com/
ports:
- "8001:80"
- "8002:443"
environment:
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: db_password
WORDPRESS_DB_NAME: blog

To reduce the navigation complexity of the post, I divided everything into three sections, one for each post.

In each of these you will find the piece of the yaml file inherent and the explanation of the various configuration files

3. Reverse proxy container

YAML part

reverse-proxy.yaml
reverse-proxy:
image: nginx:1.7
container_name: reverse_proxy
depends_on:
- profile_site
- blog
volumes:
- ./reverse_proxy/nginx.conf:/etc/nginx/nginx.conf
- ./ssl/example.com/:/ssl/private/example.com/
ports:
- "80:80"
- "443:443"

For the reverse proxy container we’re using a nginx v1.7 server, linking 80 and 443 ports and settings volumes for configuration and ssl certificates.

nginx.conf

Let’s take a look to the basic nginx configuration for reverse proxy:

nginx.conf
http {
include /etc/nginx/mime.types;
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name example.com www.example.com;
ssl_certificate /ssl/private/example.com.crt;
ssl_certificate_key /ssl/private/example.com.key;
location / {
proxy_pass http://profile_site:443;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /blog {
location ~ \.css {
add_header Content-Type text/css;
proxy_pass http://blog:443$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ \.js {
add_header Content-Type application/x-javascript;
proxy_pass http://blog:443$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ \.php$ {
proxy_pass http://blog:443$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
proxy_pass http://blog:443;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
}

How are mapped:

  • the server catch any api call direct to https port on domain example.com and www.example.com
  • the location / (root) is set to re-route http calls to http://profile_site:443
  • the location /blog is set to re-route http calls to http://blog:443

The sublocations for css, js and php are for redirecting calls to wordpress static files in the right direction

Note: http://profile_site and http://blog are defined in docker network

It’s also present the part for redirect http request to https.

4. Personal site container

YAML part

personal_site.yaml
personal_site:
image: nginx:1.7
container_name: profile_site
volumes:
- ./profile_site/nginx.conf:/etc/nginx/nginx.conf
- ./profile_site/site/:/var/www/http/profile_site
ports:
- "8003:80"
- "8004:443"

The /var/www/http/profile_site will be the root folder for our website

nginx.conf

nginx.conf
http {
include /etc/nginx/mime.types;
#Personal site
server {
listen 443 default_server;
listen [::]:443 default_server;
server_name localhost;
root /var/www/http/profile_site;
location / {
location ~ \.css {
add_header Content-Type text/css;
}
location ~ \.js {
add_header Content-Type application/x-javascript;
}
index index.html;
try_files $uri $uri/ /index.html;
}
}
}

5. MySQL Container

YAML part

db.yaml
db:
image: mysql:5.7
container_name: db
volumes:
- ./blog/db_data:/var/www/data/
command: --default-authentication-plugin=mysql_native_password --innodb-flush-method=O_DSYNC
environment:
MYSQL_ROOT_PASSWORD: db_password
MYSQL_DATABASE: blog

Here we should consider a couple things:

  • The volume **./blog/db_data **should contain all data of our database to avoid to loosing them
  • The command –default-authentication-plugin=mysql_native_password –innodb-flush-method=O_DSYNC allow us to use password as authentication method

6. WordPress container

YAML part

blog.yaml
blog:
depends_on:
- db
container_name: blog
image: wordpress:latest
links:
- db:mysql
volumes:
- ./blog/wordpress_data:/var/www/html
- ./blog/apache_data/000-default.conf:/etc/apache2/sites-available/000-default.conf
- ./blog/apache_data/apache2.conf:/etc/apache2/apache2.conf
- ./blog/apache_data/ports.conf:/etc/apache2/ports.conf
- ./ssl/example.com/:/ssl/example.com/
ports:
- "8001:80"
- "8002:443"
environment:
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: db_password
WORDPRESS_DB_NAME: blog

This part is also very intuitive. The files for Apache inserted in the volumes we will see them shortly. I would like to make just a couple of clarifications:

  • The – db : mysql under links key it’s for assign an alias for db in the docker network. It might be useful to find the container of the database more easily
  • The volume ./blog/wordpress_data will contain all wordpress site automatically, to avoid to loose data

000-default.conf

000-default.conf
<VirtualHost *:443>
ServerAdmin [email protected]
DocumentRoot /var/www/html
Alias /blog /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

A really simple file, to set the server inside the wordpress container so that it exposes only port 443 to call up our blog

apache2.conf

apache2.conf
RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,QSA]

Just add these 3 lines to have your HTTP calls re-routed to HTTPS

Note: the Apache rewrite module is enabled by default in the wordpress docker image. If not, follow the steps below:

  • Execute docker exec -it blog_container_name bash from the host to enter in the container
  • Run a2enmod rewrite for enable rewrite module
  • Run systemctl restart httpd for reboot apache service

ports.conf

ports.conf
#Listen 80
#<IfModule ssl_module>
# Listen 443
#</IfModule>
#<IfModule mod_gnutls.c>
# Listen 443
#</IfModule>

Just comment all lines or delete them for unset other access points to apache server

That’s all!

You’ll find all the project files with some security features at this link: https://github.com/antoniodevivo/personal_site

I have tried to make this article as streamlined as possible, so that you can have a quick dynamic on how to set up your deployment environment.

I’m sure you will be able to find the answers to any doubts on Google 😉

If not, write to me in private.

Thanks for reading the article 😀