Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Hosting Multiple Dockerized Websites on a Single Host

Video Demonstration

I've written about running multiple "dockerized" websites on a single host with the same port, but this required you to be able to assign multiple IPs to your host, which may not always be possible. The solution outlined below will allow you to deploy multiple websites on the same host by having the containers run on different public ports, but directing users to them through a reverse proxy. That way your users still don't need to type port numbers into the URI in order to get to your sites. This is a technique that I found on Jason Wilder's Blog. Its a simple solution which automatically registers and points to any website containers you spawn after the proxy container has initiated.

To get the frontend proxy running, you just need to run this command:

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

Then all you need to do is add the VIRTUAL_HOST environment variable to your container. This should be your site's domain.

  docker run \
  -e VIRTUAL_HOST=my.domain.com \
  -p 80 \
...

Now you should be able to view your website by plugging that $VIRTUAL_HOST into your browser (so long as your computer resolves that domain name to where you are hosting the container). Navigating to the server by IP won't work! Linux users can achieve this in a dev environment by just updating their /etc/hosts file.

Docker PHP Website Template

To simplify the creation of dockerized websites, I created a github template, which revolves primarily around two scripts which can be placed into any project, without any modifications necessary, in order to build and then run the container.

Build Script

Below is my build script. It will move the Dockerfile above all of my project's files

#!/bin/bash
if ! [ -n "$BASH_VERSION" ];then
    echo "this is not bash, calling self with bash....";
    SCRIPT=$(readlink -f "$0")
    /bin/bash $SCRIPT
    exit;
fi


# Ensure that we are running in this scripts directory
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT") 
cd $SCRIPTPATH

# Load settings (environment variables)
source ../../settings/docker_settings.sh

IMAGE_NAME="`echo $REGISTRY`/`echo $PROJECT_NAME`"

# Copy the docker file up and run it in order to build the container.
# We need to move the dockerfile up so that it can easily add everything to the container.
cp -f Dockerfile ../../.
cd ../../.

# Ask the user if they want to use the docker cache
read -p "Do you want to use a cached build (y/n)? " -n 1 -r
echo ""   # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    docker build --tag="$IMAGE_NAME" .
else
    docker build --no-cache --tag="$IMAGE_NAME" .
fi

docker push $IMAGE_NAME

echo "Run the container with the following command:"
echo "bash run-container.sh"

Run Script

#!/bin/bash

# ensure running bash
if ! [ -n "$BASH_VERSION" ];then
    echo "this is not bash, calling self with bash....";
    SCRIPT=$(readlink -f "$0")
    /bin/bash $SCRIPT
    exit;
fi

# Setup for relative paths.
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT") 
cd $SCRIPTPATH

# load the variables from the relative path
# Feel free to change this relative path.
source ../../settings/docker_settings.sh

CONTAINER_IMAGE="`echo $REGISTRY`/`echo $PROJECT_NAME`"

# Kill the container if it is already running.
docker kill $PROJECT_NAME
docker rm $PROJECT_NAME


#######################
# ensure that we are running the frontend proxy 
# which allows us to run multiple web containers
RESULT=`docker ps | grep jwilder | wc -l`

if [ $RESULT -gt 0 ];then
    echo "found frontend proxy."
else
    echo "Deploying frontend proxy"
    docker run -d \
    -p 80:80 \
    -v /var/run/docker.sock:/tmp/docker.sock \
    -t jwilder/nginx-proxy
fi
#######################


# Now start the container.
docker run -d \
  -e VIRTUAL_HOST=$VIRTUAL_HOST \
  -p 80 \
  -p 443 \
  --name="$PROJECT_NAME" \
  $CONTAINER_IMAGE

Settings

An example settings file that would go along with the previous two scripts is as follows

VIRTUAL_HOST="images.programster.org"
REGISTRY="docker-registry.programster.org:5000"
PROJECT_NAME="dev-programster-images"
Last updated: 8th August 2020
First published: 16th August 2018