WordPress on Docker, with phpMyAdmin, SSL (via Traefik) and automatic updates
This was a very helpful for me to understanding trying to reverse engineer to make my own.
Original link is below. Tried my best to COPY/PASTE the HTML code cutting out certain bits. This is for me to reference.
- Docker, a powerful and standardized way to deploy applications
- Free SSL certificates from Let’s Encrypt (via Traefik)
- phpMyAdmin to easily manage your databases
- Automatic container updates (via Watchtower)
If you’ve got your own server already — whether at Bytemark or not — skip the Create a Cloud Server section and run our setup script on your server instead.
If you’re looking for recommended deployment practices and what’s happening under the hood, skip down to Look a bit deeper.
Create a Cloud Server
- Login to the Bytemark Panel (or start a free trial).
- Add a Cloud Server with these settings:
- Name: Give your server a name (eg, “wordpress”)
- Group: Leave as “default”
- Resources: 1 Core, 1GiB Memory
- Operating System: Debian 9
- Discs: 25GiB SSD storage
- Backup Schedule: Leave enabled (recommended)
- Boot options: Select Add script and paste this inside:
Setup Script
#!/bin/shexport DEBIAN_FRONTEND=noninteractive# Wait for apt-get to be available.while ! apt-get -qq check; do sleep 1s; done# Install docker-ce and docker-compose.apt-get updateapt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-commoncurl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian `lsb_release -cs` stable"apt-get updateapt-get install -y docker-cecurl -fsSL https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-composechmod +x /usr/local/bin/docker-compose# Check for security updates every night and install them.apt-get install -y unattended-upgrades# Retrieve configuration files. Lots of explanatory comments inside!# If you'd rather inspect and install these files yourself, see:mkdir -p /root/composecurl -fsSL https://raw.githubusercontent.com/BytemarkHosting/configs-wordpress-docker/master/docker-compose.yml -o /root/compose/docker-compose.ymlcurl -fsSL https://raw.githubusercontent.com/BytemarkHosting/configs-wordpress-docker/master/.env -o /root/compose/.envcurl -fsSL https://raw.githubusercontent.com/BytemarkHosting/configs-wordpress-docker/master/traefik.toml -o /root/compose/traefik.tomlcurl -fsSL https://raw.githubusercontent.com/BytemarkHosting/configs-wordpress-docker/master/php.ini -o /root/compose/php.ini# Traefik needs a file to store SSL/TLS keys and certificates.touch /root/compose/acme.jsonchmod 0600 /root/compose/acme.json# Use the hostname of the server as the main domain.sed -i -e "s|^TRAEFIK_DOMAINS=.*|TRAEFIK_DOMAINS=`hostname -f`|" /root/compose/.envsed -i -e "s|^WORDPRESS_DOMAINS=.*|WORDPRESS_DOMAINS=`hostname -f`|" /root/compose/.env# Fill /root/compose/.env with some randomly generated passwords.sed -i -e "s|^WORDPRESS_DB_ROOT_PASSWORD=.*|WORDPRESS_DB_ROOT_PASSWORD=`cat /dev/urandom | tr -dc '[:alnum:]' | head -c14`|" /root/compose/.envsed -i -e "s|^WORDPRESS_DB_PASSWORD=.*|WORDPRESS_DB_PASSWORD=`cat /dev/urandom | tr -dc '[:alnum:]' | head -c14`|" /root/compose/.envapt-get install -y apache2-utilsBASIC_AUTH_PASSWORD="`cat /dev/urandom | tr -dc '[:alnum:]' | head -c10`"BASIC_AUTH="`printf '%s\n' "$BASIC_AUTH_PASSWORD" | tee /root/compose/auth-password.txt | htpasswd -in admin`"sed -i -e "s|^BASIC_AUTH=.*|BASIC_AUTH=$BASIC_AUTH|" /root/compose/.env# Start our containers.cd /root/composedocker-compose up -d |
- Have a cup of tea! Your WordPress site will be ready in about 5 minutes.
- The Panel will tell you the root password for your server. Save it!
- Click on the Console button next to your Cloud Server. You’ll know installation has finished when you see a login prompt. You can login with username root.
After installation
Now you can browse to your new WordPress website at the hostname of your server (eg, http://name.of.server.uk0.bigv.io) and start writing content! If you want to use your own domain, it’s easier to configure that first before going through the WordPress setup wizard.
 and automatic updates - Documentation _ Bytemark_files/wordpress-language.png)
If you see a Bad Gateway message, wait a few seconds for the database to initialize and then refresh your page.
Read on if you want to Allow WordPress to send email, Use your own domain, Enable SSL/TLS or Access phpMyAdmin.
Allow WordPress to send email
We’re running a container to handle outgoing email. WordPress has access to this via SMTP at the hostname mail and port 25. Follow these steps to enable it:
- Login to your WordPress admin dashboard.
- Go to Plugins, click Add New, search for WP Mail SMTP by WPForms and click Install Now.
- Once it’s finished installing, click Activate to enable the plugin.
- Click Settings to configure the plugin.
- Fill in the From Email (eg,
no-reply@example.com) and the From Name (eg,Jane Doe). - In the Mailer section, select Other SMTP.
- Set the SMTP Host to mail and the SMTP Port to 25.
- Click Save Settings.
 and automatic updates - Documentation _ Bytemark_files/wordpress-othersmtp.png)
Use your own domain
If you’ve already gone through the WordPress setup wizard, before you take the next steps: login to your WordPress dashboard, go to Settings and change the WordPress Address and Site Address to the new domain. After you click `Save`, you’ll get a 404 message but that’s expected until you do the next steps.
Login to your Cloud Server and open /root/compose/.env in a text editor:
nano /root/compose/.env |
Change WORDPRESS_DOMAINS to your own domain. For example:
WORDPRESS_DOMAINS=my-brilliant-site.com,www.my-brilliant-site.com |
Do the same for TRAEFIK_DOMAINS if you want to access the Traefik dashboard.
Restart your Docker containers to apply the change:
cd /root/composedocker-compose downdocker-compose up -d |
Enable SSL/TLS
Configure your own domain as per the previous step. All domains you list in WORDPRESS_DOMAINS must point to your server (via DNS records) for this to work.
Once you’ve done that, Traefik will generate Let’s Encrypt SSL certificates for you automatically! Browse to https://your_domain.com to see if it worked.
If you want to redirect all HTTP traffic to HTTPS (as is recommended these days), open /root/compose/traefik.toml in a text editor and uncomment two lines so that it looks like this:
[entryPoints] [entryPoints.http] address = ":80" # Uncomment the following two lines to redirect HTTP to HTTPS. [entryPoints.http.redirect] entryPoint = "https" |
Open /root/compose/docker-compose.yml in a text editor. Under the wp: section, uncomment the bottom line so that it looks like this:
# Uncomment the next line to enable HSTS header.- "traefik.frontend.headers.STSSeconds=15768000" |
Restart your Docker containers to apply the change:
cd /root/composedocker-compose downdocker-compose up -d |
Access phpMyAdmin
The setup script generated a password and saved it inside /root/auth-password.txt on your server. Look inside to see what the browser authentication password is for the admin user:
cat /root/compose/auth-password.txt |
Go to http://name.of.server.uk0.bigv.io/phpmyadmin/ in your browser. The last forward slash is important! Login with username admin.
If that works, you’ll see this:
 and automatic updates - Documentation _ Bytemark_files/phpmyadmin_login.png)
Here you can login as any MySQL user you want. You can find the password for the MySQL root user inside your Docker environment file (which also has instructions on how to change any of the passwords used):
cat /root/compose/.env |
Access the Traefik dashboard
Traefik has a nice dashboard with health metrics. Navigate to http://name.of.server.uk0.bigv.io/traefik/ and login with username admin and the same browser authentication password as for phpMyAdmin above.
 and automatic updates - Documentation _ Bytemark_files/traefik-dashboard-e1530799927462.png)
