loading...

Hosting WordPress over HTTPS with Docker

foresthoffman profile image Forest Hoffman Updated on ・11 min read

Just the other day, I moved my portfolio to a separate server and started serving it over HTTPS. I was super stoked when it was all done! I wanted to talk a bit about what steps I took, since I found some annoying gotchas along the way. This isn't a step-by-step tutorial, rather I'm sharing the configurations that finally got it working for me.

I use example.com as a placeholder here. If you're reading this and trying to get your own setup working, you'd replace example.com with your own domain. Also, though I doubt it needs to be said, don't use root as your MySQL password.

Table of Contents

The Server Stack

^ ToC

I use Docker to isolate my servers, so that they can all have their own dedicated environments. That, and if I mess something up on a Docker container, I can just rebuild it. Also, I use Docker Compose to handle grouping together my containers.

I chose Nginx as my proxy and my WordPress web server. Note that the proxy isn't necessary here, but I needed it for a few other things. I wanted to keep things consistent, so that's why I opted to use Nginx across the board.

If you're trying to achieve a similar setup, but sans-proxy, you can just as well provide your WordPress container's Nginx server with the SSL certificates and add in the proper server blocks for SSL-support. I mention those below in the Nginx Proxy Container section.

For my SSL certificates, I'm using Let's Encrypt via Certbot. It's a lovely little tool that automates the whole certificate registration process.

Docker Compose Configuration Overview

^ ToC

The following sections list out all the necessary configurations for the following containers:

- proxy:
  - nginx
- wordpress:
  - nginx
  - wordpress
  - mariadb

Nginx Proxy Container

^ ToC

docker-compose.yml

version: '3.3'

services:
  nginx-proxy:
    container_name: nginx-proxy
    build: .
    image: nginx-proxy:0.0.2
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./etc/letsencrypt/:/etc/letsencrypt/"
      - "./etc/ssl/:/etc/ssl/"
    command: bash -c "startup.sh"
    restart: always

The container uses an adjacent Dockerfile for building the custom nginx-proxy image. The container exposes port 80 and port 443 to the host machine. When the container is brought up, it mounts the two local directories to the /etc/letsencrypt/ and /etc/ssl/ directories on the container, respectively. Then, the default startup command (nginx -g 'daemon off;') is overridden to run the startup.sh file. Finally, the container is configured to restart whenever it goes now, via the restart: always line.

Dockerfile

FROM nginx:1.13.5-alpine

LABEL maintainer="Forest Hoffman<forestjhoffman@gmail.com>"

##
# setting necessary server configurations
##

# add curl and other necessary packages for openssl and certbot
RUN apk add --update \
                curl \
                bash \
                openssl \
                certbot \
                python \
                py-pip \
        && pip install --upgrade pip \
        && pip install 'certbot-nginx' \
        && pip install 'pyopenssl'

RUN addgroup staff
RUN addgroup www-data
RUN adduser -h /home/dev/ -D -g "" -G staff dev
RUN adduser -S -H -g "" -G www-data www-data
RUN echo dev:trolls | chpasswd

##
# copying nginx configuration file
##

COPY nginx.conf /etc/nginx/nginx.conf
RUN chown root:staff /etc/nginx/nginx.conf

# adds certbot cert renewal job to cron
COPY crontab /tmp/crontab-certbot
RUN (crontab -l; cat /tmp/crontab-certbot) | crontab -

# copies over the startup file which handles initializing the nginx
# server and the SSL certification process (if necessary)
COPY startup.sh /bin/startup.sh
RUN chown root:root /bin/startup.sh
RUN chmod ug+rx /bin/startup.sh
RUN chmod go-w /bin/startup.sh

The Dockerfile will include all the necessary requirements for running the Certbot within the container. It will also add a line to the container's cron which will run the Certbot in renewal mode, early in the morning (according to the Docker container's system time).

crontab

# Renew Let's Encrypt SSL Certificates that have < 30 days to go,
# in the morning (UTC time).
0 11 * * * /usr/bin/certbot renew --quiet

startup.sh

#!/bin/bash
#
# startup.sh
#

# Startup the nginx server. The server has to be active for the Let's Encrypt Certbot to
# register and install the certificates.
nginx -g "daemon on;"

# Checks that the SSL certificates are installed. If they are, renews any that are old, and
# installs them if not.
if [[ -d "/etc/letsencrypt/live/example.com" ]]; then
        certbot renew --quiet
