Skip to main content

How to Build a Custom NGINX Docker Image

Learn how to set up, configure, and optimize NGINX in Docker. This guide covers running containers, serving custom content, building images, setting up a reverse proxy, and using Docker Compose for efficient deployment.
Mar 26, 2025  · 10 min read

NGINX inside a Docker container can provide a dependable web service in many environments. You might be working on a personal website, a lightweight web service, or a home lab project. Docker can help you keep it consistent across different machines and setups. This approach offers a way to run NGINX with fewer overhead concerns when moving between development and production. It is common to rely on Docker for simple packaging of the web server, which makes it easier to share your projects without messy manual steps.

This article explains how to run a ready-made NGINX image, how to serve content from your local machine, and how to build your image with custom features. It also covers setup steps for a reverse proxy, plus the usage of Docker Compose for multi-service deployment. In the end, you will see how to manage containers with a few simple commands and have a detailed view of how to integrate Docker and NGINX for practical tasks.

If you’re new to Docker, be sure to check out our Docker for Beginners guide to learn all the essentials. 

What is Docker NGINX?

Docker NGINX refers to running the NGINX web server inside a lightweight container. Docker containers are similar to small packages that hold an application and all the dependencies it needs. 

NGINX is a popular web server known for strong performance, easy configuration, and flexible handling of HTTP traffic. Pairing NGINX with Docker is appealing because it keeps the environment consistent. If you deploy on macOS, Windows, or Linux, the container’s content remains the same. That means it behaves identically, regardless of the host operating system.

A typical Docker image for NGINX includes the server software, a default configuration, and a directory structure for hosted files. You can run that container right away or adjust it by mapping specific folders, adjusting configuration files, or building an image based on your own Dockerfile. By doing that, you might add custom modules or scripts to customize NGINX for your application.

Components of Docker NGINX

To learn more about how to containerize applications, refer to our How to Containerize an Application Using Docker tutorial. It goes into more detail about how to containerize machine learning applications with Docker in a beginner-friendly way. 

Prerequisites

Before you start running NGINX containers, confirm that Docker is installed. Docker has official installers for macOS, Windows, and various Linux distributions. The Docker documentation provides a step-by-step process for each operating system. 

Check out our Introduction to Docker course for a comprehensive guide on how to get started with Docker. 

Test your installation with a quick command such as:

docker version
derrickmwiti@Derricks-MacBook-Pro% docker version
Client:
 Version:           28.0.1
 API version:       1.48
 Go version:        go1.23.6
 Git commit:        068a01e
 Built:             Wed Feb 26 10:38:16 2025
 OS/Arch:           darwin/arm64
 Context:           desktop-linux
Server: Docker Desktop 4.39.0 (184744)
 Engine:
  Version:          28.0.1
  API version:      1.48 (minimum version 1.24)
  Go version:       go1.23.6
  Git commit:       bbd0a17
  Built:            Wed Feb 26 10:40:57 2025
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.7.25
  GitCommit:        bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
 runc:
  Version:          1.2.4
  GitCommit:        v1.2.4-0-g6c52b3f
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

If you see version details, that means Docker is ready. Beyond Docker itself, you need internet access to pull the NGINX image from Docker Hub. If you plan to create your image, you will need a text editor to write a Dockerfile. Any text editor works fine, but pick one that is comfortable for you.

Setting up NGINX in a Docker Container

Running NGINX from a ready-made image is usually the quickest method. You can pull it from Docker Hub in a matter of seconds and then launch a container. Thereafter, you will see the default page in your browser. This short process is often the first step in many Docker-based server setups.

Pulling the NGINX Docker image

The official NGINX image is publicly available on Docker Hub under the name nginx

Pull it like this:

docker pull nginx
derrickmwiti@Derricks-MacBook-Pro % docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
4c7c1a5bd3af: Download complete 
bef2ee7fab45: Download complete 
d9b636547744: Downloading [======================>                            ]  12.58MB/28.04MB
0994e771ba34: Downloading [===>                                               ]  3.146MB/40.56MB
589701e352f8: Download complete 
13f89c653285: Download complete 
8e77214beb25: Download complete 

This goes to the registry and gets the latest version. When everything is complete, you can check that it exists on your machine:

docker images
derrickmwiti@Derricks-MacBook-Pro% docker images
REPOSITORY                                                                     TAG                                                                           IMAGE ID       CREATED         SIZE

nginx                                                                          latest                                                                        124b44bfc9cc   6 weeks ago     280MB
keras-custom-training-container                                                latest                                                                                                                                     <none>                                                                        7de0f04013e5   7                                                  

That command shows a list of images in your local library. You should see the nginx image in the table. That means you are ready to create a container from it. Discover a list of images you would need for your data science projects from our Top 12 Docker Container Images for Machine Learning and AI tutorial. 

Running the NGINX container

Once the NGINX image is on your system, you can start the container with a single command:

docker run --name mynginx -p 8080:80 -d nginx
derrickmwiti@Derricks-MacBook-Pro % docker run --name mynginx -p 8080:80 -d nginx
ec44d1a43016e9684b602437b967c0ed8e11c933a8a76b341b68c25168d2af70

This command does several things. It runs a container named mynginx in the background (because of the -d option). It maps port 8080 on your host machine to port 80 inside the container. That port mapping means you can visit http://localhost:8080 in your browser and see the default NGINX page. The container uses the nginx image pulled from Docker Hub.

The new container should keep running because NGINX is active. If you check logs with:

docker logs mynginx
derrickmwiti@Derricks-MacBook-Pro % docker logs mynginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2025/03/20 04:33:10 [notice] 1#1: using the "epoll" event method
2025/03/20 04:33:10 [notice] 1#1: nginx/1.27.4
2025/03/20 04:33:10 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14) 
2025/03/20 04:33:10 [notice] 1#1: OS: Linux 6.10.14-linuxkit
2025/03/20 04:33:10 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2025/03/20 04:33:10 [notice] 1#1: start worker processes
2025/03/20 04:3

You can see any messages that NGINX wrote. 

Serving Custom Content With NGINX

You might want to serve an HTML page, rather than the default page included in the container image. You can create a simple file on your local machine and then mount it into the container. That keeps your content separate from the container. Whenever you tweak your page, you can refresh the browser to see the change.

Creating a custom HTML page

Pick a convenient location on your local machine. Possibly a folder called html_content in your home directory. Create an index.html file there. It might look like this:

<html>
<head>
    <title>My NGINX Page</title>
</head>
<body>
    <h1>Hello from my Docker NGINX setup!</h1>
    <p>This is a custom page served from a local folder.</p>
</body>
</html>

That is a basic HTML page. You can put any text or styling you want. Add images or JavaScript, or anything else that suits your needs.

Mounting the HTML page to the NGINX container

The official NGINX image serves content from /usr/share/nginx/html by default. You can map your local folder to that location inside the container. That is done with the -v (volume) flag in Docker. Stop any existing container named mynginx to avoid a port conflict:

docker stop mynginx
docker rm mynginx

Then start a new container:

docker run --name mynginx -p 8080:80 -d \
    -v /Users/derrickmwiti/Documents/html_content:/usr/share/nginx/html \
    nginx
6f68085733eeac418c3302e5c580a4f7c5c0c36d3591d8e2a7f35f69f6308d18

Replace /path/to/html_content  with the actual path on your computer. This way, whenever you browse to http://localhost:8080, the container will serve the HTML file you placed in html_content. By default, if you call it  index.html, NGINX will load that file automatically.

Mounting the HTML page to the NGINX container

That approach is beneficial when you want to adjust the content regularly. You do not need to rebuild the container each time. The server pulls content from your local folder on the fly. 