Look a bit deeper
This is what we consider an ideal way to deploy WordPress.
Our setup script did all of these steps for you, but if you’re looking to walk through each step yourself then read on!
Install Docker
If you’re running Debian 9 (Stretch) or similar, run these commands:
apt-get updateapt-get install -y apt-transport-https ca-certificates curl gnupg2 software-properties-commoncurl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian `lsb_release -cs` stable"apt-get updateapt-get install -y docker-cecurl -fsSL https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-composechmod +x /usr/local/bin/docker-compose |
docker-compose.yml
Docker Compose helps you to manage Docker containers and easily link related containers together.
Create a directory called /root/compose and save the text below as /root/compose/docker-compose.yml on your server:
version: '3'# See https://docs.docker.com/compose/overview/ for more information.# If you make changes to this file or any related files, apply them by# navigating to the directory that holds this file and run this as root:# docker-compose down; docker-compose up -d# Create two networks: one for front-end containers that we'll make# publicly accessible to the internet, and one for private back-end.networks: frontend: backend:# Create persistent Docker volumes to preserve important data.# We don't want our data to be lost when restarting containers.volumes: vol-wp-db: vol-wp-content:# Create our containers.services: # Traefik is a reverse proxy. It handles SSL and passes traffic to # Docker containers via rules you define in docker-compose labels. # Its dashboard is at http://example.com/traefik/ (behind a login). traefik: image: traefik:latest command: --api --docker --acme.email="${ACME_EMAIL}" restart: always networks: - backend - frontend volumes: - /var/run/docker.sock:/var/run/docker.sock # Access to Docker - ./traefik.toml:/traefik.toml # Traefik configuration - ./acme.json:/acme.json # SSL certificates ports: # Map port 80 and 443 on the host to this container. - "80:80" - "443:443" labels: - "traefik.docker.network=frontend" - "traefik.enable=true" - "traefik.frontend.rule=Host:${TRAEFIK_DOMAINS}; PathPrefixStrip:/traefik" - "traefik.port=8080" - "traefik.protocol=http" # Remove next line to disable login prompt for the dashboard. - "traefik.frontend.auth.basic=${BASIC_AUTH}" # Watchtower detects if any linked containers have an new image # available, automatically updating & restarting them if needed. watchtower: image: v2tec/watchtower:latest # This schedule applies updates (if available) at midnight. command: --cleanup --schedule "0 0 0 * * *" restart: always volumes: - /var/run/docker.sock:/var/run/docker.sock labels: - "traefik.enable=false" wp-db: # Specify 10.3 as we only want watchtower to apply minor updates # (eg, 10.3.1) and not major updates (eg, 10.4). image: mariadb:10.3 restart: always networks: - backend volumes: # Ensure the database persists between restarts. - vol-wp-db:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: ${WORDPRESS_DB_ROOT_PASSWORD} MYSQL_DATABASE: ${WORDPRESS_DB_NAME} MYSQL_USER: ${WORDPRESS_DB_USER} MYSQL_PASSWORD: ${WORDPRESS_DB_PASSWORD} labels: - "traefik.enable=false" # The main front-end application. wp: # Replace "latest" with "4.9" to stick to a specific version. image: wordpress:latest depends_on: - wp-db restart: always networks: - backend - frontend volumes: # Ensure WP themes/plugins/uploads persist between restarts. - vol-wp-content:/var/www/html/wp-content # Install our own php.ini, which can be customized. - ./php.ini:/usr/local/etc/php/php.ini environment: WORDPRESS_DB_HOST: wp-db:3306 WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME} WORDPRESS_DB_USER: ${WORDPRESS_DB_USER} WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} labels: - "traefik.docker.network=frontend" - "traefik.enable=true" - "traefik.frontend.rule=Host:${WORDPRESS_DOMAINS}" - "traefik.port=80" - "traefik.protocol=http" # Navigate to http://example.com/phpmyadmin/ to manage your MySQL # databases. (Don't forget the last forward slash.) Like the Traefik # dashboard, this is behind a login prompt to help you stay secure. wp-phpmyadmin: image: phpmyadmin/phpmyadmin:latest depends_on: - wp-db restart: always networks: - backend - frontend volumes: # Install our own php.ini, which can be customized. - ./php.ini:/usr/local/etc/php/php.ini environment: PMA_HOST: wp-db PMA_ABSOLUTE_URI: /phpmyadmin/ MYSQL_ROOT_PASSWORD: ${WORDPRESS_DB_ROOT_PASSWORD} labels: - "traefik.docker.network=frontend" - "traefik.enable=true" - "traefik.frontend.rule=Host:${WORDPRESS_DOMAINS}; PathPrefixStrip:/phpmyadmin/" - "traefik.port=80" - "traefik.protocol=http" # Remove the next line if you don't want a browser login prompt. - "traefik.frontend.auth.basic=${BASIC_AUTH}" # This allows WordPress to send email straight out of the box without # having to rely on an external provider like SendGrid or MailGun. # It makes an SMTP host available at the hostname "mail". mail: image: bytemark/smtp restart: always networks: - frontend labels: - "traefik.enable=false" |
Docker .env file
Save the following text as /root/compose/.env on your server:
# Docker Compose can read environment variables from this file.# Put admin areas behind a login prompt, with username and password# specified here. Run `htpasswd -n admin` to create a password for# user "admin" and paste the output here. SSL strongly recommended.BASIC_AUTH=# Let's Encrypt needs an email address for registration.ACME_EMAIL=# The Traefik dashboard will be available at these domains.# The URL is http://example.com/traefik/TRAEFIK_DOMAINS=example.com,www.example.com# Your WP site will be available at these domains. If all domains# have DNS records pointing to your server, they'll get SSL certs.WORDPRESS_DOMAINS=example.com,www.example.com# Set a secure password for the MySQL root user. Remember this so# you can login to phpMyAdmin (as username "root").WORDPRESS_DB_ROOT_PASSWORD=# Set the MySQL database name, user and password for WordPress.WORDPRESS_DB_NAME=wordpressWORDPRESS_DB_USER=wordpressWORDPRESS_DB_PASSWORD= |
As a minimum, you need to fill the following settings:
BASIC AUTHTRAEFIK_DOMAINSWORDPRESS_DOMAINSWORDPRESS_DB_ROOT_PASSWORDWORDPRESS_DB_PASSWORD
Optionally, auto-fill those settings by running the commands below. It sets the domain to the hostname of your server, and it generates random passwords that you can review inside /root/compose/.env and /root/compose/auth-password.txt.
sed -i -e "s|^TRAEFIK_DOMAINS=.*|TRAEFIK_DOMAINS=`hostname -f`|" /root/compose/.envsed -i -e "s|^WORDPRESS_DOMAINS=.*|WORDPRESS_DOMAINS=`hostname -f`|" /root/compose/.envsed -i -e "s|^WORDPRESS_DB_ROOT_PASSWORD=.*|WORDPRESS_DB_ROOT_PASSWORD=`cat /dev/urandom | tr -dc '[:alnum:]' | head -c14`|" /root/compose/.envsed -i -e "s|^WORDPRESS_DB_PASSWORD=.*|WORDPRESS_DB_PASSWORD=`cat /dev/urandom | tr -dc '[:alnum:]' | head -c14`|" /root/compose/.envapt-get install -y apache2-utilsBASIC_AUTH_PASSWORD="`cat /dev/urandom | tr -dc '[:alnum:]' | head -c10`"BASIC_AUTH="`printf '%s\n' "$BASIC_AUTH_PASSWORD" | tee /root/compose/auth-password.txt | htpasswd -in admin`"sed -i -e "s|^BASIC_AUTH=.*|BASIC_AUTH=$BASIC_AUTH|" /root/compose/.env |
Traefik
Making multiple containers accessible to the internet and sorting out SSL certificates can be a pain. That’s where Traefik comes in!
Traefik acts as a reverse proxy, listening on ports 80 and 443 and passing web traffic to the appropriate container based on rules you decide (eg, based on the URL). It also automatically retrieves Let’s Encrypt certificates for you.
Save the following text as /root/compose/traefik.toml on your server:
# Traefik will listen for traffic on both HTTP and HTTPS.defaultEntryPoints = ["http", "https"]# Network traffic will be entering our Docker network on the usual web ports# (ie, 80 and 443), where Traefik will be listening.[entryPoints] [entryPoints.http] address = ":80" # Comment out the following two lines to redirect HTTP to HTTPS. # [entryPoints.http.redirect] # entryPoint = "https" [entryPoints.https] address = ":443" [entryPoints.https.tls]# These options are for Traefik's integration with Docker.[docker]endpoint = "unix:///var/run/docker.sock"domain = "docker.localhost"watch = trueexposedByDefault = false# These options are for Traefik's integration with Let's Encrypt.# Your certificates are stored inside /acme.json inside the container,# which is /root/compose/acme.json on your server.[acme]storage = "acme.json"onHostRule = trueentryPoint = "https" [acme.httpChallenge] entryPoint = "http"# Comment out the next line to enable Traefik's access logs.# [accessLog] |
Traefik needs a file to store SSL keys and certificates, so run these commands:
touch /root/compose/acme.jsonchmod 0600 /root/compose/acme.json |
php.ini
Some of PHP’s default settings are a bit restrictive and a frequent cause of misery. Fortunately, you can use a custom php.ini file in the WordPress container.
Save the following text as /root/compose/php.ini on your server:
# Feel free to add and change any settings you want in here.upload_max_filesize = 128Mpost_max_size = 128Mmax_execution_time = 180memory_limit = 256M |
SMTP
WordPress needs a way to send outgoing email. There are lots of SMTP images on Docker Hub that you can choose from.
We maintain a single-purpose bytemark/smtp image, which simply lets linked containers send email. (Optionally, you can configure it to act as a smart host that relays mail to an intermediate server such as SendGrid.)
Our Docker Compose file configures SMTP to be available to other containers at hostname mail and port 25.
Watchtower
Watchtower keeps all of your Docker containers up-to-date. Whenever the images you’re using are updated (eg, MariaDB, WordPress), watchtower automatically updates the relevant containers.
Our Docker Compose file configures watchtower to auto-update every night.
Manage your containers
To start your containers:
cd /root/composedocker-compose up -d |
To stop your containers:
cd /root/composedocker-compose down |