<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arthur Malumian</title>
    <description>The latest articles on DEV Community by Arthur Malumian (@amalumian).</description>
    <link>https://dev.to/amalumian</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F895808%2F1587f62c-2e3a-41cf-85f8-3c2c8be545e1.jpg</url>
      <title>DEV Community: Arthur Malumian</title>
      <link>https://dev.to/amalumian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amalumian"/>
    <language>en</language>
    <item>
      <title>NGINX and Docker</title>
      <dc:creator>Arthur Malumian</dc:creator>
      <pubDate>Thu, 16 Jan 2025 07:01:16 +0000</pubDate>
      <link>https://dev.to/amalumian/nginx-and-docker-2g3k</link>
      <guid>https://dev.to/amalumian/nginx-and-docker-2g3k</guid>
      <description>&lt;p&gt;In this article, I will share how to configure and use Docker and NGINX for both frontend and backend applications, providing a step-by-step approach for development and production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;For simplicity, I won’t fill the article with the actual application code. Instead, I’ll provide the locations of the relevant files discussed below. I will be using React (Vite) and Express, but this configuration will be suitable for most languages, with differences only in specific commands or package managers.&lt;/p&gt;

&lt;p&gt;Here’s the project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application/
├── frontend/
│   ├── .dockerignore
│   ├── Dockerfile.dev
│   ├── Dockerfile.prod
│   ├── nginx.conf
│   └── vite.config.js
├── backend/
│   ├── .dockerignore
│   ├── .env.dev
│   ├── .env.prod
│   ├── Dockerfile.dev
│   └── Dockerfile.prod
├── docker-compose.dev.yml
└── docker-compose.prod.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command Line Instructions
&lt;/h3&gt;

&lt;p&gt;The following commands are commonly used for managing Docker images and containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker image ls&lt;/code&gt; – Lists available Docker images.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker ps&lt;/code&gt; – Displays running containers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker ps -a&lt;/code&gt; – Lists all containers, including stopped ones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To build and run images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker build -t &amp;lt;image name&amp;gt; .&lt;/code&gt; – Builds an image with a specific name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker run -p &amp;lt;host-port:container-port&amp;gt; --name &amp;lt;container-name&amp;gt; &amp;lt;image name or ID&amp;gt;&lt;/code&gt; – Runs a container with port mapping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Managing containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker stop &amp;lt;container name or ID&amp;gt;&lt;/code&gt; – Stops a running container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker start &amp;lt;container name or ID&amp;gt;&lt;/code&gt; – Starts an existing container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Accessing container logs and shell:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker exec -it &amp;lt;container name or ID&amp;gt; /bin/sh&lt;/code&gt; – Opens a shell in the running container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker logs &amp;lt;container name or ID&amp;gt;&lt;/code&gt; – Displays container logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For multi-container setups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker compose up --build&lt;/code&gt; – Builds and starts containers defined in &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker compose -f &amp;lt;docker-compose file name&amp;gt; up --build&lt;/code&gt; – Uses a specific compose file for production. Add &lt;code&gt;-d&lt;/code&gt; to run in the background.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: Replace values inside &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; with the actual name, ID, or value as required.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;The main difference between &lt;code&gt;Dockerfile.dev&lt;/code&gt; and &lt;code&gt;Dockerfile.prod&lt;/code&gt; lies in optimization for development and production stages. In &lt;code&gt;Dockerfile.prod&lt;/code&gt;, we apply the following optimizations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;--omit=dev&lt;/code&gt; flag is used with &lt;code&gt;npm install&lt;/code&gt; to exclude development dependencies, reducing the final image size.&lt;/li&gt;
&lt;li&gt;The startup command is changed from &lt;code&gt;npm run dev&lt;/code&gt; to &lt;code&gt;npm run start&lt;/code&gt;, ensuring the application runs in production mode. When using &lt;code&gt;npm run dev&lt;/code&gt;, the application is started with &lt;code&gt;nodemon&lt;/code&gt;, which automatically restarts the server upon detecting changes in the source code, making it ideal for development. In contrast, &lt;code&gt;npm run start&lt;/code&gt; runs the application with the standard &lt;code&gt;node&lt;/code&gt; command, suitable for production where frequent restarts are unnecessary, ensuring a more stable and efficient environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Dockerfile.dev
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "dev"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Dockerfile.prod
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  .env
&lt;/h4&gt;

