title | description | author | tags | date_published |
---|---|---|---|---|
Running an NGINX Reverse Proxy with Docker and Let's Encrypt on Google Compute Engine |
Learn to serve multiple websites simultaneously in a single Compute Engine instance with Docker and NGINX. Also, learn how to secure the sites with Let's Encrypt. |
tswast |
Compute Engine, NGINX, Docker, Let's Encrypt |
2017-04-19 |
This tutorial will guide you through running multiple websites on a Google Compute Engine instance using Docker. You will secure the websites using free SSL/TLS certificates from Let's Encrypt.
- Create a Compute Engine instance.
- Run an NGINX reverse proxy.
- Run multiple web applications in Docker.
- Install SSL/TLS certificates with Let's Encrypt.
- Create or select a Cloud Platform project from the Google Cloud Platform console's projects page.
- Enable billing for your project.
This tutorial uses billable components of Cloud Platform including
Use the Pricing Calculator to estimate the costs for your usage.
Create a new Compute Engine instance using the CoreOS stable image. CoreOS comes with Docker pre-installed and supports automatic system updates.
- Open the Google Cloud Platform console.
- Create a new Compute Engine instance.
- Select the desired Zone, such as "us-central1-f".
- Select the desired Machine type, such as "micro" (f1-micro).
- Change the Boot disk to "CoreOS stable".
- Check the boxes to allow HTTP and HTTPS traffic in the Firewall section.
- Expand the Management, disk, networking section.
- Click the Networking tab.
- Select New static IP address under External IP.
- Give the IP address a name, such as "reverse-proxy".
- Click the Create button to create the Compute Engine instance.
Create multiple A type DNS records for various domains/subdomains on your DNS provider pointing at the external IP address for your new instance.
For example, in Google Domains, open DNS for your domain, scroll to Custom resource records and add an A type record. The name "@" corresponds to the root of your domain or you can change it to a subdomain, such as "a" and "b".
This tutorial will assume you have two subdomains with A records:
- a.example.com
- b.example.com
To have the separate websites respond only to their respective hosts, you'll use a reverse proxy. This tutorial uses the nginx-proxy Docker container to automatically configure NGINX to forward requests to the corresponding website.
As an example, this tutorial will show a plain NGINX server running as site A and a plain Apache server running as site B.
-
Run the reverse proxy.
docker run -d \ --name nginx-proxy \ -p 80:80 \ -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
-
Start the container for site A, specifying the domain name in the
VIRTUAL_HOST
variable.docker run -d --name site-a -e VIRTUAL_HOST=a.example.com nginx
-
Check out your website at http://a.example.com.
-
With site A still running, start the container for site B.
docker run -d --name site-b -e VIRTUAL_HOST=b.example.com httpd
-
Check out site B at http://b.example.com.
Congratulations, you are running multiple apps on the same host using Docker and an nginx reverse proxy.
Note: If you do not wish to set up HTTPS for your websites using Let's Encrypt, you can skip reading the rest of this tutorial.
Plain HTTP is not secure. It is not encrypted and is vulnerable to man-in-the-middle attacks. In this step, you'll add support for the HTTPS protocol.
-
Stop the containers.
docker stop site-a docker stop site-b docker stop nginx-proxy
-
Remove the containers.
docker rm site-a docker rm site-b docker rm nginx-proxy
To enable HTTPS via TLS/SSL, your reverse proxy requires cryptographic certificates. Use Let's Encrypt via the Docker Let's Encrypt nginx-proxy companion to automatically issue and use signed certificates.
-
Create a directory to hold the certificates.
cd mkdir certs
-
Run the proxy, but this time declaring volumes so that the Let's Encrypt companion can populate them with certificates.
docker run -d -p 80:80 -p 443:443 \ --name nginx-proxy \ -v $HOME/certs:/etc/nginx/certs:ro \ -v /etc/nginx/vhost.d \ -v /usr/share/nginx/html \ -v /var/run/docker.sock:/tmp/docker.sock:ro \ --label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true \ jwilder/nginx-proxy
-
Run the Let's Encrypt companion container.
docker run -d \ --name nginx-letsencrypt \ --volumes-from nginx-proxy \ -v $HOME/certs:/etc/nginx/certs:rw \ -v /var/run/docker.sock:/var/run/docker.sock:ro \ jrcs/letsencrypt-nginx-proxy-companion
-
Run site A.
In addition to
VIRTUAL_HOST
, specifyLETSENCRYPT_HOST
to declare the host name to use for the HTTPS certificate. Specify theLETSENCRYPT_EMAIL
so that Let's Encrypt can email you about certificate expirations.docker run -d \ --name site-a \ -e 'LETSENCRYPT_EMAIL=webmaster@example.com' \ -e 'LETSENCRYPT_HOST=a.example.com' \ -e 'VIRTUAL_HOST=a.example.com' nginx
-
You can watch the companion creator request new certificates by watching the logs.
docker logs nginx-letsencrypt
You should eventually see a log which says
Saving cert.pem
. -
After the certificate is issued, check out your website at https://a.example.com.
-
Run site B.
docker run -d \ --name site-b \ -e 'LETSENCRYPT_EMAIL=webmaster@example.com' \ -e 'LETSENCRYPT_HOST=b.example.com' \ -e 'VIRTUAL_HOST=b.example.com' httpd
-
You can watch the companion creator request new certificates by watching the logs.
docker logs nginx-letsencrypt
You should eventually see a log which says
Saving cert.pem
. -
After the certificate is issued, check out your website at https://b.example.com.
Congratulations, your web apps are now running behind an HTTPS reverse proxy.
In order to proxy the nginx-proxy
container and the web app container must be on
the same Docker network.
When you run a multi-container web app with docker-compose
, Docker attaches the
containers to a default network.
The default network is different from the bridge network that containers run with
the docker run
command attach to.
If you run the docker-compose
and have specified a VIRTUAL_HOST
environment variable in the docker-compose.yml
configuration file,
you'll see this error message in the docker logs nginx-proxy
output:
no servers are inside upstream
The proxy will also stop working. To resolve this,
-
Create a new Docker network.
docker network create --driver bridge reverse-proxy
-
Stop and remove your web application containers, the
nginx-proxy
container, and thenginx-letsencrypt
container.docker stop my-container docker rm my-container docker stop nginx-proxy docker rm nginx-proxy docker stop nginx-letsencrypt docker rm nginx-letsencrypt
-
Run the proxy and other containers, specifying the network with the
--net reverse-proxy
command-line parameter.Run the proxy container.
docker run -d -p 80:80 -p 443:443 \ --name nginx-proxy \ --net reverse-proxy \ -v $HOME/certs:/etc/nginx/certs:ro \ -v /etc/nginx/vhost.d \ -v /usr/share/nginx/html \ -v /var/run/docker.sock:/tmp/docker.sock:ro \ --label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true \ jwilder/nginx-proxy
Run the Let's Encrypt helper container.
docker run -d \ --name nginx-letsencrypt \ --net reverse-proxy \ --volumes-from nginx-proxy \ -v $HOME/certs:/etc/nginx/certs:rw \ -v /var/run/docker.sock:/var/run/docker.sock:ro \ jrcs/letsencrypt-nginx-proxy-companion
Run your website containers.
docker run -d \ --name site-a \ --net reverse-proxy \ -e 'LETSENCRYPT_EMAIL=webmaster@example.com' \ -e 'LETSENCRYPT_HOST=a.example.com' \ -e 'VIRTUAL_HOST=a.example.com' nginx
-
Modify the
docker-compose.yml
file to include the network you created in the networks definition.networks: reverse-proxy: external: name: reverse-proxy back: driver: bridge
In the container definitions, specify the appropriate networks. Only the web server needs to be on the reverse-proxy network. The other containers can stay on their own network.
The final
docker-compose.yml
file will look something like this:version: '2' services: db: restart: always image: my_database networks: - back web: restart: always image: my_webserver networks: - reverse-proxy - back environment: - VIRTUAL_PORT=1234 - VIRTUAL_HOST=c.example.com - LETSENCRYPT_HOST=c.example.com - LETSENCRYPT_EMAIL=webmaster@example.com networks: reverse-proxy: external: name: reverse-proxy back: driver: bridge
-
Run the
docker-compose up -d
command to run your composed containers with the new configuration.
When your Compute Engine instance restarts, the Docker containers will not
automatically restart. Use the --restart
flag for the docker run
command to
specify a Docker restart
policy.
I suggest always
or unless-stopped
so that Docker restarts the containers
on reboot.
Running many web apps on a single host behind a reverse proxy is an efficient way to run hobby applications. To make your experience even better,
Note that apps deployed to a single instance are not highly available. For example, your applications will not be available during a system reboot.
To see how to run an app which requires high availability or scaling to many queries per second, try out some more scalable ways of hosting.
- Deploy a scalable web app using App Engine flexible environment.
- Host a static website using Firebase Hosting.
- Host a static website using Cloud Storage.