DEV Community

Cover image for How to deploy an app using Docker and Nginx in 2025!
Eugene YuIT
Eugene YuIT

Posted on • Edited on

How to deploy an app using Docker and Nginx in 2025!

📃In this article we will figure it out, how to deploy your own app using docker and nginx, for our app we will use the following technologies:

  • 🌐 For frontend (my repository): Next.js is a popular open-source React framework that enables developers to build server-rendered and static web applications with ease. It offers features like automatic code splitting, server-side rendering (SSR), static site generation (SSG), and API routes, which enhance performance and SEO. Next.js simplifies the development process by providing a file-based routing system and built-in support for CSS and TypeScript, making it a powerful choice for creating modern web applications.

  • ⚙️ For backend (my repository): Nest.js is a progressive Node.js framework for building efficient, scalable server-side applications. It leverages TypeScript and incorporates modern design patterns, such as modular architecture and dependency injection, to promote maintainability and testability. With built-in support for GraphQL, WebSockets, and microservices, Nest.js is ideal for developing robust APIs and enterprise-level applications. Its extensible architecture allows integration with various libraries and tools, making it a versatile choice for backend development.

  • 🗄️ For database: PostgreSQL is a powerful, open-source relational database management system (RDBMS) known for its robustness, extensibility, and compliance with SQL standards. It supports advanced data types, complex queries, and transactions, making it suitable for a wide range of applications. With features like ACID compliance, full-text search, and support for JSON and GIS data, PostgreSQL is favored for its performance, reliability, and ability to handle large volumes of data. Its active community and rich ecosystem of extensions further enhance its capabilities, making it a popular choice for developers and organizations.

Sometimes developers confuse Nest and Next😀
Ok, let's move on and do it!💪

📑Create docker network

The fist of all to create a common network for feauture containers
First of all, it is necessary to create and check a common network for future docker containers via command:

docker network create -d bridge yuit-net, where are the following parameters:

  • yuit-net - the name of our network
  • bridge - the default network driver
  • -d or --driver - this flag allows you to specify which network driver docker should use to create the network
    • docker network ls - for check network

And in our terminal we can see all networks (including ours)

PS ...\portfolio-yues-nestjs-docker> docker network create -d bridge yuit-net
6019f719ee8bcd39cf0406a795f5a61a531756ce29c9a53974868d7797ed19e7
PS ...\portfolio-yues-nestjs-docker> docker network ls
NETWORK ID     NAME       DRIVER    SCOPE
33c0431165ec   bridge     bridge    local
295269f446db   host       host      local
801fdcaabbb4   none       null      local
6019f719ee8b  yuit-net   bridge    local
Enter fullscreen mode Exit fullscreen mode

📑Create docker container for database

Next step we need to create container for our database PostgreSQL via:

docker run --network=yuit-net --name yuit-chart-db-host --rm -d -p 5432:5432 -i -t -e POSTGRES_DB=yuit-chart-db -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=adminyuitpassword postgres, where are the following parameters:

  • --network=yuit-net - it's our docker network
  • --name yuit-chart-db-host - it's a custom name to the container for data source option in our backend project (we will consider this below) Data source option Dockerfile of backend
  • --rm - this option tells docker to automatically remove the container when it stops
  • -d - this flag runs the container in detached mode
  • -p 5432:5432 - this option maps port 5432(first) of the host machine to port 5432(second) of the container
  • -i - this flag stands for "interactive" mode. It keeps the standard input (stdin) open even if not attached, allowing you to interact with the container if needed
  • -t - this flag allocates a pseudo-TTY (terminal) for the container, which is useful for running interactive applications. It is often used in conjunction with -i.
  • -e POSTGRES_DB=yuit-chart-db - this option sets an environment variable inside the container. Here, it creates a PostgreSQL database named yuit-chart-db
  • -e POSTGRES_USER=admin - this environment variable sets the username for the PostgreSQL database
  • -e POSTGRES_PASSWORD=adminyuitpassword - this environment variable sets the password for the PostgreSQL user specified in the previous parameter
  • postgres - it's the official PostgreSQL image from Docker Hub

And via command docker ps -a to check the DB container

PS ...\portfolio-yues-nestjs-docker> docker run --network=yuit-net --name yuit-chart-db-host --rm -d -p 5432:5432 -i -t -e POSTGRES_DB=yuit-chart-db -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=adminyuitpassword postgres
PS ...\portfolio-yues-nestjs-docker>  docker ps -a
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS         PORTS                    NAMES
2ef9f2d7daa6   postgres   "docker-entrypoint.s…"   5 seconds ago   Up 5 seconds   0.0.0.0:5432->5432/tcp   yuit-chart-db-host
Enter fullscreen mode Exit fullscreen mode

