launch
CLI::Wordpress Launch ImplementationDetails
wordpress-launch.yaml
On zef install https://github.com/p6steve/raku-CLI-Wordpress.git
, the file wordpress-launch.yaml is populated. It contains site-specific configuration and should be edited to match your domain name, etc before setup is run.
#~/.rawp-config/wordpress-launch.yaml
instance:
domain-name: furnival.net
admin-email: '[email protected]'
db-image: mysql:8.0
wordpress-image: wordpress:php8.0-fpm-alpine
webserver-image: nginx:1.15.12-alpine
certbot-image: certbot/certbot
wpcli-image: wordpress:cli-php8.0
Care should be taken to preserve any changes to this file in the event of a zef reinstall
rawp setup, launch & renewal
This sequence largely follows the guidance at viz. https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-docker-compose
The rawp setup
command positions the various configuration files in the ~/wordpress directory and populates them with info from wordpress-launch.yaml.
The rawp launch
command will then start a new Wordpress instance. It performs a 'staging' phase to check your kit for a no ssl website and, if OK, then fetches and installs a letsencrypt ssl certificate and restarts the website.
The rawp renewal
command will then configure the crontab for periodic renewal of the letsencrypt certificate.
Launch Details
###0. Prerequisites
server system instance
a server running Ubuntu 20.04, along with a non-root user with sudo privileges and an active firewall
docker installed on your server
docker-compose installed on your server
registered domain name, DNS records:
a record with your_domain -> IP
a record with www.your_domain -> IP
###1. Environment Variables
The rawp setup
command automatically setups config files following these steps.
These secrets are autogenerated during setup to avoid storing them in GitHub.
#sudo vi .env [~/.env]
MYSQL_ROOT_PASSWORD='xxx'
MYSQL_USER=yyy
MYSQL_PASSWORD='zzz'
Because your .env file contains sensitive information, you want to ensure that it is included in your project’s .gitignore and .dockerignore files.
#sudo vi .dockerignore [~/wordpress/.dockerignore]
.env
.git
.dockerignore
###2. Webserver Configuration
The rawp setup
command automatically setups config files following these steps.
Initially a no ssl config is used for staging...
#~/wordpress/nginx-conf/nginx.conf
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
index index.php index.html index.htm;
root /var/www/html;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress: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 ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off; access_log off;
}
location = /robots.txt {
log_not_found off; access_log off; allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
If OK, this is swapped to the ssl version & restarted...
#~/wordpress/nginx-conf/nginx.conf
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your_domain www.your_domain;
index index.php index.html index.htm;
root /var/www/html;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/furnival.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/furnival.net/privkey.pem;
include /etc/nginx/conf.d/options-ssl-nginx.conf;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# enable strict transport security only if you understand the implications
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress: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 ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off; access_log off;
}
location = /robots.txt {
log_not_found off; access_log off; allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
###3. Docker Compose Services
The rawp setup
command automatically setups config files following these steps.
The set of services is predefined by the docker-compose.yaml. The specific docker images used are set in the wordpress-launch.yaml.
#`/wordpress/docker-compose.yaml
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- '80:80'
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email $admin-email --agree-tos --no-eff-email --staging -d $domain-name -d www.$domain-name
wpcli:
container_name: wpcli
depends_on:
- wordpress
image: wordpress:cli
user: 1000:1000
command: tail -f /dev/null
volumes:
- wordpress:/var/www/html
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
profiles:
- dev
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
###4. SSL Certificates and Credentials
The rawp launch
command automatically setups SSL following these steps.
Start No SSL config for staging
sudo docker-compose up -d
sudo docker-compose ps
Output
Name Command State Ports
-------------------------------------------------------------------------
certbot certbot certonly --webroot ... Exit 0
db docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
wordpress docker-entrypoint.sh php-fpm Up 9000/tcp
Run certbot staging command
sudo docker-compose run certbot certonly --webroot --webroot-path=/var/www/html --email admin-email --agree-tos --no-eff-email -d your_domain -d www.your_domain --staging
Check dummy certificate is OK
sudo docker-compose exec webserver ls -la /etc/letsencrypt/live
Output
total 16
drwx------ 3 root root 4096 May 10 15:45 .
drwxr-xr-x 9 root root 4096 May 10 15:45 ..
-rw-r--r-- 1 root root 740 May 10 15:45 README
drwxr-xr-x 2 root root 4096 May 10 15:45 your_domain
Run cerbot production command
Now that you know your request will be successful, you can edit the certbot service definition to remove the --staging flag.
sudo docker-compose run certbot certonly --webroot --webroot-path=/var/www/html --email admin-email --agree-tos --no-eff-email -d your_domain -d www.your_domain --force-renewal
Note that letsencrypt restricts this to 5 calls per week.
The certbot image is then restarted:
sudo docker-compose up --force-recreate --no-deps certbot
###5. Modifying the Web Server Configuration and Service Definition
The rawp launch
command automatically setups SSL following these steps.
Enabling SSL in your Nginx configuration will involve adding an HTTP redirect to HTTPS, specifying your SSL certificate and key locations, and adding security parameters and headers.
Since you are going to recreate the webserver service to include these additions, you can stop it now: sudo docker-compose stop webserver
Before modifying the configuration file, get the recommended Nginx security parameter from Certbot using curl:
sudo curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
This command will save these parameters in a file called options-ssl-nginx.conf, located in the nginx-conf directory.
Next, the no SSL nginx configuration file is replaced by the SSL nginx configuration file (see section 2).
Recreate the webserver service: sudo docker-compose up -d --force-recreate --no-deps webserver
###6. Front End Setup
Now you can visit your new server forntend and perform the standard Wordpress configuration steps to select language and setup an admin user.
###7. Certificate Renewals
The rawp renewal
command automatically sets up letsencrypt SSL certificate renewal following these steps.
Letsencrypt certificates are valid for 90 days. This cron job will renew your certificates and reload your nginx configuration.
The renewal script is predefined at ~/wordpress/ssl_renew.sh:
#!/bin/bash
DOCKER="/usr/bin/docker"
COMPOSE="/usr/bin/docker-compose"
cd /home/ubuntu/wordpress
$DOCKER container prune -f
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
This script is loaded to the server instance crontab with the following lines:
0 12 * * * /home/ubuntu/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
0 */4 * * * date >> /var/log/cron.log 2>&1 #4 hourly timestamp
It is then run every 4 hours. You can monitor the status with tail -f /var/log/cron.log