If you see a permission-related warning, you might need to ensure that your local user and Docker can read that folder. 

On Linux, that might mean adjusting ownership or file permissions. On Windows or macOS, it might mean granting Docker permission to access your drive.

Building a Custom NGINX Docker Image

Mounting a local folder is a quick fix for simple testing. You might want extra modules, special configuration, or some other tweak. That is where building an image can be helpful. 

Docker makes it easy to create a small recipe file, known as a Dockerfile, which sets up a container exactly how you want it. Then you will have an image that you can share with others or push to a registry. That can be more convenient than telling people to mount a bunch of local files.

Creating a Dockerfile

A Dockerfile is just a plain text file. You can name it Dockerfile or pick another name if you prefer. Place it in a new directory. Here is a simple example:

FROM nginx:latest

COPY index.html /usr/share/nginx/html/index.html

This uses the official nginx:latest image as the base. Then, it copies a local file named  index.html into the default web content folder in the container. You could include more lines if you need them. For instance, you might copy a custom nginx.conf or add modules you want to install. That depends on your needs. If you just want to place a static page, the snippet above is enough.

When you have that Dockerfile in place, add the custom index.html in the same folder. Then you can build. The typical command is:

docker build -t mynginximage .
[+] Building 2.6s (7/7) FINISHED                                                                                                                                                       docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                                                                   0.0s
 => => transferring dockerfile: 245B                                                                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/nginx:latest                                                                                                                                        0.0s
 => [internal] load .dockerignore                                                                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                                                                        0.0s
 => [internal] load build context                                                                                                                                                                      0.0s
 => => transferring context: 365B                                                                                                                                                                      0.0s
 => [1/2] FROM docker.io/library/nginx:latest@sha256:124b44bfc9ccd1f3cedf4b592d4d1e8bddb78b51ec2ed5056c52d3692baebc19                                                                                  2.3s
 => => resolve docker.io/library/nginx:latest@sha256:124b44bfc9ccd1f3cedf4b592d4d1e8bddb78b51ec2ed5056c52d3692baebc19                                                                                  2.3s
 => [2/2] COPY index.html /usr/share/nginx/html/index.html                                                                                                                                             0.0s
 => exporting to image                                                                                                                                                                                 0.1s
 => => exporting layers                                                                                                                                                                                0.0s
 => => exporting manifest sha256:19dd719772fd7ac839fadb7d49862f84422f4571a442994ffaca152e9674553a                                                                                                      0.0s
 => => exporting config sha256:e974f3ab92a0278e419f0fe4c5a6fc43fafb4a75da27895d90c4f2d5d385daa1                                                                                                        0.0s
 => => exporting attestation manifest sha256:e391b0212e5a100bd9132fbbd6cb4052e288dbbcba093839b55f8b197131372b                                                                                          0.0s
 => => exporting manifest list sha256:d35c2bbe10ab0b7e46d1ef8fba6c6cb28628eb9c283db02607e32e95b62e14d5                                                                                                 0.0s
 => => naming to docker.io/library/mynginximage:latest                                                                                                                                                 0.0s
 => => unpacking to docker.io/library/mynginximage:latest                                                                                                                                              0.0s

The dot at the end indicates the current directory as the build context. Docker reads the Dockerfile in that directory. You end up with an image called mynginximage on your local system. Check it with:

docker images
mynginximage                                                                   latest                                                                        d35c2bbe10ab   36 seconds ago   281MB

You should see the new name in the list. This approach means your Dockerfile and HTML are combined into a single image. You do not have to mount anything at runtime.

Building and running the custom image

Now that you have mynginximage built, you can run it:

docker run --name mycustomnginx -p 8080:80 -d mynginximage

Be sure to stop the previous container to avoid port conflicts. 

This container automatically serves the page you placed in the Dockerfile. If you open a browser at http://localhost:8080, you should see that HTML text. 

Building and running the custom image