else
        if ! [[ -d "/etc/letsencrypt/live/example.com" ]]; then
                certbot --nginx -m your-email@ex.com --agree-tos --no-eff-email --redirect --expand -d example.com,www.example.com
        fi
        if ! [[ -f "/etc/ssl/certs/dhparam.pem" ]]; then
                openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
        fi
fi

# Shuts down the daemonized nginx server and fires up one in the foreground.
nginx -s stop && nginx -g 'daemon off;'

The startup.sh script is intended to be run whenever the container is brought up (i.e. with docker-compose up). It will only attempt to register certifications if the directory for the certifications are not already in the /etc/letsencrypt/live directory on the container. The paths in startup.sh must coincide with those in the Nginx configuration file. Speaking of which...

nginx.conf

user www-data;
worker_processes 1;
pid /run/nginx.pid;

events {
    worker_connections 1024;
    # multi_accept on;
}

http {
    #sendfile on;

    upstream docker-wordpress {
            server nginx-wordpress:80;
    }

    # listen for HTTPS requests to example domain
    server {
            ssl_dhparam /etc/ssl/certs/dhparam.pem;
            server_name example.com www.example.com;

            listen 443 ssl; # managed by Certbot
            ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
            ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
            include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

            location / {
                    proxy_pass       http://docker-wordpress;
                    proxy_redirect   off;
                    proxy_set_header Host $host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Host $server_name;
                    proxy_set_header X-Forwarded-Proto https;
            }
    }

    # handle ACME challenge from Certbot, and send HTTP requests to HTTPS
    server {
            listen 80;
            server_name example.com www.example.com;

            # listen for ACME challenge from Certbot
            location ^~ /.well-known/acme-challenge/ {
                    # No HTTP authentication
                    allow all;

                    default_type "text/plain";
            }

            location = /.well-known/acme-challenge/ {
                    return 404;
            }

            # Redirect other HTTP traffic to HTTPS
            location / {
                    access_log off;
                    return 301 https://$host$request_uri;
            }
    }
}