❔ We may also connect to our DB, for example, via Database Tool: DBeaver
Connect to DB
And we see our DB with the name yuit-chart-db
Check DB

📑Create docker image and container for backend

After we have created our DB, we can create a docker image of backend.
For that:

1️⃣We need to create our Dockerfile, it's needed for describe our image, for example

 # build app
 FROM node:20.16-alpine AS build
 WORKDIR /app
 COPY . /app

 ENV DATABASE_HOST=yuit-chart-db-host

 RUN yarn install
 RUN echo "Build is ok! Go to next layer!"
 RUN yarn build

 EXPOSE 8080

 # start migration and api
 CMD npm run migration:prod && npm run start:prod
Enter fullscreen mode Exit fullscreen mode

2️⃣After that we can build image of backend, via command:

docker build . -t yuit-chart-backend-image -f infra/docker/Dockerfile, where are the following parameters:

  • the dot (.) - indicates that the current directory should be used as the context for the build
  • -t yuit-chart-backend-image - it's name of your image
  • -f infra/docker/Dockerfile - it's directory of our Dockerhfile

3️⃣The next step is run container:

docker run --network=yuit-net --name yuit-chart-backend -d -p 127.0.0.1:8080:8080/tcp yuit-chart-backend-image, where are the following parameters:

  • --network=yuit-net - it's our docker network
  • --name yuit-chart-backend - this flag assigns a name to the container, i.e. yuit-chart-backend
  • -d - this stands for detached mode
  • -p 127.0.0.1:8080:8080/tcp - this option is used to publish a container's port to the host, where:
    • 8080:8080 indicates that port 8080 on the host will be mapped to port 8080 on the container.
    • /tcp specifies the protocol to use (in this case, TCP). This part is optional, as TCP is the default protocol
  • yuit-chart-backend-image - it's the name of the Docker image

❔ We can check our contaner via command docker ps -a or to see containers in the docker desktop

the docker desktop

We may also select our data from the DB, because we have run migrations in the Dockerfile. To do this, just run the select command in the DBeaver:

SELECT id, data
FROM public.chart;

SELECT *
FROM public.__migrations;
Enter fullscreen mode Exit fullscreen mode

Select data

4️⃣After running the backend container, we can test the api through url: http://localhost:8080/data_chart

api

📑 Create docker image and container for frontend

Finally, we need to create an image and run a container for the frontend.

1️⃣ Let's add settings for the web server - nginx, for example:

Add these settings to the file conf.d

http {
  server {
    listen  8080;
    server_name localhost;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri /index.html;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
  }
}

events {}
Enter fullscreen mode Exit fullscreen mode

2️⃣We need to create our Dockerfile, for example:

# build app
FROM node:20.16-alpine as build
WORKDIR /app
COPY . /app
RUN yarn
RUN echo "Build is ok! Go to next layer!"
RUN yarn deploy

# build server
FROM nginx:alpine as normal
# copy the build folder from react to the root of nginx
COPY --from=build /app/build /usr/share/nginx/html
COPY infra/docker/conf.d /etc/nginx/nginx.conf
RUN echo "Nginx is ok!"
# expose port 3000 to the outer world
EXPOSE 3000
# start nginx
CMD ["nginx", "-g", "daemon off;"]
Enter fullscreen mode Exit fullscreen mode

3️⃣After that we can build image, via command:

docker build -t yuit-chart-frontend-image --target normal -f infra/docker/Dockerfile ., where are the following parameters:

  • -t yuit-chart-backend-image - it's image name
  • -f infra/docker/Dockerfile - it's directory of our Dockerhfile
  • --target normal - it's needed for build only a part of the Dockerfile, which can be useful for optimizing the build process and reducing image size
  • the dot (.) - indicates that the current directory should be used as the context for the build

4️⃣The next step is run container:

docker run --network=yuit-net --name yuit-chart-frontend --rm -itd -p 127.0.0.1:3000:8080/tcp yuit-chart-frontend-image, where are the following parameters:

  • --network=yuit-net - it's our docker network
  • --name yuit-chart-frontend - this flag assigns a name to the container
  • --rm - this option tells Docker to automatically remove the container when it exits
  • -itd - this stands for i - interactive mode, t - allocates a pseudo-TTY and d - detached mode
  • -p 127.0.0.1:3000:8080/tcp - this option is used to publish a container's port to the host, where:
    • 3000:8080 indicates that port 3000 on the host will be mapped to port 8080 on the container
    • /tcp specifies the protocol to use (in this case, TCP). This part is optional, as TCP is the default protocol
  • yuit-chart-frontend-image - it's the name of the Docker image

5️⃣After running the frontend container, we can test the api through http://localhost:3000/

web app

My examples:

Thanks for reading and feedback!🤝

Top comments (0)