6 min read
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.
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
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
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,
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
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 page.
Nginx default page
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 so that our new configuration takes effect
/etc/init.d/nginx reload
If you visit example.com now, you should see the following.
example site
For issuing and renewing certificates, we’ll use the letsencrypt package.
Simply install it by running the following command:
sudo apt install letsencrypt
Next, generate a 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.
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 one more time!
/etc/init.d/nginx reload
You should now be able to access https://example.com
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
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
That’s it!
I build a lot of websites, and I run them in Docker containers. What happens is, I end up with URLs like this 0.0.0.0:1234. This is a problem becaus...
2 min read
WordPress is a very popular and flexible Content Management System (CMS). If you build websites, you are likely to have worked with (or had someone a...
6 min read
Comments