Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / containers / docker

Host Multiple Websites on a Single Host with Docker

5.00/5 (4 votes)
29 Sep 2016CPOL4 min read 20.4K  
How to host multiple websites on a single host with Docker

Docker - host multiple subdomains
Docker becomes more and more suitable for personal environments, especially with private servers, which can be migrated very often.

A developer usually has more than one app living on his own private server such as a blog, some development apps like Jenkins, GitLab and so on. These apps are likely to be using the standard web port 80. As this port is already bound for your main site for example, your Docker instances will not be accessible throughout this one.

This post will show you one way to host multiples applications such as a blog, a personal website and many others, on a single host using Docker containers.

Target Architecture

The ideal architecture for hosting multiples apps within a dedicated server would be to expose each application on port 80 through a specific sub-domain (blog.domain.com, jenkins.domain.com, gitlab.domain.com).

Using Nginx as a reverse-proxy

These requirements can be achieved using a proxy (also called reverse-proxy). Here is a diagram:

Docker - host multiple subdomains - Nginx version

This classic architecture could be implemented using Nginx as a reverse-proxy, but this solution comes with inconveniences:

  • necessity to write a configuration file per application/container to proxy
  • reload Nginx each time an application or a container is added to the architecture

Using nginx-proxy from Jason Wilder

Nginx-proxy (https://github.com/jwilder/nginx-proxy) consists in a simple Nginx server and docker-gen (https://github.com/jwilder/docker-gen). Docker-gen is a small tool written in Go which can be used to generate Nginx/HAProxy configuration files using Docker containers meta-data (obtained via the Docker API).

These two applications are running as a Docker container and so are easy to get up running. Once started, nginx-proxy will act as a reverse proxy between your host and all your sub-domains (blog.domain.com, jenkins.domain.com, etc.), effectively routing incoming requests using the VIRTUAL_HOST environment variable (if set, for each Docker containers).

To proxy a Docker container, you basically have to expose the port the applications uses (for example 80 for WordPress) and add the VIRTUAL_HOST environment variable to the container:
Using docker run command:

docker run -d --expose 80 -e VIRTUAL_HOST=blog.domain.com wordpress

Via docker-compose.yml file:

wordpress:
  image: wordpress
  links:
    - db:mysql
  expose:
    - 80
  environment:
    - "VIRTUAL_HOST=blog.domain.com"
db:
  image: mariadb
  environment:
    MYSQL_ROOT_PASSWORD: example

The following configuration could be represented like this:

Docker - host multiple subdomains
As you can see above, the Nginx-proxy listens on http standard port (80) and forwards incoming requests to the appropriate container. We will see later how this routing is made.

Starting Nginx-proxy

To start the nginx-proxy, type the following command:

docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy

Using docker-compose syntax:

nginx-proxy:
  image: jwilder/nginx-proxy
  ports:
    - "80:80"
  volumes:
    - /var/run/docker.sock:/tmp/docker.sock

How It Works?

As you can guess with the last command, the nginx-proxy container listens on port 80 and has access to the host Docker socket. By giving the Docker host socket, the nginx-proxy container will be able to receive Docker events (i.e., container creations, shutdowns, etc.), and react to them.

At its startup, the nginx-proxy container will look for containers with the VIRTUAL_HOST environment variable set and create appropriate basic Nginx configuration file for each of them. These configuration files will tell Nginx how to forward incoming requests to these containers.

Then, each time a container starts, the nginx-proxy will receive an event and create Nginx configuration needed to serve the container application and reload Nginx.

Routing Requests using VIRTUAL_HOST Environment Variable

Nginx-proxy will route requests to containers, according to the VIRTUAL_HOST environment variable of each container. This means that if you want a container to be served with a specific domain or subdomain, you have to launch this one with the VIRTUAL_HOST environment variable.

Here is an example:

# Launch WordPress (db part omitted for clarity)
docker run -d --name blog --expose 80 -e VIRTUAL_HOST=blog.domain.com wordpress
# Launch Jenkins
docker run -d --name jenkinsci --expose 8080 
-e VIRTUAL_HOST=jenkins.domain.com -e VIRTUAL_PORT=8080 jenkins

Again, here is the equivalent configuration for the Jenkins instance, using docker-compose syntax:

jenkins:
  image: jenkins
  expose:
    - 8080
    - 50000
  environment:
    - "VIRTUAL_HOST=jenkins.domain.com"
    - "VIRTUAL_PORT=8080"
  volumes:
    - "/your/home:/var/jenkins_home"

Note: The port used by the application inside the container must be exposed for nginx-proxy to see it. If the application exposes multiple ports, you have to tell nginx-proxy which port to proxy using the VIRTUAL_PORT environment variable.

In this example, nginx-proxy will forward all requests matching with blog.domain.com url to the WordPress container.
However, all requests beginning by the url jenkins.domain.com will be forwarded to the Jenkins container.

This tool is really simple and gives great flexibility. It allows running multiple Docker containers in the same dedicated server, without writing much configuration.

Tip

Map a container to multiple domains:

A common requirement is using multiple domains for a given container. To do this, simply add hosts to VIRTUAL_HOST variable like this: VIRTUAL_HOST=domain.com,www.domain.com,home.domain.com

Further documentation can be found at the following URL:

The post Host multiple websites on a single host with Docker appeared first on Florian Lopes's blog.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)