Docker logo

Suppose you have an Ubuntu instance (Google Compute Engine, Amazon EC2, etc.) running a web app via Nginx inside a Docker container and you would like the app to communicate over HTTPS.

There are numerous posts online on how to do this, usually using Nginx as a reverse proxy and Docker Compose. This is a powerful and general solution, but if you want a stopgap, read on.

Step 1

As Nginx is currently running inside a Docker container, it probably isn’t installed yet on the host instance itself

sudo apt-get update
sudo apt-get install nginx

Step 2

Let’s Encrypt is a free SSL/TLS certificate provider accepted by modern browsers for HTTPS communication. Certficates expire after three months and are renewable.

Installation and configuration is easiest via certbot (follow the instructions under Install).

Step 3

sudo certbot --nginx -d <mydomain>

replacing <mydomain> with the domain your app is currently being served from:

Running this command will get a certificate for you and have Certbot edit your Nginx configuration automatically to serve it.

When prompted, select "redirect all HTTP requests to HTTPS".

At this stage, you can check everything works by starting Nginx (and stopping any processes running on ports 80 and 443)

sudo systemctl start nginx

then navigating in a browser to <mydomain>. You should see a padlock in the address bar and the default Debian-Nginx message.

After checking,

sudo systemctl stop nginx

For reference, these are the nginx configuration files produced by Certbot:

/etc/nginx/sites-available/default

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}
}


# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
#       listen 80;
#       listen [::]:80;
#
#       server_name example.com;
#
#       root /var/www/example.com;
#       index index.html;
#
#       location / {
#               try_files $uri $uri/ =404;
#       }
#}

server {

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;
    server_name <mydomain>; # managed by Certbot


        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}


    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/<mydomain>/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/<mydomain>/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = <mydomain>) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80 ;
        listen [::]:80 ;
    server_name <mydomain>;
    return 404; # managed by Certbot


}

/etc/letsencrypt/options-ssl-nginx.conf

# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file.

ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

ssl_ciphers "xxxxxx";

/etc/letsencrypt/ssl-dhparams.pem

-----BEGIN DH PARAMETERS-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END DH PARAMETERS-----

Step 4

Assuming your app’s Dockerfile contains a line similar to

COPY ./path/to/my/html /usr/share/nginx/html

then in /etc/nginx/sites-enabled/default on the host instance, replace

root /var/www/html;

with

root /usr/share/nginx/html;

and set the server name

server_name <mydomain>;

Step 5

Add the below lines to the app’s Dockerfile

RUN addgroup -g 1000 -S www-data \
&& adduser -u 1000 -D -S -G www-data www-data

and rebuild the Docker image.

Step 6

Run the new Docker image, with some extra options:

--publish=443:443

to open up port 443;

--volume=/etc/letsencrypt/:/etc/letsencrypt/

to get the certificates (and other required Let’s Encrypt files) on the host instance onto the container;

--volume=/etc/nginx/:/etc/nginx/

to ensure the container has the same Nginx configurations as the host instance.

The last option is why in Step 5 the www-data user needs creating.

Step 7

Navigate in the browser to <mydomain>. You should see your app running over HTTPS.