Docker and nginx perfect match

Currently my server is all containeraized by docker. Each web, service, whatever is a container that can be started at any time.

For example, there is a really simple webapp that is just a static html https://points.allocsoc.net,

Creating the first container

The docker file to create the image for this webapp is super easy :)

FROM nginx

ADD html/index.html /usr/share/nginx/html  

What basically means is, please create a new container on nginx and always serve this index.html.

And for start our container I would just

docker build -t points --rm .  
docker run -d --name points -p 80:80 points  

And on my server, all request on the port 80 will return the static html, Hurra! our first container :)

Containers with virtual hosts

Okay perfect! Seems that this docker thing works perfectly, now let's imagine we also want to host our blog using the ghost blog engine.

Wow! there is already a ghost container image, perfect let's start our new container!

$ docker run -p 80:2368 --name blog ghost

docker: Error response from daemon: driver failed programming external connectivity on endpoint blog2 (3ec45d368b4f3e355849f51842ea502f69b41d2eeea138210122ae7f5850408b): Bind for 0.0.0.0:80 failed: port is already allocated.  

Wait! why this don't work?! well... you have only one 80 port and there is already a container using it...

How we can solve it? we can use nginx-proxy container. The nginx-proxy container has a special ability to listen for new containers, and will proxy all the request to the corresponding container.

To let nginx-proxy know to which container to proxy, we have to add some new environment variables to the containers.

  1. VIRTUAL_HOST: which host domains or subdomains will respond for each container
  2. VIRTUALPORT: If we need to specify on which port we want to proxy, normally the containers only expose one port, so we don't have to specify the VIRTUALPORT

See the following example

## Start nginx-proxy on port 80 of the host machine
docker run -d -p 80:80 -p 443:443 --name nginx \  
 -v /var/run/docker.sock:/tmp/docker.sock:ro \
 jwilder/nginx-proxy


## Start the static file container that will be reachable throught the url points.allocsoc.net 
docker run -d \  
  --name points \
  -e "VIRTUAL_HOST=points.allocsoc.net" 
  points

## Start the blog container that would be reachable from blog.allocsoc.net
docker run -d \  
  --name blog \
  -e VIRTUAL_HOST="blog.allocsoc.net,*.coscolla.net,allocsoc.net,coscolla.net" \
  -e VIRTUAL_PORT=2369\
  ghost

When you need ssl and you don't want to pay

Currently I'm developing a pet project that uses the dropbox API to store files, and guess what, I want to host this project in my docker server!

Problem is, dropbox force you to use a oauth redirect link to an https url, and I don't want to pay for a ssl certificate.

Hopefully, there is a new kid in town named Let's Encrypt, Let's encrypt is a company that grants you ssl certs for free with an automatic tool that detects if your own the domain.

One difference with the traditional CA is that the issued certificates fo let's encrypt are only valid for 3 months, so you have to maintain it.

See this links for more information https://letsencrypt.org/getting-started/

Add let's encrypt to our containers!

The idea behind letsencrypt-nginx-proxy-companion is the same as in nginx-proxy, is a container that will listen to other containers start events. And with a couple of new environment variables will ask to https://letsencrypt.org the certs, keys and everything.

Here is an extract from my current docker configuration :)

NGINX_CERTS=`pwd`/../machines/nginx/certs  
echo $NGINX_CERTS

docker run -d -p 80:80 -p 443:443 --name nginx \  
  -v /var/run/docker.sock:/tmp/docker.sock:ro \
  -v /usr/share/nginx/html \
  -v /etc/nginx/vhost.d \
  -v $NGINX_CERTS:/etc/nginx/certs:ro \
  jwilder/nginx-proxy


docker run -d \  
  --name nginx-letsencrypt \
  -e "DEBUG=true" \
  -v $NGINX_CERTS:/etc/nginx/certs:rw \
  --volumes-from nginx \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  jrcs/letsencrypt-nginx-proxy-companion

## let's make points work on https, it's free!!!
docker run -d \  
  --name points \
  -e "VIRTUAL_HOST=points.allocsoc.net" \
  -e "LETSENCRYPT_HOST=allocsoc.net" \
  -e "LETSENCRYPT_EMAIL=kozko2001@gmail.com" \
  points

Once start the points container we can do docker logs nginx-letsencrypt to see all the request and what is going on under the hood.

Also while the nginx-letsencrypt container is up, will maintain renew all the certs each three months, so less things to worry about.

Refs