Setup an nginx-based webserver for multiple websites on Google Cloud Platform with Docker and Let’s Encrypt auto-updates
If you need to run multiple websites on one place, you can either get one of the preconfigured web-hosting solutions or setup one by yourself which allows you to have full control over your configuration. Thanks to docker the latter is easier nowadays then ever before.
- Google Cloud Platform setup (VM instance, IP-address, domain)
- Setup Docker and docker-compose
- Obtain Let’s Encrypt certificate and auto-renewal
Google Cloud Platform setup
Create a VM instance
The first thing you will need is a VM instance in GCP. You can create VM instances very easily under “Computing Engine” > VM instances.
Make sure to enable “Allow HTTP traffic” and “Allow HTTPS traffic”. This will set the right firewall settings for you. You can also set the Access scopes to “Allow full access to all Cloud APIs” which will make it easier for you to work with all GCP APIs in the future if you need.
Note that your VM instance has an IP assigned to it. This IP is temporary, which means it will change after you restart your instance. We want the instance to have a static IP address.
Create a static IP address
For that, we have to go over to “VPC network” > External IP addresses and “Reserve a static address”
Make sure to select your previously created VM instance in the “Attached to” dropdown.
Now that your VM instance has a fixed IP address, we need to create a DNS entry for a domain which will point to that address.
Point your domain to the VM instance/IP address
Important note: Depending on where you’ve registered your domain, you additionally need to point the domain to the previously created IP address in your registrars settings. Since your registrar and all the other root-servers in the world don’t know nothing about your intention to host your website on GCP. The propagation of the DNS can take up to 48 hours. As a workaround you may want to edit your hosts-file and point the IP address manually.
For detailed information on how DNS works: https://en.wikipedia.org/wiki/Domain_Name_System
So, let’s setup the DNS entry for your first domain, you want to point to your newly created VM instance. Go to “Network services” > Cloud DNS and create a new zone. Repeat this step for every domain you want to point to your future web server.
BONUS: If you want to use your domain’s emails with Google GSuite, this is the place where you can set the MX-records according to the GSuite guidlines. Otherwise you can ignore them.
Now that you’ve a piece of server where you can work on, an IP address, that points to that server and a domain, which points to that IP, you’re ready to move to the next step. Setup a web-server with Docker.
Install Docker and docker-compose
So to setup Docker we have to SSH into our VM instance and install docker there.
Head back to “Compute Engine” > VM instances and click on the SSH button next to your instance. A new window with a console will pop up and you’re ready to go.
To install Docker, follow the instructions as shown on the official Docker documentation: https://docs.docker.com/install/linux/docker-ce/debian/#set-up-the-repository
Do the same for docker-compose: https://docs.docker.com/compose/install/
To verify that everything is installed correctly, type `docker -v` and `docker-compose -v` to get the versions respectively.
Choose a Docker image
When you search for pre-configured web-server images, you will come across many good ones. I like to work with the WebDevOps Docker images, since I find them to be the best configured ones and you can use them in your docker-compose right out of the box without further configuration.
package: Dockerfiles from WebDevOps for PHP, Apache and Nginx (with PHP5 and PHP7)
For this tutorial I’m going to use the webdevops/php-nginx:ubuntu-17.04 tag. As the name already suggests, it’s an Ubuntu based image, with PHP and nginx. With that image, I was able to run WordPress, TYPO 3, CakePHP without any further configuration. Other frameworks like Laravel, Symfony, Drupal, etc. should work as well.
For MySQL, I’ll use the official mysql:latest Docker image.
Well, before we create our docker-compose.yml, let’s first start up a container and extract the configuration files for the webdevops-image so that you can add virtual hosts, SSL-certificates etc. more easily later on.
Type the following command into your console:
docker run --name webtmp -d webdevops/php-nginx:ubuntu-17.04
This will download and execute the Docker container in the background and give it a name we can refer to.
Now copy the nginx folder from the container to your home-folder:
docker cp webtmp:/opt/docker/etc/nginx .
Finally you must stop and delete the container with, so that we can run it with docker-compose later.
docker stop webtmp && docker rm webtmp
We need to create a docker-compose.yml that will start up a Docker container for the nginx web-server and one for the MySQL-server.
You can use my pre-configured docker-yompose.yml and modify it to your settings:
When done, run `docker-compose up -d` to start up the containers in the background.
As for now, you should have a running web-server, be able to create an index.html file in the html folder which will be mounted in the web-containers /var/www/html folder, which then should be shown when you browse to http://example.com.
This also allows you to SFTP into your server and copy all your files into the html folder without worrying about colliding read/write permissions.
I’d recommend to create a new subfolder for each domain within the html-folder and thus setup the nginx virtual hosts.
Let’s move on to the Let’s Encrypt certificates.
Obtain a Let’s Encrypt certificate for your domain and setup auto-renewal cronjob
If the DNS propagation isn’t done by now, you have to postpone this step until it’s done.
To generate and renew Let’s Encrypt certificates we well use the official certbot Docker image. Since we don’t need this image to run continuously in the background, simply run the container with Docker and follow the instructions.
docker run --rm -v nginx/ssl:/etc/letsencrypt -v html:/webroot certbot/certbot certonly --webroot
Repeat this step for every domain you want to obtain a certificate for.
Your obtained SSL-certificate should now be in the nginx/ssl folder.
Make sure, your nginx/ssl/renewal/example.com.conf has the proper webroot_map set, otherwise the renewal won’t work properly.
example.com = /webroot/example.com
Setup cronjob for renewal
Finally, we will create a cronjob that runs every day at 01:00AM to renew all the certificates.
I’ve written a small shell script that runs the certbot renewal and restarts the nginx server for the changes to take effect. It also logs the output to a file in your home folder. Feel free to use it and adapt it to your needs.
Opening up the cron editor we can execute the script every day at 01:00AM.
Add the following line to your cronfile and save.
0 1 * * * sh /home/YOUR_GCP_USERNAME/cronjob_renew_letsencrypt.sh
We’re done folks :-) As you can see, It’s that simple to set up a web-server on Google Cloud Platform with Docker.
Let me know in the comments if you have troubles or any recommendations.