Docker, Nginx and Let’s Encrypt for a secure website
Tags: programming, certificate, docker, letsencrypt
Published: 2018-02-02 20:10:42

---
title: "Docker, Nginx and Let’s Encrypt for a secure website"
published: true
tags: Programming,certificate,docker,let's encrypt
canonical_url: https://stephenafamo.com/blog/docker-container-nginx-lets-encrypt/
---

 ![docker](https://i1.wp.com/stephenafamo.com/blog/wp-content/uploads/2017/03/docker.jpg?fit=1024%2C347&ssl=1 "docker")

Let me show you how I use Docker, Nginx and Let’s encrypt to host my websites and serve secure https content.

I use Docker to host a lot of my websites, and with Let’s Encrypt now issuing free SSL certificates, there is no excuse for not having one.

## Things to note

My test server is running on Ubuntu 16.04. Though the process should be similar on most other servers.

You should have pointed your domain nameservers or A records to the server.

I’ll use _ **example.com** _ for this tutorial, you should replace that with your domain name.

I’ll use _ **12.34.56.78** _ as the IP address of the server. You should replace this with your own IP

## Step 1: Install Docker

We will now install our beloved Docker using the command.

```
sudo apt install docker.io
```

For more ways to install docker, visit the official [docker installation page](https://docs.docker.com/engine/installation)

## Step 2: Set up our site with Docker

So, we’ll create a directory for our site, this will hold the code that we’ll mount put into the docker container.

```
sudo mkdir -p /var/www/example.com
```

Next, we’ll pull the container.

```
docker pull creativitykills/nginx-php-server:1.1.1
```

This is a container with nginx and php installed. The web root of the container is /var/www/public so we’ll have to mount our site there.

Next, we’ll start up our docker container.

```
docker run -d -p 1234:80 \
 --name example_site \
 -v /var/www/example.com:/var/www/public \
 creativitykills/nginx-php-server:1.1.1
```

With that command,

1. We ran the container creativitykills/nginx-php-server:1.1.1
2. We told docker to run this container&nbsp;in detached mode [-d]
3. We mapped the port 1234 on our server to port 80 of the container [-p 1234:80]
4. We named the container example\_site [–name example\_site]
5. We mounted our sites folder to the web root of the container. [-v /var/www/example.com:/var/www/public]

Lastly, we’ll add an index.html file in /var/www/example.com/index.html

```
<!-- /var/www/example.com/index.html -->
<!DOCTYPE html>
<html>
<head>
 <title>Example Site</title>
</head>
<body>
 <h2>This is my example site</h2>
</body>
</html>
```

If you visit _ **12.34.56.78:1234** _ now, you should see the following.

![example site](https://i2.wp.com/blog.stephenafamo.com/wp-content/uploads/2017/03/example.png?resize=274%2C73&ssl=1)<figcaption>example site</figcaption>

## Step 2:&nbsp;Point our domain to our docker container with Nginx

We’ll install Nginx on our server to use as a reverse proxy for our Docker containers. We can install it using the following command.

```
sudo apt install nginx
```

If you visit _ **example.com** _ now, you should see the default nginx&nbsp;page.

![ngins-default-page](https://i2.wp.com/blog.stephenafamo.com/wp-content/uploads/2017/03/nginx.png?resize=714%2C346&ssl=1)<figcaption>Nginx default page</figcaption>

To point our domain to our docker container, we have to set up some custom nginx configuration.

Add an example.com.conf file to /etc/nginx/sites-available.

```
touch /etc/nginx/sites-available/example.com.conf
```

use your favorite editor to add this to example.com.conf

```
server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location / {
   proxy_pass http://0.0.0.0:1234;
   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-Proto $scheme;
 }

}
```

Next, add example.com.conf to the sites-available list using this command.

```
ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf
```

Now, reload nginx&nbsp;so that our new configuration takes effect

```
/etc/init.d/nginx reload
```

If you visit _ **example.com** _&nbsp;now, you should see the following.

![example site](https://i2.wp.com/blog.stephenafamo.com/wp-content/uploads/2017/03/example.png?resize=274%2C73&ssl=1)<figcaption>example site</figcaption>

## &nbsp;Step 4: Obtain an SSL certificate from Let’s Encrypt

For issuing and renewing certificates, we’ll use the letsencrypt package.

Simply install it by running the following command:

```
sudo apt install letsencrypt
```

Next,&nbsp;generate a&nbsp;Diffie-Hellman group. Using this command

```
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
```

Now, we’ll adjust the nginx configuration to create the certificate.

Edit /etc/nginx/sites-available/example.com.conf and make it this.

```
server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   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-Proto $scheme;
 }

}
```

Reload nginx again for our configuration changes to take effect.

```
/etc/init.d/nginx reload
```

Obtain the certificates from let’s encrypt.

```
letsencrypt certonly -a webroot \
--webroot-path=/var/www/example.com \
-d example.com -d www.example.com
```

You will be asked to enter your email and agree to the terms and conditions.

After that, you should have your ssl certificates installed successfully.

## Step 5: Serve your site over HTTPS

We edit our example.com.conf once more to serve https content. Change it to this.

```
server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   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-Proto $scheme;
 }

}

server {
 listen 443 ssl;
 server_name example.com www.example.com;
 
 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
 ssl_session_cache shared:SSL:10m;
 ssl_session_timeout 5m;
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 ssl_prefer_server_ciphers on;
 ssl_dhparam /etc/ssl/certs/dhparam.pem;
 ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
 ssl_stapling on;
 ssl_stapling_verify on;
 add_header Strict-Transport-Security max-age=15768000;
 
 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   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-Proto $scheme;
 }
}
```

Reload nginx&nbsp;one more time!

```
/etc/init.d/nginx reload
```

You should now be able to access _ **https://example.com** _

## Step 6: Setup Auto Renewal for your SSL certificates

Let’s encrypt certificates expire after 90 days. The command for renewal is

```
letsencrypt renew
```

If you run it now, it’ll tell you that your certificates are not due for renewal.

What we’ll do is to set up a weekly cronjob that tries to renew the certificates, and then reloads nginx.

First, open the crontab as root

```
sudo crontab -e
```

Next add the following lines to the crontab.

```
30 2 * * 1 letsencrypt renew
35 2 * * 1 /etc/init.d/nginx reload
```

## Step 7: Redirect all http requests to https (Optional)

On the quest to make the web safer, it is better to serve all requests over https except you have a good reason not to. To redirect all http traffic to https, modify example.com.conf to this.

```
server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 return 301 https://example.com$request_uri;
}

server {
 listen 443 ssl;
 server_name example.com www.example.com;
 
 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
 ssl_session_cache shared:SSL:10m;
 ssl_session_timeout 5m;
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 ssl_prefer_server_ciphers on;
 ssl_dhparam /etc/ssl/certs/dhparam.pem;
 ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
 ssl_stapling on;
 ssl_stapling_verify on;
 add_header Strict-Transport-Security max-age=15768000;
 
 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   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-Proto $scheme;
 }
}
```

Now, reload nginx.

```
/etc/init.d/nginx reload
```

&nbsp;

That’s it!

The post [Docker, Nginx and Let’s Encrypt for a secure website](https://stephenafamo.com/blog/docker-container-nginx-lets-encrypt/) appeared first on [Stephen AfamO's Blog](https://stephenafamo.com/blog).