Create an NGINX Reverse Proxy in Docker and Learn 1 New Skill!

Do you need powerful web servers to handle requests to your Docker application? Using an NGINX reverse proxy in Docker gives you the ability to handle and manage web application requests to and from a containerized application in various ways.

In this article, you will learn how to create an NGINX Reverse Proxy in Docker and configure the container to reverse proxy requests to and from another container. Read on to learn more!

Prerequisites

To follow along with this tutorial, be sure you have the following:

  • Docker Desktop – This tutorial uses version 3.5.2.
  • Windows 10 – The tutorial uses Windows to run Docker on, but the same general steps can also be applied to Linux or macOS.
  • The Docker engine if you’re on Linux.

All examples in this tutorial will use a minimal Linux Distribution called Alpine Linux, running the latest NGINX version available. As of this writing, v1.21.1 is the latest version.

Creating an PHP-FPM NGINX Reverse Proxy in Docker

One of the most popular programming languages, PHP is often a use case for a reverse proxy. PHP-FPM or Fast CGI Process Manager is a great product to use for proxying traffic in the PHP world. Using PHP-FPM in a Docker container and NGINX in another, you can set up PHP-FPM to listen and respond to requests on a network port, assisting in networking two containers together.

Bonus! This tutorial will also provide examples of using NodeJS as a reverse proxy throughout as well. Be on the lookout for the NodeJS callouts.

Let’s now set up both an NGINX container and a PHP-FPM Docker container to see how they work together proxying requests from the browser through NGINX to PHP-FPM backend and back.

1. First, create a directory to contain your configuration files. This directory will contain all of the configuration files needed to provision both containers. In this example, the directory C:\Articles\NGINX-PHP is used.

2. Next, create the Dockerfile with the following contents. The below Dockerfile will tell the Docker engine to download the nginx:mainline-alpine image from the Docker Hub repository and will copy the NGINX configuration file you’ll be creating into the image to provide a custom NGINX configuration.

# The image to pull the base configuration from
FROM nginx:mainline-alpine
# The directory where additional files will be referenced
WORKDIR C:\Articles\NGINX-PHP
# Copy the custom default.conf from the WORKDIR (.) and overwrite the existing internal configuration in the NGINX container
COPY ./default.conf /etc/nginx/conf.d/default.conf

Grab the NodeJS Dockerfile to set up the NodeJS Docker image in the ATA Scripts Github repository!

3. Next, create the file docker-compose.yml file, containing the following. This Docker Compose file will tell Docker to create two containers, web and php running NGINX and PHP-FM, respectively.

# The specification version of docker-compose
version: "3.9"
# The collection of applications composing this service
services:
  # The NGINX custom container, and the name, web, will function as the host name of the container
  web:
    # Instead of referencing image: nginx:mainline-alpine here, use build to
    # reference the current directory (.), which will look for a dockerfile
    # by default. In this tutorial, this is C:\Articles\NGINX-PHP
    build: .
    # The external directory location to map to an internal location
    volumes:
      - C:\Articles\NGINX-Content:/usr/share/nginx/html
    # The external port mapping to internal port mapping
    ports:
      - "80:80"
  # The name, php, will also function as the host name of the container
  php:
    image: php:fpm-alpine
    ports:
      - "9000:9000"
    # It is important that both containers can reference the same files
    volumes:
      - C:\Articles\NGINX-Content:/usr/share/nginx/html

Grab the NodeJS Docker Compose file to set up the NodeJS Docker image in the ATA Scripts Github repository!

4. Create the NGINX configuration file, default.conf, with the following. The file below defines a short NGINX configuration that creates a site listener and sends content requests to the PHP container.