You can share your Dockerfile with others or push the built image to a registry. Then, your teammates or colleagues can spin it up without copying local folders.

That completes the process of building a minimal custom NGINX image. You started from the official base, copied a file or two, and produced a new container that includes your custom site. You can extend the Dockerfile further. For example, you might want to switch the configuration to handle PHP with php-fpm in a multi-stage build or add custom modules that NGINX does not ship by default.

This approach highlights the flexibility of Docker: each layer in the Dockerfile can handle a certain task, and the final image can be distributed easily. You have control over the web content, plus the option to tweak settings so that your container suits your project. 

Explore these 10 Docker Project Ideas to learn Docker with hands-on projects focused on building and optimizing data science applications.

Setting up NGINX as a Reverse Proxy

NGINX can direct incoming requests to other services on the same system or even to services running on separate machines. This is typically called a reverse proxy setup. You might place NGINX in front of a group of applications so that requests can pass through a single entry point. 

This is common when you have multiple Docker containers listening on different ports, and you want a single domain name for everything. NGINX listens on a standard port, such as 80 or 443, then routes the traffic to the correct service based on the path, hostname, or other factors.

Understanding reverse proxy

When NGINX is configured this way, the client is unaware of background communication. NGINX acts as the public face, receiving and forwarding requests and then sending responses back to the client. 

This design simplifies your network structure. It also provides an opportunity to adjust caching, add TLS certificates, handle load balancing, or do other advanced tasks. It is often easier to manage these tasks in a single place, rather than scattering them among multiple services.

A typical scenario might involve a container running NGINX on port 80, which sends requests to an application container on port 3000. While the client visits http://yourdomain.com, the traffic is directed to a separate container behind the scenes. 

That might allow your application container to keep its default port, with minimal overhead for the user. Another reason to do this is that you might wish to hide the internal details of your services or present a uniform URL structure.

Configuring NGINX for reverse proxy

To achieve this, you need a configuration file for custom NGINX that uses proxy directives. You can do it by mounting a new configuration file when you run the container, or by building a custom image that includes it. Here is a simple example to illustrate the general idea:

server {
    listen 80;

    location / {
        proxy_pass http://myapp:3000/;
    }
}

In the snippet above, any request that arrives at your NGINX container on port 80 gets sent to myapp:3000. The myapp name suggests there is another container reachable by that name, which is common when you use Docker networking. 

If you are using standalone containers, you might map the container’s IP address or use the host’s IP. You can also add more advanced settings, such as proxy_set_header Host $host, or adjust timeout values to suit your application.

If you opt to mount this file at runtime, you can do something like this:

docker run --name nginxproxy -p 8080:80 -d \
    -v /path/to/nginx.conf:/etc/nginx/conf.d/default.conf \
    --network mynetwork \
    nginx

This command runs the NGINX container, mounting your local nginx.conf as the default configuration. It also connects the container to a Docker network called mynetwork, where your other container (myapp) resides. That is often the simplest approach if you want to keep your configuration outside the container. 

It is also possible to put that file into a Dockerfile if you want a static arrangement. Either approach can work, as long as the references match how you named your containers and networks.

This design gives you the ability to have a single domain or IP for all your web services, along with a common port. If you are dealing with multiple back-end containers, you could define multiple locations in your configuration, each pointing to a different container port. NGINX can then route to each service based on the URL path. That is a popular approach for microservices because it consolidates the entry points in a tidy manner.

Using Docker Compose for NGINX Deployment

A reverse proxy often becomes more useful when you have several containers that work together. Docker Compose is a tool that lets you define many services in a single file. Then, you can bring them up or down with a single command. It reduces the chance of mistakes in container creation or port mapping because all the details reside in a standard file.

Introduction to Docker Compose

Docker Compose allows you to manage sets of containers. You describe each service, specify the image to use, ports to publish, volumes to mount, and networks to join. Then a simple command (docker compose up -d) will start everything at once. 

This approach is handy for local development or for staging environments where you need to coordinate multiple Docker containers. The usual file format is YAML.

Although Docker Compose can work on various operating systems, you need to ensure that it is installed and recognized. Modern Docker Desktop installations often include it by default. Check your version with:

docker compose version
Docker Compose version v2.33.1-desktop.1

If you see the version details, you are ready to proceed. If you do not, consult the Docker documentation on how to enable or install Docker Compose.

Creating a docker-compose.yml file

The file typically includes a services section. Below is a simple example of a docker-compose.yml file that sets up a single NGINX container to serve static content: 

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html

Here is how to interpret the file:

  • services: Defines the services (containers) that make up your application.
  • web: This service uses the latest NGINX image.
    • ports: Maps port 80 inside the container to port 8080 on your host machine. This allows you to access the web server via http://localhost:8080.
    • volumes: Mounts the local html directory to NGINX's default content directory inside the container (/usr/share/nginx/html). This setup enables NGINX to serve your custom static content.

Deploying services with Docker Compose

In the same directory as your docker-compose.yml file, create a subdirectory named html.

Place your static HTML files (e.g., index.html) inside this html directory. In this case, I have created a simple Bootstrap-styled page for demonstration. 

Open a terminal and navigate to your project directory containing the docker-compose.yml file.

Run the following command to start the service:

docker compose up -d
[+] Running 1/1
 ✔ Container html_content-web-1  Started 

The -d flag runs the container in detached mode, allowing it to run in the background.

Docker Compose looks at the docker-compose.yml file in the current directory. It pulls any images that are not already present and then launches each container. 

Open your web browser and navigate to http://localhost:8080.

You should see your static content being served by NGINX.

Deploying services with Docker Compose

You can inspect the status of your running services by checking:

docker compose ps

If you need to tear everything down, a single command can remove the containers:

docker compose down

That stops and removes them all at once. If you want to replicate the entire environment, you simply copy the Docker Compose file and the relevant code to a new machine, and then run the same commands. This ensures that you get consistent results with minimal manual steps.

Managing NGINX Containers

While the Docker Compose approach is popular for multi-service workflows, sometimes you just need to manage a single container or a small cluster. Docker provides a few basic commands that help you see what is running, pause or stop containers, and remove them if needed. 

These are essential for housekeeping, especially if you are experimenting with multiple NGINX containers or repeated builds.

Viewing running containers

Whenever you want to see which containers are running, you can use:

docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                  NAMES
2d386c085129   mynginximage   "/docker-entrypoint.…"   4 minutes ago   Up 4 minutes   0.0.0.0:8080->80/tcp   mycustomnginx

That command prints a table of active containers, each with an ID, image, name, and more. If you need to see all containers, including ones that have stopped, use:

docker ps -a

This shows every container that exists on your system, even those that exited. It is often helpful to see which containers are still there from older runs. You might have containers named mynginx, mycustomnginx, or something else if you have been experimenting with multiple images.

Stopping and removing containers

When you are done with a container, you can stop it by name:

docker stop mynginx
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                  NAMES
2d386c085129   mynginximage   "/docker-entrypoint.…"   4 minutes ago   Up 4 minutes   0.0.0.0:8080->80/tcp   mycustomnginx

That tells Docker to send a stop signal to the container, which should cause NGINX to shut down cleanly. If you want to remove the container right afterward, you can do:

docker rm mynginx

If you prefer a single command that stops and removes in one go, try:

docker rm -f mynginx

That forces removal, stopping the container if it is still running. Be cautious when using that approach in a production environment, since it terminates the container without giving it a graceful shutdown. Another housekeeping command is docker rmi, which removes images from your system. 

That is helpful if you no longer need certain images and want to free up space. However, Docker only deletes images that are not being used by a running container.

Conclusion

NGINX in Docker is a handy way to serve web pages or route traffic to other services. You can run the official image or build your own with a simple Dockerfile. Docker Compose can coordinate multiple containers, making complex setups easier to handle. 

With these methods, you can deliver content, handle forwarding, and maintain a reliable environment for web projects. 

If you need a more hands-on approach to learning Docker, check out our Introduction to Docker Course to discover its importance in the data professional’s toolkit.

NGINX Docker FAQs

How do I run an NGINX container using Docker?

You can start an NGINX container with the following command:

docker run --name mynginx -p 8080:80 -d nginx

This runs an NGINX container in the background and maps port 8080 on your machine to port 80 inside the container. You can then access it by visiting http://localhost:8080 in your browser.

How can I serve custom content with NGINX in Docker?

To serve your HTML files, mount a local directory to the NGINX container like this:

docker run --name mynginx -p 8080:80 -d -v /path/to/html:/usr/share/nginx/html nginx

Replace /path/to/html with the actual path to your HTML files. The container will serve the content from that directory instead of the default page.

How do I configure NGINX as a reverse proxy in Docker?

Create a custom nginx.conf file with the following content:

server {
    listen 80;
    location / {
        proxy_pass http://myapp:3000/;
    }
}

Then, run the NGINX container with the mounted configuration file:

docker run --name nginxproxy -p 8080:80 -d -v /path/to/nginx.conf:/etc/nginx/conf.d/default.conf nginx

This will forward requests from http://localhost:8080 to your application running on port 3000.

How can I use Docker Compose to deploy NGINX with other services?

Create a docker-compose.yml file with the following content:

services:
  myapp:
    image: myappimage
    expose:
      - "3000"

  nginxproxy:
    image: nginx
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - myapp

Run the services with:

docker compose up -d

This starts both your application and NGINX proxy in a single command.

How do I stop and remove an NGINX container in Docker?

To stop a running NGINX container, use:

docker stop mynginx

To remove the container completely, run:

docker rm mynginx

If you want to remove the container and its image, you can also delete the image with:

docker rmi nginx

This cleans up the container and its stored image from your system.

Topics

Top DataCamp Courses

Track

Containerization and Virtualization with Docker and Kubernetes

0 min
Learn the power of Docker and Kubernetes, this interactive track will allow you to build and deploy applications in modern environments.
See DetailsRight Arrow
Start Course
See MoreRight Arrow
Related

blog

How to Learn Docker from Scratch: A Guide for Data Professionals

This guide teaches you how to learn Docker from scratch. Discover practical tips, resources, and a step-by-step plan to accelerate your learning!
Joel Wembo's photo

Joel Wembo

14 min

Tutorial

How to Set Up and Configure MySQL in Docker

Learn how to set up and configure MySQL database inside Docker containers. The tutorial includes concepts such as connecting to MySQL servers, running MySQL clients to connect to containers, and so on.
Bex Tuychiev's photo

Bex Tuychiev

12 min

Tutorial

Docker for Beginners: A Practical Guide to Containers

This beginner-friendly tutorial covers the essentials of containerization, helping you build, run, and manage containers with hands-on examples.
Moez Ali's photo

Moez Ali

14 min

Tutorial

Install Docker on Ubuntu: From Setup to First Container

It only takes a few commands from a fresh OS installation to running your first containerized application. Learn how to do this with this ultimate Docker Ubuntu setup guide.
Dario Radečić's photo

Dario Radečić

11 min

Tutorial

How to Containerize an Application Using Docker

Learn how to containerize machine learning applications with Docker and Kubernetes. A beginner-friendly guide to building, deploying, and scaling containerized ML models in production.

Rajesh Kumar

15 min

Docker bot

Tutorial

Docker for Data Science: An Introduction

In this Docker tutorial, discover the setup, common Docker commands, dockerizing machine learning applications, and industry-wide best practices.
Arunn Thevapalan's photo

Arunn Thevapalan

15 min

See MoreSee More