Last Updated:

Host multiple sites on a single VPS with Docker and Nginx

Docker

Docker is a great tool for running multiple services on a single VPS without them interacting with each other – for example, one website is built on WordPress and the other is built on Ghost or some other CMS.

Table of Contents

But the containerization of software leads to another problem: how to host several sites, each in a separate Docker container, on one VDS server? Fortunately, with a little foresight and customization, you can use Docker and Nginx to host multiple websites from a single VPS.

By default, all Docker services listen on port 80, which creates conflicts for inbound traffic. Sure, you can change the listening port, but no one wants to enter andreyex.ru:27364 to access their favorite site.

What if instead you could use nginx to route traffic coming from the andreyex.ru to a unique container listening on port 27364 and to route traffic coming into the anotherandreyex.ru a second container listening on 43567?

This is exactly what nginx-proxy does: it listens to port 80 (the standard HTTP port) and redirects incoming requests to the appropriate container. This is often referred to as a reverse proxy and uses the variable VIRTUAL_HOST in Docker.

In this article, we'll set up an nginx-proxy and learn how to use Docker and Nginx to route traffic to different containers, allowing you to host multiple sites in different domains from the same site.

 

Background

  • Ubuntu/Debian/CentOS server with Docker installed – check out docker installation article first.
  • A non-rooted user with sudo privileges.
  • Fully qualified domain name from the domain registrar.
  • Possibility to change DNS settings through your domain's registrar.

 

Step 1. Running nginx-proxy to connect Docker and Nginx

First, let's run the nginx-proxy container. This can be done either with a single docker command or with docker-compose. Let's look at both.

But before we do, let's create a Docker network. The network will allow our containers to "communicate" with each other.

$ docker network create nginx-proxy

 

Now we need to add new containers to this nginx-proxy Docker network.

 

Install nginx proxy using Docker

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

 

Installing the nginx proxy using docker-compose

First, create a new docker-compose.yml file in the directory of your choice (one named nginx-proxy) and copy the following text:

version: "3"
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

networks:
  default:
    external:
      name: nginx-proxy

 

And then run the following docker-compose command to get started.

$ docker-compose up -d

 

How nginx-proxy uses Docker and Nginx to host multiple sites on a single VPS

First, the nginx-proxy container listens on port 80. All traffic coming to the VPS through port 80 will pass through it nginx-proxy. That's a good first step. But how does he know whether to send person A to blog.example.com and person B to app.example.com?

This is what the string /var/run/docker.sock:/tmp/docker.sock does. Essentially, this gives any container access to the host's Docker socket, which contains information about various Docker events, such as creating a new container or closing it.

Every time you add a new Docker container to your network, nginx-proxy scans the event over the socket, automatically creates the configuration file needed to route the traffic, and reboots nginx so that the changes are immediately available.

The proxy looks for containers with the VIRTUAL_HOST variable enabled. In this variable, you can specify the destination, like the blog.example.com, or app.example.com. The proxy server will see that Person A has requested blog.example.com and will forward (or proxy server!) their request to the correct container. The same goes for app.example.com or any other containers you want to run.

Also note the string —net nginx-proxy in the Docker command and the networks: default: external: name: nginx-proxy block in the docker-compose.yml file. This way, you specify which Docker network you want to use in the new container.

 

Step 2. Add your first Docker container to nginx proxy

Now that we have started nginx-proxy, we can start adding new containers that will be automatically picked up and configured. Since we covered this in the article on installing Docker, and since it's a simple implementation, try using WordPress as an example.

 

Using Docker

Running a WordPress container with a basic configuration is pretty simple.

$ docker run -d --name blog --expose 80 --net nginx-proxy -e VIRTUAL_HOST=blog.DOMAIN.TLD wordpress

 

Notice a few elements here. —expose 80 will allow traffic to enter the container on port 80. —net nginx-proxy ensures that we are using the Docker network we created earlier. Finally, the -e flags VIRTUAL_HOST=blog. DOMAIN. TLD to create an nginx-proxy and route any traffic requesting this domain to this new Docker container. Replace the DOMAIN domain. TLD of your choice.

 

Using docker-compose

Start by creating a 'docker-compose.yml' file in an empty directory and copy it as follows.

version: "3"

services:
   db_node_domain:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: PASS123
       MYSQL_DATABASE: DBNAME
       MYSQL_USER: USER
       MYSQL_PASSWORD: PASS123
     container_name: wordpress_db

   wordpress:
     depends_on:
       - db_node_domain
     image: wordpress:latest
     expose:
       - 80
     restart: always
     environment:
       VIRTUAL_HOST: blog.DOMAIN.TLD
       WORDPRESS_DB_HOST: db_node_domain:3306
       WORDPRESS_DB_USER: adminx1960
       WORDPRESS_DB_PASSWORD: PASS123
     container_name: wordpress
volumes:
    db_data:

networks:
  default:
    external:
      name: nginx-proxy

 

Again, take note of the expose and environment: settings in the VIRTUAL_HOST file. Also, the networks option at the bottom is needed to allow this container to "talk" to nginx-proxy.

You'll also want to change the passwords for the MySQL database itself and for the WordPress user who accesses the specified database. WordPress will automatically create a new password for your WordPress admin account during the installation process.

Once the configuration file is complete, you can run the docker-compose up -d command.

 

Step 3. Visit your new WordPress site and add more

At this point, you need to configure your domain's DNS settings to point to the IP address of your VPS. If you have set up your blog blog. DOMAIN. TLD, create an A record for this subdomain and point to the same IP address.

With the right DNS setup, you'll be able to visit and see the famous 5-minute WordPress installation! Congratulations!

Now you can upload any other Docker containers, such as another WordPress site or any other type of application/service, and nginx-proxy will take care of the rest.

 

One Caveat for Multiple WordPress Installations

If you are going to host multiple WordPress blogs using this method, you need to create a unique database name for each instance. By doing so, you connect WordPress A to MySQL A, and WordPress B connects to MySQL B, and so on.

See the line just below services? The one that reads db_node_domain? This is the name of your container. You can choose any naming scheme you want, but each instance must have its own name.

Once you've chosen a name, you need to update the other two fields.

First:

depends_on:

— db_node_domain

 

And the second thing:

WORDPRESS_DB_HOST: db_node_domain:3306

 

Additional resources for hosting multiple sites

Of course, don't forget to check out the extensive documentation for nginx-proxy to learn more about how you can configure some more complex proxies between Docker and Nginx, such as using ssl, multi-port, or multiple networks.

We haven't tested it yet, but there is a "satellite" for nginx-proxy, the letsencrypt-nginx-proxy-companion call, which allows you to create/renew Let's Encrypt certificates automatically directly along with the proxy itself.

 

If you find an error, please select the text fragment and press Ctrl+Enter.