Albert Callarisa Roca

Tech blog

Zero downtime deployment of any web app with Docker and Nginx

17 Jul 2014

In this post I'll explain how to deploy any web app in Docker with zero downtime using nginx. I used this setup with Node.js, Go and Ruby web apps, but it should work with any web app.

The main idea is to have multiple containers running our web app and a container running nginx with our web server as upstream. When we start new web app instances we just update the upstream to point to the new instances and reload nginx config.

First things first, start two app containers:

docker run -d --name app1 -p :3000 app
docker run -d --name app2 -p :3000 app

Next we will run nginx with the following extra config files:

api.conf:

server {
  listen 4000;

  location / {
    proxy_pass http://api_servers;
  }
}

api_upstream.conf:

upstream api_servers {
  server localhost:3000;
}

Now, when we run the nginx container, we should create a volume on the folder where we store the config files. Something like:

docker run -d -v /opt/nginx/sites-available --name nginx nginx
Assuming we have those two config files in /opt/nginx/sites-available.

Since the config files are available as a volume, we can run a new container to update the upstream config file. For that we will need to get the ports mapped of the web servers doing docker ps or docker port app1 3000. Assuming the host IP is 10.0.1.84 we should now update the upstream config file of nginx:

docker run --rm --volumes-from nginx nginx bash -c '/
echo "upstream api_servers {\n/
  server http://10.0.1.84:$(docker port app1 3000);\n/
  server http://10.0.1.84:$(docker port app2 3000);\n/
}" > /opt/nginx/sites-available/api_upstream.conf'

Once this is done, we can now tell nginx to reload the config:

docker kill -s HUP nginx
And from now on nginx will be proxying the requests to both upstream servers.

The steps to deploy a new code using this setup will be:

  1. Update the web app image
  2. Run the new app containers
  3. Update again the upstream config file with the new ports
  4. Reload nginx config

I hope this is useful.