Host multiple subdomains/applications on a single host using Docker

13th Mar 2022
Table of contents

Host multiple subdomains/applications on a single host using 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 have 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 to 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
Docker - host multiple subdomains - Nginx version

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

  • 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 consists in a simple Nginx server and 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 - Nginx version

Docker - host multiple subdomains

As you can see above, the Nginx-proxy listens on http standard port (80) and forward 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:

shell 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

Update:

As Moon suggested in his comment, you could add a piece of extra security to hide Nginx server version using a custom configuration file:

server_tokens off;

To make nginx-proxy use your custom Nginx config file, launch it with this flag:

-v /path/to/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro

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 (ie. 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 the underlying containers.

Then, each time a container starts, the nginx-proxy will receive an event and create an appropriate 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 desired 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.

Bạn thấy bài viết này như thế nào?
1 reaction

Add new comment

Image CAPTCHA
Enter the characters shown in the image.
Câu nói tâm đắc: “Điều tuyệt với nhất trong cuộc sống là làm được những việc mà người khác tin là không thể!”

Related Articles

Trong blog CI-CD CHO DỰ ÁN CỦA BẠN mình đã giới thiệu về CI-CD và apply Gitlab CI vào dự án của bạn.

Xin chào các bạn, mình đã trở lại với bài viết chia sẻ đầu tiên về kiến thức lập trình. Như những chia sẻ ở phần giới thiệu bản thân, mình là một coder

Now, I didn’t publish the service on any port. I will, in fact, add another service that will serve as our management interface of the database, phpmyadmin:

Docker provides a single command that will clean up any resources — images, containers, volumes, and networks — that are dangling (not tagged or associated with a container):

Elastic Search already has docker image available. So lets use that Docker image to setup elastic search with WordPress.