&lt;p&gt;Make sure to create &lt;code&gt;.env&lt;/code&gt; files in backend directory for both development and production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development (&lt;code&gt;.env.dev&lt;/code&gt;)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=development
PORT=3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Production (&lt;code&gt;.env.prod&lt;/code&gt;)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=production
PORT=3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  .dockerignore
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;.dockerignore&lt;/code&gt; file helps to exclude unnecessary files from being copied into the Docker image, reducing its size and build time. Here's a typical &lt;code&gt;.dockerignore&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
.env*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;Just like the backend, we create separate Dockerfiles for development and production in the frontend.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dockerfile.dev
&lt;/h4&gt;

&lt;p&gt;This file sets up a development environment with hot-reloading enabled for React (Vite):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5173&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "dev"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Dockerfile.prod
&lt;/h4&gt;

&lt;p&gt;The production Dockerfile involves a two-stage build process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build. This stage compiles the frontend application using the specified environment variables.&lt;/li&gt;
&lt;li&gt;Production. The compiled assets are copied into an NGINX container to serve the static files efficiently.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; VITE_API_URL&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; VITE_API_URL=$VITE_API_URL&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;nginx:1.23-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /usr/share/nginx/html/50x.html

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/dist /usr/share/nginx/html&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/default.conf&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  nginx.conf
&lt;/h4&gt;