This configures the proxy to listen for requests hitting port 443 (e.g. requests using https://*). If the requests are for the example.com or www.example.com domain, then the request is passed on to the http://docker-wordpress server. The http://docker-wordpress "upstream" server is defined as port 80 of the connected nginx-wordpress container. Connecting the containers together is accomplished by using the networks key in the docker-compose.yml files.

[Edited 12/05/17]: I updated the Nginx configuration file above to include a server block for allowing the proxy to pass Certbot's ACME challenge. This is required for certificate renewal.

Any requests to port 80 are not using SSL (just normal http://*), so those requests are redirected to port 443 by forcing the use of HTTPS in the url.

This block...

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

Indicates where the SSL certificates for the desired domains will reside on the proxy container.

The WordPress Containers

^ ToC

Note that I've broken up the container configurations in the sub-sections below, but they are actually all from the same docker-compose.yml file.

Here's what it looks like all together.

docker-compose.yml

version: '3.3'

services:
  nginx:
    container_name: nginx-wordpress
    image: nginx:latest
    ports:
      - '80'
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./logs/nginx:/var/log/nginx
      - ./wordpress:/var/www/html
    depends_on:
      - wordpress
    restart: always
    networks:
      - nginxproxy_default
  mysql:
    container_name: mysql
    image: mariadb
    ports:
      - '3306'
    volumes:
      - ./mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
    restart: always
    networks:
      - nginxproxy_default
  wordpress:
    container_name: wp
    image: wordpress-prod:4.8.1-php5.6-fpm
    ports:
      - '9000'
    volumes:
      - ./wordpress:/var/www/html
    environment:
      - WORDPRESS_DB_NAME=wordpress
      - WORDPRESS_TABLE_PREFIX=wpprefix_
      - WORDPRESS_DB_HOST=mysql
      - WORDPRESS_DB_PASSWORD=root
    depends_on:
      - mysql
    restart: always
    networks:
      - nginxproxy_default
networks:
  nginxproxy_default:
    external: true

The key thing to see here is the top-level networks key, which allows each of the containers to connect to the proxy's default network. The network is not generated by any of the containers in this compose file, so the nginxproxy_default network is defined as an external network via the external: true line.

Nginx for WordPress Container

^ ToC

docker-compose.yml

###
  nginx:
    container_name: nginx-wordpress
    image: nginx:latest
    ports:
      - '80'
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./logs/nginx:/var/log/nginx
      - ./wordpress:/var/www/html
    depends_on:
      - wordpress
    restart: always
    networks:
      - nginxproxy_default
###

The nginx server in front of the WordPress container exposes port 80, and mounts four directories. The first two are for nginx configuration, the next is for preserving logs, and the last one is for preserving the WordPress core file structure.

nginx.conf

user www-data;
worker_processes auto;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # Virtual Host Configs
    ##

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

This is just a basic configuration file that includes any additional configuration files mounted to the /etc/nginx/conf.d and /etc/nginx/sites-enabled/ directories. This file also specifies the www-data user as the user that Nginx should use when attempting to serve files. This means that the server's file structure (mounted to /var/www/html) should be accessible by the www-data user/group.

nginx/wordpress.conf

server {
    listen 80;

    root /var/www/html;
    index index.php;

    access_log /var/log/nginx/wp-access.log;
    error_log /var/log/nginx/wp-error.log;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.(png|jpg|jpeg|gif|svg)$ {
        try_files $uri $uri/;
    }

    # Deny access to config.
    location = /wp-config.php {
        deny all;
    }

    # Deny access to htaccess.
    location ~ /\. { 
        deny all;
    }

    # Directly allow access to /wp-admin/admin-ajax.php. This is necessary for
    # WordPress to function on the admin side.
    location ~* ^/wp-admin/admin-ajax.php$ {
        try_files $uri =404;
        fastcgi_intercept_errors on;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Allow access to all other PHP files.
    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;
    }
}

This Nginx server specifically caters to the WordPress container. It listens for requests to port 80 and sends any requests for PHP files (include admin pages) to the WordPress container on port 9000. This nginx server doesn't need to listen on port 443 because the proxy is handling the SSL authentication. Any incoming requests for files on the WordPress server will be routed through the proxy.

WordPress Container

^ ToC

docker-compose.yml

###
  wordpress:
    container_name: wp
    image: wordpress-prod:4.8.1-php5.6-fpm
    ports:
      - '9000'
    volumes:
      - ./wordpress:/var/www/html
    environment:
      - WORDPRESS_DB_NAME=wordpress
      - WORDPRESS_TABLE_PREFIX=wpprefix_
      - WORDPRESS_DB_HOST=mysql
      - WORDPRESS_DB_PASSWORD=root
    depends_on:
      - mysql
    restart: always
    networks:
      - nginxproxy_default
###

The WordPress container exposes port 9000. It has one volume mounted for preserving the WordPress file structure. It also has four environment variables that correspond to the connected database. Note that the WORDPRESS_DB_HOST name is the same as the container_name of the connected mysql container.

wordpress/wp-config.php

<?php
###
// If we're behind a proxy server and using HTTPS, we need to alert Wordpress of that fact
// see also http://codex.wordpress.org/Administration_Over_SSL#Using_a_Reverse_Proxy
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';

    // Force SSL in admin.
    define('FORCE_SSL_ADMIN', true);
}

/* That's all, stop editing! Happy blogging. */
###

Ignoring the default configurations, these few lines can be placed before the "stop editing!" comment. These lines force WordPress to make internal requests using HTTPS, including on the admin side.

Mariadb (MySQL) for WordPress Container

^ ToC

docker-compose.yml

###
  mysql:
    container_name: mysql
    image: mariadb
    ports:
      - '3306'
    volumes:
      - ./mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
    restart: always
    networks:
      - nginxproxy_default
###

The difference between the Mariadb/MySQL container and the others is the mysql volume being mounted, and the root-password environment variable. All the other configurations in this block are very similar to the previous containers.

Installing SSL Certificates with Certbot

^ ToC

In order to run the Certbot on the Nginx Proxy, the server has to be up and running first. Assuming that the certifications are not already in their local ./etc/letsencrypt directory, attempting to run the proxy now would fail. I discovered that in order for the proxy to run, several things need to happen.

1) The nginxproxy_default network needs to be created. If the proxy has never been run before, then the network can't exist. Create the network by running the following in a terminal:

$ docker network create nginxproxy_default

2) The WordPress containers need to be running before the proxy can start. First navigate to wherever the docker-compose.yml file is for the WordPress containers. Then run the following in a terminal:

$ docker-compose up -d

Note: The -d flag starts the containers in headless mode, meaning that you won't see any errors unless you use the command docker-compose logs or check the status of the containers via docker ps -a.

3) The paths to the certificates need to be commented out before the proxy container can run the Certbot. In the proxy's nginx.conf I had to comment out the server block containing the listen 443 ssl; line and the server block handling redirection from HTTP to HTTPS. In their place, I temporarily entered this server block:

server {
    listen 80;
    server_name example.com www.example.com;
    location / {
         proxy_pass       http://docker-wordpress;
         proxy_redirect   off;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Host $server_name;
    }
}

Then, the Nginx Proxy can be built using the docker-compose up --build command. Docker-compose will then user the non-default startup command, which will run the startup.sh script in the container. I would recommend not using headless mode when building the proxy, that way you can be sure that the Certbot was able to register the certificates.

Once the Certbot has successfully created the certificates, the temporary sever block added in step 3 above can be removed and the two other server blocks uncommented. After changing the configuration, the proxy will need to be reloaded with an docker-compose down && docker-compose up -d.

Doing a quick check with docker ps -a should display something along these lines:

CONTAINER ID  IMAGE                            COMMAND                 CREATED     STATUS        PORTS                                     NAMES
62b3a673f1cb  nginx-proxy:0.0.2                "nginx -g 'daemon ..."  3 days ago  Up 2 minutes  0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp  nginx-proxy
ea4acq9cc868  nginx:latest                     "nginx -g 'daemon ..."  4 days ago  Up 3 minutes  0.0.0.0:8081->80/tcp                      nginx-wordpress
c7badc76c834  wordpress-prod:4.8.1-php5.6-fpm  "docker-entrypoint..."  4 days ago  Up 3 minutes  0.0.0.0:8082->9000/tcp                    wp
2ba321f4afdd  mariadb                          "docker-entrypoint..."  4 days ago  Up 3 minutes  0.0.0.0:8083->3306/tcp                    mysql

If everything looks well, navigating to https://example.com/wp-admin should display the good 'ole WordPress setup screen.

Further Reading

^ ToC

Posted on by:

foresthoffman profile

Forest Hoffman

@foresthoffman

Full Stack Engineer. Golang Engineer at The Home Depot. Musings about Go, TypeScript, Node.js. My thoughts are my own.

Discussion

markdown guide
 

Hi, thank you for taking the time to create this write-up. It captures what I want to achieve.

A few questions are still keeping me away from understanding the entire picture so hopefully you could shed some light on this:

  • When focusing on the "The WordPress Containers" I think I might be missing some configuration, e.g. the nginx.conf inside the container named "nginx-wordpress". Does this mean you run the nginx container as is? Or is it using the same nginx.conf as the proxy?

  • The container named "wp" refers to a Dockerfile "Dockerfile-wp" which is not on display, am I correct?
    Also, I think I might be missing the reason behind the volume "- ./wordpress:/var/www/html" as there is a container to provide this data? Or is this for any additional files being uploaded which will not be kept inside the data-container?

Is this setup available on github somewhere?

Thank you very much!

 

Hi Roberto, thanks for taking the time to read it!

When focusing on the "The WordPress Containers" I think I might be missing some configuration, e.g. the nginx.conf inside the container named "nginx-wordpress". Does this mean you run the nginx container as is? Or is it using the same nginx.conf as the proxy?

No, the nginx-wordpress container has a separate configuration file from the nginx-proxy container. The nginx.conf file for the nginx-wordpress container is a run-of-the-mill configuration file. I believe mine is a slightly modified version of the default file that is created when you install Nginx on a normal machine. The key parts are the user and include statements. I'll add the configuration file to the write-up.

The container named "wp" refers to a Dockerfile "Dockerfile-wp" which is not on display, am I correct?

Ah yes, my apologies. I hadn't caught that. I believe the reason I overlooked it was, the Dockerfile ("Dockerfile-wp") runs some additional commands for debugging and such. I like to have a non-admin user and WP-CLI installed so that I can run some batch commands, should the need arise. The Dockerfile itself isn't crucial and the setup will work just as well if the build block in the Compose file were removed. I'll update that in the write-up.

Also, I think I might be missing the reason behind the volume "- ./wordpress:/var/www/html" as there is a container to provide this data? Or is this for any additional files being uploaded which will not be kept inside the data-container?

The reason that the wordpress directory exists on the host machine is to maintain the state of the WordPress docker container should it shutdown (this also goes for the mysql directory).

I would assume, correct me if i'm wrong, that the confusing part is the fact that the volume is mounted on both the wordpress and nginx containers. Nginx needs access the file structure that it is hosting, which happens to the same file structure that WordPress is being run on. Only mounting the wordpress directory to the WordPress container would be useless, since WordPress is about as interesting as a pet rock without a web server. On the other hand, only mounting the wordpress directory to the Nginx container would also be useless, as requests to the WordPress container wouldn't do anything.