server {
    # The port to listen on
    listen 80;
    # The root directory, which must exactly match the internal volume share
    root /usr/share/nginx/html;

    # For all files with the PHP extension run the following
    location ~ ^/.+\.php(/|$) {
        # Pass the request to the host "php" and port 9000 (default PHP-FPM port).
        # The "php" host name is generated from the application name in the
        # Docker Compose file that was previously defined.
        fastcgi_pass  php:9000;
				# Include the default NGINX FastCGI Parameters
        include       fastcgi_params;
				# Define one additional parameter telling PHP-FPM where to find the file
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Grab the NodeJS NGINX configuration file to set up the NodeJS in NGINX in the ATA Scripts Github repository!

5. Open a terminal session and navigate to the C:\Articles\NGINX-PHP directory.

6. Run the command, docker-compose up to generate and start your custom service. This action will bring up both containers.

Starting the NGINX and PHP-FPM containers via Docker Compose.Starting the NGINX and PHP-FPM containers via Docker Compose.

7. Next, create a PHP file called index.php in C:\Articles\NGINX-Content and paste the following contents. This file will be used to test that NGINX can serve and process files via PHP. The phpinfo() command will output the PHP informational page to verify that the container is working.

<?php phpinfo(); ?>

Instead of a PHP file, download the index.js file in the ATA Scripts Github repository!

8. Finally, open a web browser and navigate to http://localhost/index.php to verify that NGINX serves up the PHP file as expected. In the below example, PHP is returning version 8.0.8, but your version may vary.

Demonstrating that the PHP file properly works.Demonstrating that the PHP file properly works.

Proxying Both NodeJS and PHP-FPM from NGINX

Now that you have proxied both PHP and NodeJS individually, how would you go about proxying both backends simultaneously? Perhaps, you have a complex application that consists of both NodeJS and PHP components. For this to work, you will need to extend your Docker Compose file to include both backends.

1. First, create a directory to contain your configuration files. In this example, the directory C:\Articles\NGINX-Both is used.

2. Create the Dockerfile and paste using the following contents. So far, not much difference at all from building PHP-FPM or a NodeJS container individually.

# The image to pull the base configuration from
FROM nginx:mainline-alpine
# The directory where any additional files will be referenced
WORKDIR C:\Articles\NGINX-Both
# Copy the custom default.conf and overwrite the existing internal configuration
COPY ./default.conf /etc/nginx/conf.d/default.conf

3. Next, create the file docker-compose.yml file, containing the following. You’ll now see that another container has been introduced called node.

# The specification version of docker-compose
version: "3.9"
# The collection of containers making up your application
services:
  # The NGINX custom container
  web:
    # Instead of referencing image: nginx:mainline-alpine here, use build to
    # reference the current directory (.) and will look for a dockerfile by default
    build: .
    # The external directory location to map to an internal location
    volumes:
      - C:\Articles\NGINX-Content:/usr/share/nginx/html
    # The external port mapping to internal port mapping
    ports:
      - "80:80"
  node:
    image: node:current-alpine
    # Override the existing entrypoint to tell Node to execute the index.js file
    entrypoint: ['node','/usr/share/nginx/html/index.js']
    ports:
      - "3000:3000"
    # It is important that all containers can reference the same files
    volumes:
      - C:\Articles\NGINX-Content:/usr/share/nginx/html
  php:
    image: php:fpm-alpine
    ports:
      - "9000:9000"
    volumes:
      - C:\Articles\NGINX-Content:/usr/share/nginx/html

4. Next, create the NGINX configuration file, default.conf, with the following. Notice the proxy_pass line. This line will pass the request to the node host.

server {
    # The port to listen on
    listen 80;
    # The root directory should exactly match the internal volume share
    root /usr/share/nginx/html;

    # For all files with the PHP extension run the following to route to PHP-FPM
    location ~ ^/.+\.php(/|$) {
        # Pass the request to the host "php" and port 9000 (default PHP-FPM port)
        fastcgi_pass  php:9000;
		# Include the default NGINX FastCGI Parameters
        include       fastcgi_params;
		# Define one additional parameter telling PHP-FPM where to find the file
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    # For all files with the JS extension run the following to route to NodeJS
    location ~ ^/.+\.js(/|$) {
        # Pass the request to the host "node" and port 3000 (default NodeJS port)
        proxy_pass  http://node:3000;
    }
}

5. Open a terminal and navigate to the C:\Articles\NGINX-Both directory.

6. Run the command, docker-compose up to generate and start your custom service as before.

Starting the NGINX, PHP, and NodeJS containers via Docker Compose.Starting the NGINX, PHP, and NodeJS containers via Docker Compose.

Next, create the file index.js in the C:\Articles\NGINX-Content directory and the file index.php in that same directory, if it does not already exist, to test NGINX proxying both backends.

index.js

// Assign the HTTP server object, built-in to NodeJS
var http = require('http');

// Create the server, on port 3000, and output the text content "Hello World!"
http.createServer(function (req, res) {
    res.write('Hello World!');
    res.end();
}).listen(3000, function(){
    console.log("server start at port 3000");
});

index.php

<?php phpinfo(); ?>

8. Finally, open a web browser and navigate to http://localhost/index.js and http://localhost/index.php. You should now see that both are accessible, as shown below.

Demonstrating that both the NodeJS and NGINX containers properly work.Demonstrating that both the NodeJS and NGINX containers properly work.

Conclusion

Now that you have successfully proxied both a PHP-FPM container and a NodeJS container, even at the same time, why not try load-balancing multiple NGINX containers?

Using an NGINX reverse proxy in Docker opens up a world of possibilities for properly segmenting applications and traffic among containers!