&lt;p&gt;Basic NGINX configuration for a SPA application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; {
    &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;;
    &lt;span class="n"&gt;server_name&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt;;

    &lt;span class="n"&gt;root&lt;/span&gt; /&lt;span class="n"&gt;usr&lt;/span&gt;/&lt;span class="n"&gt;share&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;html&lt;/span&gt;;
    &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;;

    &lt;span class="n"&gt;location&lt;/span&gt; / {
        &lt;span class="n"&gt;try_files&lt;/span&gt; $&lt;span class="n"&gt;uri&lt;/span&gt; /&lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  vite.config.js
&lt;/h4&gt;

&lt;p&gt;When running React (Vite) apps inside Docker, it’s important to configure the Vite server correctly for development mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// https://vitejs.dev/config/&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5173&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;usePolling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These parameters are necessary to ensure the correct functioning of a Vite application inside a Docker container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;host: '0.0.0.0'&lt;/code&gt; — makes the application accessible from outside the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;port: 5173&lt;/code&gt; — explicitly specifies the port to be exposed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;watch: { usePolling: true }&lt;/code&gt; — solves file change detection issues inside the container, ensuring HMR works properly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  .dockerignore
&lt;/h4&gt;

&lt;p&gt;Same as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
.env*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker-compose.yml
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;docker-compose.yml&lt;/code&gt; simplifies managing multiple services, such as the backend and frontend, by defining them in a single file. Below are separate configurations for development and production.&lt;/p&gt;

&lt;h4&gt;
  
  
  docker-compose.dev.yml
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend-dev&lt;/span&gt;
    &lt;span class="na"&gt;pull_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;never&lt;/span&gt; &lt;span class="c1"&gt;# Image pull policy — do not pull from a registry, use the local image&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend-dev-container&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend/.env.dev&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3000:3000&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./backend:/app&lt;/span&gt; &lt;span class="c1"&gt;# Mounting the local code directory into the container for development&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt; &lt;span class="c1"&gt;# Excluding node_modules from mounting to use container-specific dependencies&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend-dev&lt;/span&gt;
    &lt;span class="na"&gt;pull_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;never&lt;/span&gt; &lt;span class="c1"&gt;# Image pull policy — do not pull from a registry, use the local image&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend-dev-container&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5173:5173&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./frontend:/app&lt;/span&gt; &lt;span class="c1"&gt;# Mounting the local code directory into the container for development&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/app/node_modules&lt;/span&gt; &lt;span class="c1"&gt;# Excluding node_modules from mounting to use container-specific dependencies&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt; &lt;span class="c1"&gt;# Dependency on the 'backend' service, ensuring the backend starts before the frontend&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Definition of a custom network&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  docker-compose.prod.yml
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;pull_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;never&lt;/span&gt; &lt;span class="c1"&gt;# Image pull policy — do not pull from a registry, use the local image&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend-container&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend/.env.prod&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.prod&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3030:3000&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./backend/uploads:/app/uploads&lt;/span&gt; &lt;span class="c1"&gt;# If you need some directory to share between container and host machine&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt; &lt;span class="c1"&gt;# Always restart the container if it stops&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
    &lt;span class="na"&gt;pull_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;never&lt;/span&gt; &lt;span class="c1"&gt;# Image pull policy — use the local image, do not pull from a registry&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend-container&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.prod&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://api.example.com&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3031:80&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt; &lt;span class="c1"&gt;# Always restart the container if it stops&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt; &lt;span class="c1"&gt;# Dependency on the 'backend' service, ensuring the backend starts before the frontend&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Definition of a custom network&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important clarification.&lt;/strong&gt; Notice that in the frontend development environment, I pass environment variables through &lt;code&gt;environment&lt;/code&gt;, but in the production environment, I pass them through build arguments (&lt;code&gt;args&lt;/code&gt;). Why is that?&lt;/p&gt;

&lt;p&gt;In development, environment variables can be dynamic and change their values while the application is running, which is convenient for frequent changes and testing.&lt;/p&gt;

&lt;p&gt;In production, values are usually fixed during the build process, as the application will run in a stable environment, and any changes should require rebuilding the container.&lt;/p&gt;

&lt;p&gt;Here’s how it’s connected:&lt;/p&gt;

&lt;p&gt;For frontend applications built with tools like Vite, variables such as the API URL in my case need to be passed through &lt;code&gt;args&lt;/code&gt; so they are embedded into the compiled code during the build stage. If you use &lt;code&gt;environment&lt;/code&gt;, the variable will only be available after the container starts, which is more suitable for a development environment.&lt;/p&gt;

&lt;p&gt;Therefore, if you want the variable to be embedded in the static code during the build stage, it's better to use &lt;code&gt;args&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  NGINX
&lt;/h2&gt;

&lt;p&gt;Once you have rented a server, installed Git, Docker, NGINX, cloned your project from GitHub, and started the Docker containers, you can begin configuring NGINX.&lt;/p&gt;

&lt;p&gt;The core idea is that NGINX will proxy requests to locally running servers. This means that NGINX will receive incoming HTTP requests and forward them to your backend or frontend services running locally, based on the rules defined in the NGINX configuration.&lt;/p&gt;

&lt;p&gt;Here’s the NGINX directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/etc/nginx/
├── sites-available/
│   ├── default
│   └── example.com.conf
├── sites-enabled/
│   ├── default@ -&amp;gt; /etc/nginx/sites-available/default
│   └── example.com.conf@ -&amp;gt; /etc/nginx/sites-available/example.com.conf
└── nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sites-available/&lt;/code&gt; contains configuration files for each website or service you want to configure. These files are not active until linked to &lt;code&gt;sites-enabled/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sites-enabled/&lt;/code&gt; contains symbolic links (&lt;code&gt;ln -s&lt;/code&gt;) to the configuration files in &lt;code&gt;sites-available/&lt;/code&gt;, which make them active.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nginx.conf&lt;/code&gt; is the main configuration file where global settings and directives are specified.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Command Line Instructions
&lt;/h3&gt;

&lt;p&gt;The following are commonly used &lt;code&gt;systemctl&lt;/code&gt; commands to manage the NGINX service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start|reload|restart|stop|status nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt;: Starts the NGINX service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reload&lt;/code&gt;: Reloads the NGINX service to apply changes in configuration without restarting the service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;restart&lt;/code&gt;: Restarts the NGINX service, which is useful when changes are made that require a full restart.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stop&lt;/code&gt;: Stops the NGINX service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: Displays the current status of the NGINX service, including whether it's running or not.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  nginx.conf
&lt;/h3&gt;

&lt;p&gt;This is the general configuration file for NGINX. Below is an example of a basic setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;www&lt;/span&gt;-&lt;span class="n"&gt;data&lt;/span&gt;;
&lt;span class="n"&gt;worker_processes&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;;
&lt;span class="n"&gt;pid&lt;/span&gt; /&lt;span class="n"&gt;run&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;.&lt;span class="n"&gt;pid&lt;/span&gt;;
&lt;span class="n"&gt;error_log&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;log&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;error&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;;
&lt;span class="n"&gt;include&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;modules&lt;/span&gt;-&lt;span class="n"&gt;enabled&lt;/span&gt;/*.&lt;span class="n"&gt;conf&lt;/span&gt;;

&lt;span class="n"&gt;events&lt;/span&gt; {
  &lt;span class="n"&gt;worker_connections&lt;/span&gt; &lt;span class="m"&gt;768&lt;/span&gt;;
}

&lt;span class="n"&gt;http&lt;/span&gt; {

  &lt;span class="c"&gt;##
&lt;/span&gt;  &lt;span class="c"&gt;# Basic Settings
&lt;/span&gt;  &lt;span class="c"&gt;##
&lt;/span&gt;
  &lt;span class="n"&gt;sendfile&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;
  &lt;span class="n"&gt;tcp_nopush&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;
  &lt;span class="n"&gt;types_hash_max_size&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;;

  &lt;span class="n"&gt;include&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;mime&lt;/span&gt;.&lt;span class="n"&gt;types&lt;/span&gt;;
  &lt;span class="n"&gt;default_type&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;/&lt;span class="n"&gt;octet&lt;/span&gt;-&lt;span class="n"&gt;stream&lt;/span&gt;;

  &lt;span class="c"&gt;##
&lt;/span&gt;  &lt;span class="c"&gt;# SSL Settings
&lt;/span&gt;  &lt;span class="c"&gt;##
&lt;/span&gt;
  &lt;span class="n"&gt;ssl_protocols&lt;/span&gt; &lt;span class="n"&gt;TLSv1&lt;/span&gt; &lt;span class="n"&gt;TLSv1&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="n"&gt;TLSv1&lt;/span&gt;.&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;TLSv1&lt;/span&gt;.&lt;span class="m"&gt;3&lt;/span&gt;; &lt;span class="c"&gt;# Dropping SSLv3, ref: POODLE
&lt;/span&gt;  &lt;span class="n"&gt;ssl_prefer_server_ciphers&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;

  &lt;span class="c"&gt;##
&lt;/span&gt;  &lt;span class="c"&gt;# Logging Settings
&lt;/span&gt;  &lt;span class="c"&gt;##
&lt;/span&gt;
  &lt;span class="n"&gt;access_log&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;log&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;access&lt;/span&gt;.&lt;span class="n"&gt;log&lt;/span&gt;;

  &lt;span class="c"&gt;##
&lt;/span&gt;  &lt;span class="c"&gt;# Gzip Settings
&lt;/span&gt;  &lt;span class="c"&gt;##
&lt;/span&gt;
  &lt;span class="n"&gt;gzip&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;

  &lt;span class="n"&gt;gzip_vary&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;;
  &lt;span class="n"&gt;gzip_proxied&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;;
  &lt;span class="n"&gt;gzip_comp_level&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;;
  &lt;span class="n"&gt;gzip_buffers&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;;
  &lt;span class="n"&gt;gzip_http_version&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;;
  &lt;span class="n"&gt;gzip_types&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;/&lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;/&lt;span class="n"&gt;css&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;/&lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;/&lt;span class="n"&gt;javascript&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;/&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;/&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;/&lt;span class="n"&gt;xml&lt;/span&gt;+&lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;/&lt;span class="n"&gt;javascript&lt;/span&gt;;

  &lt;span class="c"&gt;##
&lt;/span&gt;  &lt;span class="c"&gt;# Proxy Path
&lt;/span&gt;  &lt;span class="c"&gt;##
&lt;/span&gt;
  &lt;span class="n"&gt;proxy_cache_path&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;cache&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;:&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;keys_zone&lt;/span&gt;=&lt;span class="n"&gt;my_cache&lt;/span&gt;:&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;max_size&lt;/span&gt;=&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;inactive&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;use_temp_path&lt;/span&gt;=&lt;span class="n"&gt;off&lt;/span&gt;;

  &lt;span class="c"&gt;##
&lt;/span&gt;  &lt;span class="c"&gt;# Virtual Host Configs
&lt;/span&gt;  &lt;span class="c"&gt;##
&lt;/span&gt;
  &lt;span class="n"&gt;include&lt;/span&gt; /&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;nginx&lt;/span&gt;/&lt;span class="n"&gt;sites&lt;/span&gt;-&lt;span class="n"&gt;enabled&lt;/span&gt;/*;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;http&lt;/code&gt; block contains most of the configuration related to handling HTTP traffic, including settings for SSL, logging, compression (gzip), and proxy caching.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;include /etc/nginx/sites-enabled/*;&lt;/code&gt; directive tells NGINX to include all configurations from &lt;code&gt;sites-enabled/&lt;/code&gt;, effectively enabling the configurations for your sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  default or example.com.conf
&lt;/h3&gt;

&lt;p&gt;Here are two examples of NGINX configuration files. The first one is for a setup where you have specific domain (&lt;code&gt;example.com&lt;/code&gt;), while the second one is for a setup where you might not have a domain but still want to proxy traffic.&lt;/p&gt;

&lt;h4&gt;
  
  
  default
&lt;/h4&gt;

&lt;p&gt;This configuration might be used when you're setting up a service without a domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; {
  &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;;

  &lt;span class="n"&gt;location&lt;/span&gt; / {
    &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;localhost&lt;/span&gt;:&lt;span class="m"&gt;3030&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; $&lt;span class="n"&gt;host&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Real&lt;/span&gt;-&lt;span class="n"&gt;IP&lt;/span&gt; $&lt;span class="n"&gt;remote_addr&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;For&lt;/span&gt; $&lt;span class="n"&gt;proxy_add_x_forwarded_for&lt;/span&gt;;
  }
}

&lt;span class="n"&gt;server&lt;/span&gt; {
  &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;;

  &lt;span class="n"&gt;location&lt;/span&gt; / {
    &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;localhost&lt;/span&gt;:&lt;span class="m"&gt;3031&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; $&lt;span class="n"&gt;host&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Real&lt;/span&gt;-&lt;span class="n"&gt;IP&lt;/span&gt; $&lt;span class="n"&gt;remote_addr&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;For&lt;/span&gt; $&lt;span class="n"&gt;proxy_add_x_forwarded_for&lt;/span&gt;;

    &lt;span class="n"&gt;proxy_cache&lt;/span&gt; &lt;span class="n"&gt;my_cache&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="m"&gt;301&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;;
    &lt;span class="n"&gt;add_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Cache&lt;/span&gt;-&lt;span class="n"&gt;Status&lt;/span&gt; $&lt;span class="n"&gt;upstream_cache_status&lt;/span&gt;;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, In the &lt;code&gt;docker-compose.prod.yml&lt;/code&gt; file, you need modify environment variables under &lt;code&gt;frontend &amp;gt; build &amp;gt; args&lt;/code&gt; to specify values such as the API URL for the production environment. This ensures that when the frontend is built, the correct API endpoint is set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://&amp;lt;IP of your server&amp;gt;:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  example.com.conf
&lt;/h4&gt;

&lt;p&gt;This configuration is useful if you have specific domain (&lt;code&gt;example.com&lt;/code&gt;) for your services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;server&lt;/span&gt; {
  &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;;
  &lt;span class="n"&gt;server_name&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;;

  &lt;span class="n"&gt;location&lt;/span&gt; / {
    &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;localhost&lt;/span&gt;:&lt;span class="m"&gt;3030&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; $&lt;span class="n"&gt;host&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Real&lt;/span&gt;-&lt;span class="n"&gt;IP&lt;/span&gt; $&lt;span class="n"&gt;remote_addr&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;For&lt;/span&gt; $&lt;span class="n"&gt;proxy_add_x_forwarded_for&lt;/span&gt;;
  }
}

&lt;span class="n"&gt;server&lt;/span&gt; {
  &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;;
  &lt;span class="n"&gt;server_name&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;;

  &lt;span class="n"&gt;location&lt;/span&gt; / {
    &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;localhost&lt;/span&gt;:&lt;span class="m"&gt;3031&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; $&lt;span class="n"&gt;host&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Real&lt;/span&gt;-&lt;span class="n"&gt;IP&lt;/span&gt; $&lt;span class="n"&gt;remote_addr&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;For&lt;/span&gt; $&lt;span class="n"&gt;proxy_add_x_forwarded_for&lt;/span&gt;;

    &lt;span class="n"&gt;proxy_cache&lt;/span&gt; &lt;span class="n"&gt;my_cache&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="m"&gt;301&lt;/span&gt; &lt;span class="m"&gt;302&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;;
    &lt;span class="n"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;;
    &lt;span class="n"&gt;add_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Cache&lt;/span&gt;-&lt;span class="n"&gt;Status&lt;/span&gt; $&lt;span class="n"&gt;upstream_cache_status&lt;/span&gt;;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need create a symbolic link to this file in &lt;code&gt;sites-enabled/&lt;/code&gt; to activate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;After setting up Docker and NGINX, you may want to add HTTPS support. One popular and free solution is Certbot by Let’s Encrypt, which automates the process of obtaining and renewing SSL/TLS certificates. Ensure that your domain is correctly pointed to your server before running Certbot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nginx.org/en/" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://certbot.eff.org/" rel="noopener noreferrer"&gt;Certbot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nginx</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