Is this setup available on github somewhere?

No, I don't currently have anything up on GitHub regarding this. I built the write-up from what I have on my live server, so I took each piece and removed/replaced sensitive data with placeholders. For example, the MySQL password. Therefore, I don't have an anonymized version.

I'll make a reply when i've updated the write-up.

Thank you for your patience!

 

So, I've read your feedback, thanks for this!

I would assume, correct me if i'm wrong, that the confusing part is the fact that the volume is mounted on both the wordpress and nginx containers...

Thank you for explaining this, but this was actually not my confusion. I am confused about what the directory would actually hold.

The reason that the wordpress directory exists on the host machine is to maintain the state of the WordPress docker container should it shutdown (this also goes for the mysql directory).

Would this mean that the wordpress directory on the host machine already contains files ? Or would the modified / uploaded files be placed in there?

As for the rest of your feedback, thank you for clarifying. I will get to work with this in the next few days so I'll let you know how far it gets me.

Thank you!

The wordpress volume on the host machine holds the core WordPress files for the WordPress container (wp-config.php, wp-content/, wp-admin, etc.). Those files initially come from the WordPress container itself, when it's built. Since the wordpress volume is mounted to the container, from the host, you can do whatever you want with the files and they will propagate over to the container.

 

I've added the Nginx configuration file above, and removed the build block from the docker-compose.yml file for the WordPress containers.

Thanks again!

 

Hi Forest, thank you for replying so fast already!
I will be taking a look at this tomorrow as it currently is 23.20 where I live.
By the looks of it you have covered the most part I guess. You will hear from me tomorrow.
Good day/night or whatever applies to your timezone.

Cheers.

Oh cool, i'm on PST, so waaaay behind ya.

 

I see server block unconditionally redirects HTTP traffic to HTTPS: So how does the Let's Encrypt server checks the ACME challenge (I was under the impression ACME checks are performed over HTTP, not over HTTPS)?

 

From my understanding they are performed over HTTP, yes. That's the reason that the redirection block and SSL blocks are commented out in step 3 of the Installing SSL Certificates with Certbot section.

Thanks!

 

Right, but I don't see where this "force-all-HTTP-traffic-to-HTTPS part gets disabled/commented when certificate renewal occurs.

When I configure certificate on a "force HTTPS" Nginx configuration, I have the following in HTTP server block:

location ^~ /.well-known/acme-challenge/ {
    # No HTTP authentication
    allow all;

    # Set correct content type. According to this:
    # https://community.letsencrypt.org/t/using-the-webroot-domain-verification-method/1445/29
    # Current specification requires "text/plain" or no content header at all.
    # It seems that "text/plain" is a safe option.
    default_type "text/plain";
}
location = /.well-known/acme-challenge/ {
    return 404;
}

# Redirect the rest of HTTP traffic to HTTPS:
location / {
    return 301 https://$host$request_uri;
    access_log off;
}

Thus ACME challenge gets served over HTTP, and the other requests are redirected to HTTPS.

Oh, interesting. I didn't know this was a potential issue. Thankfully, I've got quite a while before my certs need renewing. Thank you for bringing this to my attention!

 

Where is the image wordpress-prod:4.8.1-php5.6-fpm located?

I can't seem to pull it and if I use the regular Wordpress docker container, the container tries to listen on port 80 by default which causes conflicts with the proxy.

 

Howdy!

I'm certain that this is a custom docker container that I built using wordpress:4.8.1-php5.6-fpm. The custom stuff that I added was just personal preference. The 4.8.1-php5.6-fpm version of the official WordPress docker container is no longer available. At a glance, I believe you'll want 4.9.1-php-5.6-fpm.

i.e. The following should work.

# docker-compose.yml
...
image: wordpress:4.9.1-php-5.6-fpm
...

That said, are you using an Nginx server to support your WordPress container, as I am? If that's the case, make sure that you're using the *-fpm variety, or else the default Apache server that comes with the run-of-the-mill WordPress container will try to take over. That could be the cause of the port 80 conflict with the proxy.

Let me know if that helps.

 

I had a few bugs with this setup, and would not recommend it for most users. Since there are now more mature tools for automating Wordpress installation/upkeep, I instead recommend easyengine.io.

It does virtually the same thing as this, but is much more automated and functional out of the box, easier to install and maintain, and it has a functioning nginx page/asset caching layer already configured (this does not).

