How to Host Many Secure Websites on One Host With Docker and a Reverse Proxy

Why Use Traefik

Traefik is a powerful modern reverse-proxy program that makes it easy to manage API and web gateways. Including comprehensive support for modern standards such as middleware and multiple routing groups, it streamlines process of load-balancing and routing web traffic between multiple hosts. It is often used to route traffic between docker containers that are running web servers.

We can take advantage of this use case to host multiple containerized websites on the same host. Normally, only one Docker container can listen for traffic on the default HTTP port. This makes it difficult to run multiple websites on that host. Traefik helps us solve this problem by serving as an intermediary between the outside world and your docker network, intelligently routing traffic to web containers without requiring each of them to listen on a different port.

Containerization

Why not just use a regular web server with different configuration files for each site instead of containerizing everything? Well, containerization brings a number of benefits that make it a desirable alternative. Firstly, the isolated environment of each container offers enhanced security for each of your sites. It also allows us to control resources access for each site, and makes it easy to spin up new sites and take sites down for maintenance. Finally, the Traefik Docker container has a number of desirable extra features, such as middleware, ACME cert support, and support for multiple routers.

Set Up Traefik Container

The first step in using Traefik to run multiple websites on one host is to set up the Traefik docker container. To do this, we will use docker-compose, a handy way to define configuration for docker containers using a single “compose” file. We will define a single traefik container, based on the traefik:v2.6 image.

Much of the default configuration is shown in the docker-compose below.

Keys

  • If you want to use the Traefik API to provide a GUI for examining your connected routes, middleware, and containers, be sure to set the relevant settings and configurations to expose the Traefik dashboard securely.
  • Traefik has built-in support for TLS certificates so that your web containers can manage secure connections to clients over HTTPS. Take not of the ACME automatic SSL arguments that are passed to the Traefik container to enable this. Read more about automatic SSL here.
  • You will need to create entrypoints for both insecure HTTP traffic and secure HTTPS traffic. In my container’s example, I redirect all HTTP visitors to HTTPS, automatically upgrading all insecure connections to secure ones.
  • In the labels section, you will see a host rule. This is a rule that Traefik uses to determine where it should forward traffic to. In this case, it forwards connections looking for “yourdomain.com” to the Traefik dashboard.
  • You could define all your websites in this docker-compose if you wish. However, I like to keep each of them in their own separate docker-compose to make managing individual sites easier, and to keep their volumes more organized. If you do this, it is important to make sure all your web containers are an the same network as your Traefik container.
  • To generate credentials to secure the dashboard, see this article. Also, note that I had to double up all $ characters in the credential to get it to work properly.

The docker-compose.yml file for the Traefik container should look something like this:

version: '3.7'

services:
  traefik:
    image: traefik:v2.6
    container_name: traefik_router
    command:
      #settings and dashboard
      - "--api.insecure=false"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      #entrypoints
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      #redirect to https
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      #acme (letsencrypt) automatic ssl for websites
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=jtgraham38@gmail.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json"
    ports:
      - "80:80"   #http port mapped to host
      - "443:443" #https port mapped to host
    volumes:
      #mount the docker socket to traefik
      - /var/run/docker.sock:/var/run/docker.sock
      #map certs to container
      - ./letsencrypt:/etc/traefik/acme
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      #http configuration
      - "traefik.http.routers.http-traefik_dashboard.entrypoints=web"
      - "traefik.http.routers.http-traefik_dashboard.rule=Host(`yourdomain.com`)"
      #https configuration
      - "traefik.http.routers.traefik_dashboard.entrypoints=websecure"
      - "traefik.http.routers.traefik_dashboard.rule=Host(`yourdomain.com`)"
      #certificate resolver configuration
      - "traefik.http.routers.traefik_dashboard.tls=true"
      - "traefik.http.routers.traefik_dashboard.tls.certresolver=letsencrypt"
      #set the traefik docker network
      - "traefik.docker.network=client_websites_net"
      #dashboard configuration and credentials
      - "traefik.http.routers.traefik_dashboard.service=api@internal"
      - "traefik.http.routers.traefik_dashboard.middlewares=dashboard_auth"
      - "traefik.http.middlewares.dashboard_auth.basicauth.users=username:$$____$$________$$_GENERATED_HASH_HERE__"
    networks:
      - websites_net

networks:
  websites_net:  #define the network
    name: websites
    driver: bridge

Use docker-compose to start this container. In the directory with the docker-compose.yml file, run:

docker-compose up -d

So now, we have our router set up. The next step is to spin up our web containers, and connect them to our Traefik router.

Add Your Web Containers

After the Traefik router is set up, we need to configure our web containers to receive their designated traffic from the outside. Usually, I use a separate docker-compose in another folder to help keep all my projects organized, so once again, create another docker-compose.yml file.

Keys

  • Note that in this example, I use httpd, which is a containerized version of the Apache Web Server. Feel free to use other web servers too, such as nginx or caddy.
  • Labels are the key to correctly routing traffic from the outside to the correct container. Start by creating two configurations, one for HTTP and the other for HTTPS.
  • Add the domain that this container should respond to requests for to both the web and websecure entrypoints.
  • To enable HTTPS, set TLS to true, and set the certresolver to “letsencrypt”. This will allow Traefik to automatically retrieve free certificates for your websites from Let’s Encrypt.
  • Ensure that you add each web container to the correct docker network.
version: '3.7'

services:

  web_1:
    image: httpd
    restart: unless-stopped
    container_name: web_1
    volumes:
      - ./web:/usr/local/apache2/htdocs/
    labels:
      - "traefik.enable=true"
      #http config
      - "traefik.http.routers.http-web_1.entrypoints=web"
      - "traefik.http.routers.http-web_1.rule=Host(`yourseconddomain.com`)"
      #https configuration
      - "traefik.http.routers.web_1.entrypoints=websecure"
      - "traefik.http.routers.web_1.rule=Host(`yourseconddomain.com`)"
      #certificate resolver configuration
      - "traefik.http.routers.web_1.tls=true"
      - "traefik.http.routers.web_1.tls.certresolver=letsencrypt"
      - "traefik.docker.network=websites"
    networks:
      - websites_net

networks:
  websites_net:
    name: websites
    external: true

With that done, run:

docker-compose up -d

After your containers are up and running, if you did everything right, visit your website’s domain in a browser. Assuming the DNS is set up properly, your request will be forwarded from the host to the container with the corresponding domain rule set. If your connection is not secure, wait a minute and refresh, and Traefik will have retrieved a certificate from Let’s Encrypt automatically, and your connection will be upgraded.

Oh look, It works!

Conclusion

If you have followed these instructions carefully, you now have multiple containerized docker websites hosted on one device. Web traffic is intelligently routed to the proper container based on the requested host. And, this method comes with the added bonus of automatically retrieving TLS certificates so your sites can connect using HTTPS! I hope this knowledge proves useful to your hosting setup. I know it has for mine. Happy Coding!

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *