Configure a Web Server in Docker with an Nginx Reverse Proxy

In this blog, I'll provide an overview of how to set up a web server in Docker and use an Nginx proxy to route traffic to the web application. It covers the steps needed to create and configure the Docker container, install and configure Nginx, and create the necessary configuration files. The blog also provides advice on setting up an SSL certificate for secure communication and making sure the web server is optimized for performance.

But Why?

Why would you want to host a website behind a reverse proxy instead of just hosting it through a hosting provider? Why use Docker? And what is a reverse proxy, anyway?

One of the main benefits of using an nginx reverse proxy with a web server is increased security. By acting as a single point of contact for external clients, the reverse proxy can manage incoming traffic and requests. By filtering incoming traffic, a reverse proxy can also help prevent DDoS attacks, which can be used to overwhelm web servers with large amounts of traffic. They can also help with load balancing and provide scalability.

Using Docker to package your web server, you can simplify deployment and management of your application. Docker containers make it easy to deploy and scale your application, as well as make updates and changes to your configuration. This can save time and reduce the risk of errors when deploying changes to your website.

Getting Started

First, I need a Virtual Machine (e.g., AWS or Digital Ocean), a registered domain, and an Ubuntu 22.10 x64 image to replicate the steps in this blog. I'll walk you through the installation of Docker and the setup of the necessary containers.

On Digital Ocean, I created a droplet with the below specifications. I also configured the VM to use an SSH key pair.

I registered two subdomains for use in this blog.

  • redraven.cyberhacktics.com
    - This site will be a WordPress site
  • test.cyberhacktics.com
    - This will be a generic Apache site

Server Setup

I typically follow these steps from Digital Ocean when setting up a new server: Initial Server Setup. Allow SSH and HTTP/S through the firewall:

sudo ufw allow 80/tcp && \
sudo ufw allow 443/tcp && \
sudo ufw allow OpenSSH

Finally, I always perform an update and upgrade to finish up my initial server setup.

sudo apt update -y && sudo apt upgrade -y

Install Docker

I followed these steps to install Docker CE: Install Docker Engine on Ubuntu.

Run Websites in Docker

Pull WordPress Image

I pulled the official WordPress image from Docker Hub.

sudo docker pull wordpress

Run WordPress Container

Next, I used the following command to run the WordPress container:

sudo docker run -d --name wp-redraven wordpress

You'll notice I'm not exposing the port with the -p option. That's because the NGINX reverse proxy will direct traffic to the container.

Pull Apache httpd Image

After WordPress was stood up, I pulled the Apache httpd image from Docker Hub.

sudo docker pull httpd

Then I ran the Apache container.

sudo docker run -d --name test-site httpd

Once the WordPress and Apache containers were created, I checked their internal IP addresses. These are needed for the configuration files that I will discuss later.

sudo docker network inspect bridge

Run Nginx in Docker

Next, I pulled the NGINX image from Docker Hub:

sudo docker pull nginx

It's important to note that the NGINX Docker image does not come preconfigured as a reverse proxy. Therefore, in my home directory, I created an nginx folder and added four files: nginx.conf, proxy_params, test.cyberhacktics.com, and redraven.cyberhacktics.com. Here's a little about each file:

  • nginx.conf: the main configuration file for the NGINX web server. It contains a set of directives that control various aspects of the web server's operation, including server blocks, location blocks, upstream servers, SSL/TLS settings, and more.
  • proxy_params: a configuration file that contains a set of default parameters that can be used when configuring an NGINX reverse proxy server.
  • redraven.cyberhacktics.com and test.cyberhacktics.com: the configuration files that tell NGINX where to route traffic to and what SSL configurations are included.

With all this in mind, I created the nginx folder.

mdkir ~/nginx

Next, I created the four files mentioned above. Below is the content for each file.

nginx.conf

user  nginx;
worker_processes  auto;
include /etc/nginx/modules-enabled/*.conf;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    error_log /var/log/nginx/error.log;

    sendfile        on;
    tcp_nopush     on;
    types_hash_max_size 2048;

    keepalive_timeout  65;

    gzip  on;


    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

proxy_params

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

redraven.cyberhacktics.com

server {
    listen 80;
    listen [::]:80;

    server_name redraven.cyberhacktics.com;

    location / {
        proxy_pass http://172.17.0.2;
        include proxy_params;
    }
}

test.cyberhacktics.com

server {
    listen 80;
    listen [::]:80;

    server_name test.cyberhacktics.com;

    location / {
        proxy_pass http://172.17.0.3;
        include proxy_params;
    }
}

You'll notice I used the internal IPs for the proxy_pass value for both WordPress and the Apache site. This tells NGINX where to route requests bound for redraven.cyberhacktics.com and test.cyberhacktics.com.

Run the NGINX Container

Now that I created my configuration files, I ran the NGINX container with the following command:

sudo docker run -d --name rev-proxy -p80:80 -p443:443 nginx

As I mentioned before, the NGINX image needs some modifications to make it a reverse proxy. I used the following command to create the sites-available and sites-enabled folders in the container:

sudo docker exec -it rev-proxy /bin/bash -c "mkdir /etc/nginx/sites-available /etc/nginx/sites-enabled"

Copy Over the Config Files

Next, I moved the configuration files from my host to the NGINX container.

sudo docker cp ~/nginx/proxy_params rev-proxy:/etc/nginx/proxy_params

sudo docker cp ~/nginx/nginx.conf rev-proxy:/etc/nginx/nginx.conf

sudo docker cp ~/nginx/redraven.cyberhacktics.com rev-proxy:/etc/nginx/sites-available/redraven.cyberhacktics.com

sudo docker cp ~/nginx/test.cyberhacktics.com rev-proxy:/etc/nginx/sites-available/test.cyberhacktics.com

I then created a symbolic link for both redraven.cyberhacktics.com and test.cyberhacktics.com:

sudo docker exec rev-proxy ln -s /etc/nginx/sites-available/redraven.cyberhacktics.com /etc/nginx/sites-enabled

sudo docker exec rev-proxy ln -s /etc/nginx/sites-available/test.cyberhacktics.com /etc/nginx/sites-enabled

Finally, I restarted the rev-proxy container.

sudo docker restart rev-proxy

Check Websites to Confirm Success

The last thing to do now was to check that the proxy was working properly. I navigated to each site and saw that both pages were being routed properly.

Configure SSL

You'll notice in my screenshots that neither site uses HTTPS. SSL needs to be configured on the NGINX reverse proxy for both sites. Before doing this, I updated and installed certbot on the NGINX container.

sudo docker exec rev-proxy /bin/bash -c "apt update -y && apt upgrade -y"
sudo docker exec rev-proxy /bin/bash -c "apt install -y python3-certbot-nginx"

Next, I ran certbot for both sites.

sudo docker exec rev-proxy certbot --nginx -d redraven.cyberhacktics.com -n --agree-tos --email info@cyberhacktics.com

sudo docker exec rev-proxy certbot --nginx -d test.cyberhacktics.com -n --agree-tos --email info@cyberhacktics.com

Conclusion

Overall, setting up a web server in Docker and using an Nginx proxy to route traffic to a web application can be a complex process, but by following the steps outlined in this blog it can be done quickly and easily. Not only will you be able to get your web server up and running quickly, but you'll also be able to secure the communication with an SSL certificate and make sure it's optimized for performance.