Important thing to note for this Hoffman's config here: It has a top layer "https" proxy, and a proxy just below it that is part of the Wordpress "group" in that docker compose file. The Wordpress group needs to be started BEFORE the "https" group, and they need to be in separate directories as separate docker compose files.
dev-to-uploads.s3.amazonaws.com/i/...

Off the top of my head, here are two important modifications needed for smooth operation of this setup:
1: An altered startup.sh (the given one does NOT work):
gist.github.com/mkrupczak3/b928644...

2: Fix the crontab for latest versions of Certbot:
gist.github.com/mkrupczak3/f6f2765...

There are some others here that I'm forgetting, but anyways, have fun finding them! If you're new to using Docker or Docker compose, this is a decent project to get you started playing with these tools, but I would recommend total newcomers shy away from this in favor of more simple/sane projects.

 

Thanks for the comment!

This article over three years old, so I agree that it's not even close to a standard solution. I had a very personal setup that I strove to keep as bare bones as possible, using docker as my only automation tool. I personally haven't used this setup in roughly two years (I can't remember the exact date that I abandoned it).


Three years later, this article still gets views and comments. So unfortunately there are still folks that are doomed to work with legacy versions of WordPress and need a legacy solution. If this does it for you, I'm happy to have helped. If not, I wish you the best of luck!

Gif of Simon Pegg Winking

 

Hi,
While running the setup, i'm getting following error in "nginx-proxy" container.

" open() "/etc/letsencrypt/options-ssl-nginx.conf" failed (2: No such file or directory) in /etc/nginx/nginx.conf:25
nginx: [emerg] open() "/etc/letsencrypt/options-ssl-nginx.conf" failed (2: No such file or directory) in /etc/nginx/nginx.conf:25
Saving debug log to /var/log/letsencrypt/letsencrypt.log ".

My folder structure is below like this---->

proxy /
crontab docker-compose.yml Dockerfile etc nginx.conf startup.sh

wordpress-nginx/
docker-compose.yml logs mysql nginx nginx.conf wordpress

and my "nginx-wordpress" container gives following error.

" /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: error: /etc/nginx/conf.d/default.conf is not a file or does not exist"

can you help.
Thanks

 

Howdy!

I believe this is an issue coming from the certbot tool, which is executed by the proxy/startup.sh script. You'll want to make sure that your nginx-proxy is accessible from outside your network. As the certbot tool will attempt to verify that the domain you've listed is actually owned by you. If your DNS provider is not already pointing to your domain, where the nginx-proxy is accessible, that would prevent the certifications from being verified.

If your domain is already correctly pointing to where your proxy will be running, you can try firing up the nginx-proxy without the startup.sh script. You'll want to comment-out the proxy/docker-compose.yml line that runs the script on docker startup (i.e. command: bash -c "startup.sh"). This should allow you to run docker-compose up on the proxy without it immediately crashing. From there, you can connect to the container and attempt to run the certbot commands manually.

Hopefully that will give you a better idea of what's going on. Best of luck! :)

 

Thanks,
I'll try with that process and let you know.

 

@Forest Hoffman, there seems to be a subtle difference between the code here and on your own site.
It's regarding the rule that denies access to .htaccess in de nginx/wordpress.conf.
It just a difference of a backward slash that's lacking on the example on your own site (you don't have comments there so I thought I post it here). I thought it would be worth pointing out because it took me a couple of hours since all I got was a 403 status on everything.

 

Furthermore I really like the set-up!

 

Well thank you very much! I'm glad you got it working, but I am sorry to hear about that little hiccup. I've been quite busy the last few months, with some serious (in a good way) life changes. Thank you for bringing this to my attention!

 

Hope you could upload this to GitHub :)

 

Hi there! Thank you for your interest. I've been quite busy the last few months with some big life changes.

This is my reasoning with regard to GitHub:

No, I don't currently have anything up on GitHub regarding this. I built the write-up from what I have on my live server, so I took each piece and removed/replaced sensitive data with placeholders. For example, the MySQL password. Therefore, I don't have an anonymized version.

In the future, perhaps i'll have time to anonymize the files that I do have and create a proper repository, but I don't expect that to happen soon.

Take care! :)

 

Hello

I want to install other software next to this using a docker ? It is possible ?

 

Hi TeSlowa,

Yes, that is absolutely possible. All Docker containers, like the ones I mention in the post, are self-contained. Anything installed to a container stays in the container. That keeps your host machine from being cluttered.

Cheers

 

Hi guys,

Is there any repository on github with the full content of this tutorial ?

Cheers,
X.