├── LICENSE.txt ├── README.md ├── acme.json ├── php.ini ├── traefik-docker-compose.yml ├── traefik.toml └── yourwp-docker-compose.yml /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 David Rubio Vidal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wordpress + Traefik + Let's Encrypt certificates using Docker Swarm 2 | 3 | This repository is all you need to have your own Wordpress site (or sites) running behind [Traefik](https://docs.traefik.io/v1.0/) proxy with free autorenewal wildcard certificate from [Let's Encrypt](https://letsencrypt.org/). And all of it with the power of [Docker](https://www.docker.com/) and the scalability provided by [Docker Swarm](https://docs.docker.com/engine/swarm/). 4 | 5 | ![Traefik](https://docs.traefik.io/img/architecture.png) 6 | 7 | ## What you need 8 | * **ONE** server, i.e. an EC2 instance on AWS. 9 | * **ONE** MySQL database, i.e. an RDS instance on AWS. However, this is not really mandatory as you can have another container that runs MySQL. See the official [Worpress Docker Hub](https://hub.docker.com/_/wordpress/) image for instructions. 10 | * Docker installed on the server. 11 | * A domain registered, i.e. Route53. 12 | 13 | ### For local development 14 | * [Docker Compose](https://docs.docker.com/compose/). 15 | 16 | ## Installation 17 | Clone this repository and change the files accordingly with the appropriate values, like: 18 | * Domain. 19 | * Passwords. 20 | * Docker network, volumes and secrets. 21 | * Wordpress configuration values: database host, name, username, etc. 22 | * Traefik labels. 23 | 24 | ### Create the Docker virtual network 25 | The Traefik proxy and the Worpress (or any other webapp) must be under the same virtual network. So, before running the containers create the network by running: 26 | 27 | ```bash 28 | # For local development 29 | docker network create 30 | 31 | # For production (Swarm mode) 32 | docker network create --driver overlay 33 | ``` 34 | 35 | ### `traefik.toml` 36 | This is the Traefik [configuration file](./traefik.toml) and it contains self-explanatory comments for each section. 37 | 38 | For local development, the Staging CA Server from Let's Encrypt can be used. To do so, add the following line under `[acme]` section: 39 | 40 | ```toml 41 | [acme] 42 | ... 43 | caServer="https://acme-staging-v02.api.letsencrypt.org/directory" 44 | 45 | ``` 46 | 47 | For more information visit: 48 | * https://docs.traefik.io/v1.0/toml/ 49 | * Examples: https://docs.traefik.io/user-guide/examples/ 50 | * Let's Encrypt configuration: https://docs.traefik.io/configuration/acme/ 51 | 52 | ### `traefik-docker-compose.yml` 53 | [This Compose file](./traefik-docker-compose.yml) creates a container (or stack/service if using Swarm) with Traefik proxy running and listening on ports 80 and 443 - although all traffic on port 80 is redirected to port 443. Then, in turns, the traffic is redirected to the appropriate containers with the applications using the `labels`. 54 | 55 | Before running this file, update the values for: 56 | * Docker network. 57 | * Traefik labels. 58 | 59 | This file makes the monitoring UI available at the URL defined in the `traefik.frontend.rule` label via HTTPS. 60 | 61 | ### `yourwp-docker-compose.yml` 62 | [This Compose file](yourwp-docker-compose.yml) contains all the configuration required to create a container (or stack/service if using Swarm) with the selected version of Worpress. 63 | 64 | Before running this file, update the values for: 65 | * Version of Worpress. 66 | * Wordpress environment. 67 | * Docker network, volumes and secrets. 68 | * Traefik labels. 69 | 70 | ### `acme.json` 71 | This empty file will contain the certificates generated from Let's Encrypt once the containers have started. It needs specific file permissions for security so, once cloned, run: 72 | 73 | ```bash 74 | sudo chmod 600 acme.json 75 | ``` 76 | 77 | ### Start the containers 78 | Once the Traefik configuration file and the Compose files have been modified accordingly, start the containers by running the following commands: 79 | 80 | ```bash 81 | # ----- For local development ----- 82 | # Start Traefik container 83 | docker-compose -f traefik-docker-compose.yml up 84 | # Start your Wordpress container 85 | docker-compose -f yourwp-docker-compose.yml up 86 | 87 | # ----- For production (Swarm mode) ----- 88 | # Enable Swarm 89 | docker swarm init 90 | # Start Traefik container 91 | docker stack deploy -c traefik-docker-compose.yml proxy 92 | # Start your Wordpress container 93 | docker stack deploy -c yourwp-docker-compose.yml yourwp 94 | ``` 95 | 96 | Note that you can start as many Wordpress (or other webapps) as you want by cloning the Compose file and modifying accordingly. 97 | 98 | You can also use the power of Docker Swarm to create replicas of the Wordpress containers across multiple hosts to scale out. 99 | 100 | ## Check that it worked 101 | Once the containers have started you can visit both the monitoring built-in Traefik dashboard and the Wordpress site. 102 | 103 | ![Dashboard](https://docs.traefik.io/img/web.frontend.png) 104 | 105 | ![Health](https://docs.traefik.io/img/traefik-health.png) 106 | 107 | The following blogs are have been powered as described above: 108 | * [Travel Everwanderer](https://travel.everwanderer.com). 109 | * [Tech Everwanderer](https://tech.everwanderer.com). 110 | 111 | ## FAQ 112 | ### Let's Encrypt won't issue a certificate 113 | You need to make sure that your DNS provider, i.e. Route53, has a CAA record to allow Let's Encrypt to issue a certificate. In this case, as we are using a wildcard certificate, make sure to create CAA record with the following value. 114 | 115 | ``` 116 | 0 issuewild letsencrypt.org 117 | ``` 118 | 119 | For full info visit https://letsencrypt.org/docs/caa/ 120 | 121 | ## License 122 | Code copyright 2018. Code released under [the MIT License](./LICENSE.txt). -------------------------------------------------------------------------------- /acme.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidrv87/traefik-wordpress-letsencrypt/6c5208af026d2f9e4270e9153e42435942ecef3d/acme.json -------------------------------------------------------------------------------- /php.ini: -------------------------------------------------------------------------------- 1 | upload_max_filesize = 64M 2 | post_max_size = 64M 3 | max_execution_time = 300 -------------------------------------------------------------------------------- /traefik-docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | 3 | # Create our containers. 4 | services: 5 | # Traefik is a reverse proxy. It handles SSL and passes traffic to 6 | # Docker containers via rules you define in docker-compose labels. 7 | # Its dashboard is at https://subdomain.yourdomain.com (behind a login). 8 | # Just create an A record that points to box that hosts this container. 9 | traefik: 10 | # https://hub.docker.com/_/traefik/ 11 | image: traefik:1.6 12 | networks: 13 | - proxy 14 | volumes: 15 | - /var/run/docker.sock:/var/run/docker.sock # Access to Docker 16 | - ./traefik.toml:/traefik.toml # Traefik configuration 17 | - ./acme.json:/acme.json # SSL certificates 18 | ports: 19 | - "443:443" 20 | - "80:80" 21 | deploy: 22 | restart_policy: 23 | condition: any 24 | mode: replicated 25 | replicas: 1 26 | update_config: 27 | delay: 2s 28 | placement: 29 | constraints: [node.role == manager] 30 | labels: 31 | - "traefik.frontend.rule=Host:subdomain.yourdomain.com" 32 | - "traefik.backend=" # For simplicity, use "subdomain" 33 | - "traefik.port=8080" 34 | - "traefik.enable=true" 35 | - "traefik.docker.network=" 36 | 37 | # Create a network before using this file: 38 | # Local env (dev): 'docker create network ', i.e. =proxy 39 | # Swarm env (prod): 'docker create network --driver overlay ' 40 | networks: 41 | : 42 | external: true 43 | -------------------------------------------------------------------------------- /traefik.toml: -------------------------------------------------------------------------------- 1 | # Full info at https://docs.traefik.io/v1.0/toml/ 2 | 3 | # Traefik will listen for traffic on both HTTP and HTTPS. 4 | defaultEntryPoints=["http", "https"] 5 | 6 | # Set the logging level 7 | logLevel="INFO" # ERROR, WARNING, INFO, DEBUG 8 | 9 | # Access to dashboard interface. 10 | # This will be a separate web app running within the Traefik container on port 8080. 11 | # However, this is reacheable on port 443 12 | [api] 13 | # For simplicity, use the same name given to the subdomain (see traefik-docker-compose.yml) 14 | entrypoint="subdomain" 15 | 16 | # Network traffic will be entering our Docker network on the usual web ports 17 | # (ie, 80 and 443), where Traefik will be listening. 18 | [entryPoints] 19 | [entryPoints.http] 20 | address=":80" 21 | [entryPoints.http.redirect] 22 | entryPoint="https" 23 | [entryPoints.https] 24 | address=":443" 25 | [entryPoints.https.tls] 26 | # Health/monitor entrypoint 27 | [entryPoints.subdomain] 28 | address=":8080" 29 | [entryPoints.subdomain.auth.basic] 30 | # Generate MD5 by running 'htpasswd -n ', ie 'htpasswd -n admin' 31 | # Multiple users can be used separating them with commas 32 | users=[""] 33 | 34 | # Enable Docker backend 35 | [docker] 36 | endpoint="unix:///var/run/docker.sock" 37 | domain="localhost" 38 | watch=true 39 | swarmmode=true 40 | exposedbydefault=false 41 | 42 | # These options are for Traefik's integration with Let's Encrypt. 43 | # Your certificates are stored inside /acme.json inside the container, 44 | # which is ./acme.json on your server. 45 | [acme] 46 | email="" 47 | storage="acme.json" 48 | entryPoint="https" 49 | OnHostRule=true 50 | [acme.dnsChallenge] 51 | provider="" # i.e. "route53" 52 | [[acme.domains]] 53 | # Generate wildcard. Note that you MUST have a CAA record in your 54 | # DNS provider with the following value: 0 issuewild "letsencrypt.org" 55 | main="*.yourdomain.com" 56 | -------------------------------------------------------------------------------- /yourwp-docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | 3 | services: 4 | wordpress: 5 | # https://hub.docker.com/_/wordpress/ 6 | image: wordpress: 7 | networks: 8 | - 9 | environment: 10 | WORDPRESS_DB_HOST: 11 | WORDPRESS_DB_NAME: 12 | WORDPRESS_TABLE_PREFIX: 13 | WORDPRESS_DB_USER: 14 | WORDPRESS_DB_PASSWORD_FILE: /run/secrets/ # For local dev, WORDPRESS_DB_PASSWORD can be used instead 15 | volumes: 16 | - :/var/www/html # To persist WP data between restarts 17 | - ./php.ini:/var/www/html/php.ini 18 | secrets: 19 | - 20 | deploy: # Note that this is ignored when using docker-compose locally 21 | mode: replicated 22 | replicas: 1 23 | restart_policy: 24 | condition: on-failure 25 | max_attempts: 3 26 | labels: 27 | - "traefik.docker.network=" 28 | - "traefik.port=80" # The port the application is listening on 29 | - "traefik.frontend.rule=Host:yourapp.yourdomain.com" 30 | - "traefik.enable=true" # Set to true to expose the container and make it visible 31 | 32 | volumes: 33 | : 34 | 35 | networks: 36 | : 37 | external: true 38 | 39 | secrets: 40 | : 41 | external: true 42 | --------------------------------------------------------------------------------