<?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: Kilama Elie</title>
    <description>The latest articles on DEV Community by Kilama Elie (@kilamaelie).</description>
    <link>https://dev.to/kilamaelie</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%2F1143485%2F63caa4b9-b30c-40e9-9a3b-2c66e90f25c1.jpeg</url>
      <title>DEV Community: Kilama Elie</title>
      <link>https://dev.to/kilamaelie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kilamaelie"/>
    <language>en</language>
    <item>
      <title>Deploy a Django Rest Api on AWS EC2 using Docker, NGINX, Gunicorn and GitHub Action.</title>
      <dc:creator>Kilama Elie</dc:creator>
      <pubDate>Thu, 24 Apr 2025 11:06:51 +0000</pubDate>
      <link>https://dev.to/kilamaelie/deploy-a-django-rest-api-on-aws-ec2-using-docker-nginx-gunicorn-and-github-action-4c6c</link>
      <guid>https://dev.to/kilamaelie/deploy-a-django-rest-api-on-aws-ec2-using-docker-nginx-gunicorn-and-github-action-4c6c</guid>
      <description>&lt;p&gt;Django, a high-level Python web framework, is a popular choice for building RESTful APIs. Once your Django Rest API has been developed and tested, deploying it becomes a crucial step in making it accessible to users.&lt;/p&gt;

&lt;p&gt;In this blog post, we’ll guide you through the process of deploying a Django Rest API on AWS EC2 using Docker for containerization, NGINX as a reverse proxy and GitHub Actions for automated deployment.&lt;/p&gt;

&lt;p&gt;And, we’ll take a closer look at each step, explain the concepts, provide code snippets and show you exactly what you need to do. Let’s get to the heart of the matter!&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;/p&gt;

&lt;p&gt;Before diving into this blog, you need a solid foundation in several technologies and concepts in order to ensure efficient implementation. Such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Django: a high-level Python web framework.&lt;/li&gt;
&lt;li&gt;Django REST framework: is a powerful and flexible toolkit for building Web APIs.&lt;/li&gt;
&lt;li&gt;NGINX: A basic knowledge of NGINX as a web server and reverse proxy will be necessary to set up the server environment.&lt;/li&gt;
&lt;li&gt;AWS Account: Create an AWS account and set up an EC2 instance.&lt;/li&gt;
&lt;li&gt;Docker: Install Docker on your local machine for building and running containers.&lt;/li&gt;
&lt;li&gt;GitHub Repository: Host your Django project on a GitHub repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind that these requirements are essential, but that you don’t have to be an expert in every field. You can learn and develop your expertise as you go along. Moreover, official documentation online tutorials and communities can provide invaluable support as you learn.&lt;/p&gt;

&lt;p&gt;Note that you can folk the source code at this the following repo: &lt;a href="https://github.com/kilamaelie/Deploy-Django-Rest-Api-on-AWS-EC2-using-Docker-NGINX-Gunicorn-And-GithubAction.git" rel="noopener noreferrer"&gt;Try it yourself&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a New Project
&lt;/h4&gt;

&lt;p&gt;Open your terminal and navigate to the directory where you want to create the project, for the purpose of this tutorial we’ll create a directory with a name &lt;code&gt;django-tutorial&lt;/code&gt; or you can name it anything depending on your project name. This folder is considered as the &lt;code&gt;root&lt;/code&gt; directory for our application which contains all folders and files structures.&lt;/p&gt;

&lt;p&gt;Then, we will change our directory into the &lt;code&gt;django-tutorial&lt;/code&gt; folder and all other parts of configuration files or folders will be in this folder.&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;django-tutorial
 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;django-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;1. Creating the Rest API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First of all, we need to install Django and Django Rest Framework, so make sure you already have Python installed on your machine.&lt;/p&gt;
&lt;h5&gt;
  
  
  create a virtual environment:
&lt;/h5&gt;

&lt;p&gt;Inside the &lt;code&gt;django-tutorial&lt;/code&gt; folder, to create and activate a new virtual environment runs the following command :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Set up Django project
&lt;/h4&gt;

&lt;p&gt;We’ll be using &lt;code&gt;pip&lt;/code&gt;, which is the package installer for Python, to install the required dependencies. Run the following commands:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
django-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;Django
django-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;djangorestframework
django-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;gunicorn
django-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;django-cors-headers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let’s create a Django project and name it &lt;code&gt;store&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;django-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;django-admin startproject store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Create a Django app:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;django-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;store
django-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py startapp book
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Define a book model
&lt;/h4&gt;

&lt;p&gt;In the &lt;code&gt;book/models.py&lt;/code&gt;, define the book model&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;publication_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Create a serializer for the Book model
&lt;/h4&gt;

&lt;p&gt;Inside book directory, let’s create a file named &lt;code&gt;serializers.py&lt;/code&gt; to define the BookSerializer.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Books&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookSerializer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelSerializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
      &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Books&lt;/span&gt;
      &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__all__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Define a Views model
&lt;/h4&gt;

&lt;p&gt;In the &lt;code&gt;book/views.py&lt;/code&gt; , let’s define the BookViewSet model, which will handle the API requests&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;viewsets&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Books&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.serializers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BookSerializer&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookViewSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelViewSet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;queryset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Books&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;serializer_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BookSerializer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Define a URL routing
&lt;/h4&gt;

&lt;p&gt;In the &lt;code&gt;core/urls.py&lt;/code&gt;, let’s add the route url for the application&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework.routers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DefaultRouter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;books.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BookViewSet&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;book&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BookViewSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&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;After that, let's update &lt;code&gt;core/settings.py&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rest_framework&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;corsheaders&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gunicorn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;book&lt;/span&gt;&lt;span class="sh"&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;Also let’s add the &lt;code&gt;Corsheaders middlewares&lt;/code&gt; here:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;corsheaders.middleware.CorsMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.middleware.common.CommonMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Test REST API locally
&lt;/h4&gt;

&lt;p&gt;First all, let make the migrations to create the database table&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py makemigrations
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Start the Django development server:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And should able to access the REST API at the &lt;code&gt;http://localhost:8000&lt;/code&gt; or &lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzz0hm7ctsmjdfcqgr0c1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzz0hm7ctsmjdfcqgr0c1.webp" alt="django"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before diving into the next section, let’s create requirements.txt file in the root directory which contains a list of all the installed packages in your Python environment, along with their corresponding versions, to generate the requirements file using:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;2. Add Docker to the Rest API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker provides a standardized way to package and distribute applications, making it easier to deploy and manage across different environments. In this section, we’ll containerize our Django application using Docker.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven’t already, install Docker on your machine by following the instructions for your operating system: &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;https://docs.docker.com/get-docker/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Create a Dockerfile:
&lt;/h4&gt;

&lt;p&gt;In your Django project’s store directory(in the same directory where manage.py is located ), create a file named &lt;code&gt;Dockerfile&lt;/code&gt;. This file defines the instructions to build your Docker image.&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="c"&gt;# Pull the image from Dockerhub&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:alpine3.19&lt;/span&gt;

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

&lt;span class="c"&gt;# set up python environment variables&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDOWNWRITEBYTECODE 1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONNUNBUFFER 1&lt;/span&gt;

&lt;span class="c"&gt;# update and  install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./requirements.txt /api/requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /api/requirements.txt

&lt;span class="c"&gt;# copy project&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Expose the port server is running on&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since in this project will have more than one container , then we ‘ll need to use &lt;code&gt;docker-compose&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Create a Docker-compose file:
&lt;/h4&gt;

&lt;p&gt;Allows you to define and run multiple Docker containers as a single service, making it easier to manage complex applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven’t already, you’ll need to install Docker Compose.&lt;br&gt;
Follow the installation instructions for your operating system: &lt;a href="https://docs.docker.com/compose/install" rel="noopener noreferrer"&gt;https://docs.docker.com/compose/install&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your project’s root directory as &lt;code&gt;django-tutorial&lt;/code&gt;, create a file named &lt;code&gt;docker-compose.yml&lt;/code&gt;.&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bookApi&lt;/span&gt;&lt;span class="pi"&gt;:&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;./store&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&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python manage.py runserver&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This defines only one service &lt;code&gt;bookapi&lt;/code&gt; but a bit later more will be added.&lt;/p&gt;
&lt;h4&gt;
  
  
  Run the Django app using Docker:
&lt;/h4&gt;

&lt;p&gt;So in order to test it, just go in your terminal, navigate to the directory containing the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftaubhuvueilifbb53hgk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftaubhuvueilifbb53hgk.webp" alt="docker-compose"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can access your django app at &lt;code&gt;http://localhost:8000&lt;/code&gt;. Kill the task in the terminal where &lt;code&gt;docker-compose up&lt;/code&gt; is running in order to stop the. To remove the containers and their associated resources:&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="s"&gt;docker-compose down --rmi all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Adding a Gunicorn:
&lt;/h4&gt;

&lt;p&gt;The Gunicorn “Green Unicorn” is a Python Web Server Gateway Interface HTTP server.&lt;/p&gt;

&lt;p&gt;So, let updating the docker-compose file for the api service to start with gunicorn as web server gateway&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&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;./store&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&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gunicorn core.wsgi:application --bind 0.0.0.0:8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;3. Adding NGINX:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;NGINX is a web server for content and application delivery and can also be used as a &lt;code&gt;reverse-proxy&lt;/code&gt; which improve performance and manage routing in production environments.&lt;/p&gt;

&lt;p&gt;In the root of your project directory, create a &lt;code&gt;Nginx&lt;/code&gt; directory named nginx then create a configuration file named &lt;code&gt;nginx.conf&lt;/code&gt; . So inside nginx.conf file adds the following configuration:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# nginx.conf  &lt;/span&gt;
&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;api_upstream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
   &lt;span class="c1"&gt;#server_name can add your domain or Ip address for your server in the production&lt;/span&gt;
   &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt; &lt;span class="mi"&gt;200M&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Url-Scheme&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$http_host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_redirect&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://api_upstream&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;in the same directory as your Nginx, create a &lt;code&gt;Dockerfile&lt;/code&gt; which defines how to build a custom Nginx Docker Image:&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="c1"&gt;# Use an official Nginx image as the base image&lt;/span&gt;
&lt;span class="s"&gt;FROM nginx:latest&lt;/span&gt;
&lt;span class="c1"&gt;# Remove any existing config files&lt;/span&gt;
&lt;span class="s"&gt;RUN rm /etc/nginx/conf.d/*&lt;/span&gt;
&lt;span class="c1"&gt;# Copy the custom Nginx configuration&lt;/span&gt;
&lt;span class="s"&gt;COPY nginx.conf /etc/nginx/conf.d/&lt;/span&gt;
&lt;span class="c1"&gt;# Expose port 80 for Nginx&lt;/span&gt;
&lt;span class="s"&gt;EXPOSE &lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Before testing in development, let’s update our &lt;code&gt;docker-compose&lt;/code&gt; file by adding the Nginx service:&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&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;./store&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&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gunicorn core.wsgi:application --bind 0.0.0.0:8000&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;static_volume:/store/staticfiles/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;media_volume:/store/mediafiles/&lt;/span&gt;
    &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&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;./nginx&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&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;80:80&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;static_volume:/store/staticfiles/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;media_volume:/store/mediafiles/&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;static_volume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;media_volume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let updating &lt;code&gt;core/settings.p&lt;/code&gt;y and &lt;code&gt;core/urls.py&lt;/code&gt; with &lt;code&gt;STATIC_ROOT&lt;/code&gt; to ensure that all static files are gathered and ready to be served by the web server.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;core/settings.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvncdtw0a7m127uixavd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvncdtw0a7m127uixavd.webp" alt="core-settings.py"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;and core/urls.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0hesuxpfx75kyxqrig9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0hesuxpfx75kyxqrig9.webp" alt="core-urls.py"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the purpose of testing if everything works, we’ll open a terminal and navigate to your &lt;code&gt;root&lt;/code&gt; directory. Run the following command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then let’s testing at &lt;code&gt;http://localhost:80&lt;/code&gt; or &lt;code&gt;http://127.0.0.1:80&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsa4oa811s70wy3pejen.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsa4oa811s70wy3pejen.webp" alt="nginx"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To remove the container and its associated resources, run:&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="s"&gt;docker-compose rm&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, let’s create &lt;code&gt;docker-compose.ci&lt;/code&gt; and &lt;code&gt;docker-compose.prod&lt;/code&gt; files for the &lt;code&gt;production&lt;/code&gt; and &lt;code&gt;workflow&lt;/code&gt; purpose in the &lt;code&gt;root&lt;/code&gt; directory of the project&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose.ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bookapi&lt;/span&gt;&lt;span class="pi"&gt;:&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;./store&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&lt;/span&gt;
      &lt;span class="na"&gt;cache_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${DJANGO_TUTORIAL_API_IMAGE}'&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${DJANGO_TUTORIAL_API_IMAGE}'&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gunicorn core.wsgi:application --bind 0.0.0.0:8000&lt;/span&gt;
    &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&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;./nginx&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&lt;/span&gt;
    &lt;span class="na"&gt;cache_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${DJANGO_TUTORIAL_NGINX_IMAGE}'&lt;/span&gt;
    &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${DJANGO_TUTORIAL_NGINX_IMAGE}'&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;80:80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose.prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bookapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django-api'&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${DJANGO_TUTORIAL_API_IMAGE}'&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gunicorn core.wsgi:application --bind 0.0.0.0:8000&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;static_volume:/store/staticfiles/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;media_volume:/store/mediafiles/&lt;/span&gt;
    &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginx'&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${DJANGO_TUTORIAL_NGINX_IMAGE}'&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;80:80&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;static_volume:/store/staticfiles/&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;media_volume:/store/mediafiles/&lt;/span&gt;
 &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;static_volume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;media_volume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So for this tutorial will use Github packages for hosting the images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/features/packages" rel="noopener noreferrer"&gt;GitHub Package Registry&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;A package hosting service provided by GitHub. It allows you to publish, share, and manage software packages directly within your GitHub repositories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. AWS Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s start by setting up an EC2 instance to deploy our application. To do this, and you’ll need to open an &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt; account (if you don’t already have one).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don’t know how to set up AWS EC2 , please visit this link which will guide you through how to : &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/get-set-up-for-amazon-ec2.html" rel="noopener noreferrer"&gt;launch EC2&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once we’ve finished configuring AWS EC2 and the instance is up and running, we can install Docker on it..&lt;/p&gt;

&lt;p&gt;So connect via SSH into the instance using your Key Pair as :&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="nv"&gt;$ &lt;/span&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; your-key-pair.pem ec2-user@&amp;lt;PUBLIC-IP-ADDRESS&amp;gt;

&lt;span class="c"&gt;#example&lt;/span&gt;
&lt;span class="c"&gt;# ssh -i ~./ssh/django-tutorial.perm ec2-user@51.20.75.55&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After accessing the instance, start by updating the server package and installing the latest version of &lt;code&gt;Docker&lt;/code&gt; and &lt;code&gt;Docker-compose&lt;/code&gt;:&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="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker start
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/docker/compose/releases/latest/download/docker-compose-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x /usr/local/bin/docker-compose

&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
Docker version 20.10.23, build 7155243

&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;--version&lt;/span&gt;
Docker Compose version v2.18.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Add the &lt;code&gt;ec2-user&lt;/code&gt; to the docker &lt;code&gt;group&lt;/code&gt; so that you can use it without running the Docker command with &lt;code&gt;sudo&lt;/code&gt; :&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="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; docker ec2-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, let’s generate the SSH KEY&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="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Please! save the Key without setting any password&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, copy the public key into the &lt;code&gt;authorized_keys&lt;/code&gt; file and set the appropriate authorizations.:&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="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa.pub
&lt;span class="c"&gt;# Copy this public key into ~/.ssh/authorized_keys&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;vi ~/.ssh/authorized_keys
&lt;span class="c"&gt;# After adding the public key into authorized_key then exits  from vi text editor&lt;/span&gt;
&lt;span class="c"&gt;# Then add these commands are used to change the permissions of these files&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, copy the contents of the private key&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="o"&gt;[&lt;/span&gt;ec2-user]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa

&lt;span class="c"&gt;# Copy the private key somewhere for later use&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;4. Automating Deployment with GitHub Actions to AWS EC2&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Github provide continuous integration and continuous deployment (CI / CD) platform.&lt;/p&gt;

&lt;p&gt;Since our workflow will use GitHub’s core features (GitHub packages), GitHub requires you to create an access token, which will be used as a GitHub user ID for all your interactions.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;Personal access tokens&lt;/a&gt; area in the Developer settings of your GitHub profile and click Generate new token.&lt;/p&gt;
&lt;h4&gt;
  
  
  Adding GitHub Actions directory
&lt;/h4&gt;

&lt;p&gt;To configure &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;, start by adding a new directory called &lt;code&gt;.github&lt;/code&gt; to the root of your project, then within this directory, add another directory called &lt;code&gt;workflows&lt;/code&gt; . Now to configure the workflow, which is a set of one or more jobs, create a new file in the workflows directory called &lt;code&gt;main.yml&lt;/code&gt; . You can also run the following command in your root directory.&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;mkdir&lt;/span&gt; .github &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; .github
&lt;span class="nb"&gt;mkdir &lt;/span&gt;workflows &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;workflows
&lt;span class="nb"&gt;touch &lt;/span&gt;main.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can only fork the complete &lt;code&gt;main.yml&lt;/code&gt; on this link :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Notes about the secrets&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;secrets.PERSONAL_ACCESS_TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;secrets.NAMESPACE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;secrets.PRIVATE_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;secret.AWS_EC2_IP_ADDRESS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;secrets.AWS_HOST_USER&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the secrets need to be set in your repository’s &lt;a href="https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets" rel="noopener noreferrer"&gt;secrets&lt;/a&gt; (Settings &amp;gt; Secrets) and use your Github username or your organization name as your &lt;code&gt;NAMESPACE&lt;/code&gt; and your personal access token as &lt;code&gt;PERSONAL_ACCESS_TOKEN&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;Once you are done, commit and push your code to the Github to trigger the workflow to run, so now you should see the images in the Github Packages.&lt;/p&gt;

&lt;p&gt;Workflow successfully finished :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxp1f8rmhqcy2vchmzmr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxp1f8rmhqcy2vchmzmr.webp" alt="workflows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once all our jobs have been executed, navigate to the IP of your instance, and you should see the &lt;code&gt;django-tutorial&lt;/code&gt; application running :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpj8ovfb3trm15e1ffjn8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpj8ovfb3trm15e1ffjn8.webp" alt="with-ip-address"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let’s add the &lt;code&gt;collectstatic&lt;/code&gt; files to the RestApi to solve the problem of &lt;code&gt;STATIC_ROOT&lt;/code&gt; by ssh into AWS EC2 with the key pair created early then run:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#bookapi as service into the docker-compose.prod file&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;ec2-user@ip-172-31-21-196 ~]&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.prod.yml &lt;span class="nb"&gt;exec &lt;/span&gt;bookapi sh
&lt;span class="c"&gt;# it'll open the django sh &lt;/span&gt;

/store &lt;span class="c"&gt;# python manage.py collectstatic --no-input --clear&lt;/span&gt;

160 static files copied to &lt;span class="s1"&gt;'/store/staticfiles'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
/store &lt;span class="c"&gt;# exit&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;ec2-user@ip-172-31-21-196 ~]&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then just reload the IP of your instance&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuhzncbsd2g3dp72oatx.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuhzncbsd2g3dp72oatx.webp" alt="ip-address-with-collectstatic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👏 In summary, deploying a Django REST API on AWS EC2 using Docker, NGINX, Gunicorn and GitHub Actions offers a robust and scalable solution for hosting Django applications in a production environment. Automating the deployment process with GitHub Actions improves productivity and reduces the risk of errors, enabling seamless integration of code changes into the deployment pipeline.&lt;/p&gt;

&lt;p&gt;If you would like to improve your skills, click on the following link on &lt;a href="https://www.patreon.com/kilamaelie/shop/master-react-app-deployment-aws-ec2-53-1089307?utm_medium=clipboard_copy&amp;amp;utm_source=copyLink&amp;amp;utm_campaign=productshare_fan&amp;amp;utm_content=join_link" rel="noopener noreferrer"&gt;Master React App Deployment: AWS EC2, Route 53, SSL &amp;amp; GitHub Actions,A comprehensive manual which walks you through the deployment process, leveraging the power of Docker, AWS EC2, Route 53, SSL encryption and GitHub actions to build a robust CI/CD pipeline.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A comprehensive manual which walks you through the deployment process, leveraging the power of AWS EC2, Route 53, SSL encryption and GitHub actions to build a robust CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;If you found this article useful, you can support me by &lt;a href="https://patreon.com/kilamaelie?utm_medium=unknown&amp;amp;utm_source=join_link&amp;amp;utm_campaign=creatorshare_creator&amp;amp;utm_content=copyLink" rel="noopener noreferrer"&gt;buying me a coffee ☕️&lt;/a&gt;. Your contribution fuels my efforts to create more relevant content and helps me to continue sharing my knowledge with the community. Also, feel free to fork the GitHub repository associated with this article and contribute your ideas, improvements or comments. Together, we can build a stronger ecosystem and enable developers to excel in deploying Django applications on AWS EC2. Don’t forget to give it a round of applause 👏 if you found it useful!&lt;/p&gt;

</description>
      <category>django</category>
      <category>nginx</category>
      <category>aws</category>
      <category>docker</category>
    </item>
    <item>
      <title>Deploy a Next.js App on AWS EC2 with Docker, NGINX, and Automate with GitHub Actions.</title>
      <dc:creator>Kilama Elie</dc:creator>
      <pubDate>Mon, 21 Apr 2025 05:12:57 +0000</pubDate>
      <link>https://dev.to/kilamaelie/deploy-a-nextjs-app-on-aws-ec2-with-docker-nginx-and-automate-with-github-actions-5g4m</link>
      <guid>https://dev.to/kilamaelie/deploy-a-nextjs-app-on-aws-ec2-with-docker-nginx-and-automate-with-github-actions-5g4m</guid>
      <description>&lt;p&gt;Next.js is a powerful React framework that provides server-side rendering, static site generation, and API routes. Deploying a Next.js app on an AWS EC2 instance allows for better control and customization of the hosting environment. This guide will walk you through the process of setting up a Next.js application on AWS EC2 using Docker and NGINX while automating deployment with GitHub Actions.&lt;/p&gt;

&lt;p&gt;Before following this Next.js tutorial, we recommend that you first consult &lt;a href="https://dev.to/kilamaelie/build-and-deploy-a-reactjs-app-to-aws-ec2-with-docker-nginx-and-automate-with-github-actions-4bk9"&gt;Build and Deploy a ReactJS App to AWS EC2 with Docker, NGINX, and Automate with GitHub Actions.&lt;/a&gt;. This guide is a continuation of the series on React deployment, focusing solely on Next.js-specific updates. The basic steps remain the same, and you can refer to the previous article for more details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting Up the &lt;strong&gt;Project&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Dockerizing the NextJS application&lt;/li&gt;
&lt;li&gt;Setting up &lt;strong&gt;NGINX&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Automating deployment with &lt;strong&gt;GitHub **Actions on **AWS EC2&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that you can find the link of final source code in the &lt;a href="https://github.com/kilamaelie/Deploy-a-Next.js-App-on-AWS-EC2-with-Docker-NGINX-and-Automate-with-GitHub-Actions%20repository" rel="noopener noreferrer"&gt;https://github.com/kilamaelie/Deploy-a-Next.js-App-on-AWS-EC2-with-Docker-NGINX-and-Automate-with-GitHub-Actions repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 1. Setting Up the Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open your terminal and navigate to the directory where you want to create the project, for the purpose of this tutorial we’ll create a directory with a name &lt;code&gt;next-tutorial-app&lt;/code&gt; or you can name it anything depending on your project name. This folder is considered as the &lt;code&gt;root&lt;/code&gt; directory for our application which contains all folders and files structures.&lt;/p&gt;

&lt;p&gt;Then, we will change our directory into the &lt;code&gt;next-tutorial-app&lt;/code&gt; folder and all other parts of configuration files or folders will be in this folder.&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;next-tutorial-app
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;next-tutorial-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside the &lt;code&gt;next-tutorial-app&lt;/code&gt; folder, to create a new Next project runs the following command using &lt;strong&gt;create-next-app@latest&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest &lt;span class="c"&gt;# website is our app-name or folder name &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let’s change the directory into &lt;code&gt;website&lt;/code&gt; as our app-name for NextJs application and start development server.&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;cd &lt;/span&gt;website &lt;span class="c"&gt;# app-name&lt;/span&gt;
npm run dev &lt;span class="c"&gt;# start the development server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;*&lt;em&gt;Step 2: Dockerizing the Next.js App&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create a Dockerfile in the root directory of your website folder:&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:23.10.0-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="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn

&lt;span class="c"&gt;# Copy all files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Expose the listening port&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, create a .dockerignore file to exclude unnecessary files:&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
.next
.DS_Store
dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In your project’s root directory as &lt;code&gt;next-tutorial-app&lt;/code&gt;, create a file named &lt;code&gt;docker-compose.yml&lt;/code&gt;. This file will contain the configuration for your multi-container application.&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="pi"&gt;:&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;./website&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&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn 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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So to test it, just go in your terminal, navigate to the directory containing the &lt;code&gt;docker-compose.yml&lt;/code&gt; file and run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Kill the task in the terminal where &lt;code&gt;docker-compose&lt;/code&gt; up is running in order to stop the. To remove the containers and their associated resources, run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Step 3: Setting Up NGINX as a Reverse Proxy in Docker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the root of your project directory, create a &lt;code&gt;Nginx&lt;/code&gt; directory named nginx then create a configuration file named &lt;code&gt;nginx.conf&lt;/code&gt; . So inside &lt;code&gt;nginx.conf&lt;/code&gt; file adds the following configuration:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
     &lt;span class="c1"&gt;#server_name can add your domain or Ip address for your server in the production&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://website:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;'upgrade'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&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;in the same directory as your Nginx, create a &lt;code&gt;Dockerfile&lt;/code&gt; which defines how to build a custom Nginx Docker Image:&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="c"&gt;# Use an official Nginx image as the base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:latest&lt;/span&gt;

&lt;span class="c"&gt;# Remove any existing config files&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; /etc/nginx/conf.d/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="c"&gt;# Copy the custom Nginx configuration&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; nginx.conf /etc/nginx/conf.d/&lt;/span&gt;
&lt;span class="c"&gt;# Expose port 80 for Nginx&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Before testing in development, let’s update our &lt;code&gt;docker-compose&lt;/code&gt; file by adding the &lt;code&gt;Nginx service&lt;/code&gt;&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="pi"&gt;:&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;./website&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&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;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&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;./nginx&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&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&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;website&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Also update the &lt;code&gt;Dockerfile&lt;/code&gt; for the next app in the website directory&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="c"&gt;# Stage 1: Install Dependencies&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NODE=node:20-alpine &lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;${NODE}&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;deps&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; libc6-compat

&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="c"&gt;# Stage 2: Build&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;${NODE}&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;builder&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; --from=deps /app/node_modules ./node_modules&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;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# Stage 3: Production Runner&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;${NODE}&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;runner&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;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PORT 3000&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NEXT_TELEMETRY_DISABLED 1&lt;/span&gt;

&lt;span class="c"&gt;# Install PM2 globally&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;-g&lt;/span&gt; pm2

&lt;span class="c"&gt;# Create a non-root user&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 nodegroup
&lt;span class="k"&gt;RUN &lt;/span&gt;adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 appuser

&lt;span class="c"&gt;# Copy necessary files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/public ./public&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/package.json ./package.json&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=appuser:nodegroup /app/.next/standalone ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=appuser:nodegroup /app/.next/static ./.next/static&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

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

&lt;span class="c"&gt;# Start the application using PM2&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["pm2-runtime", "node", "--", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Before testing, ensure that output: &lt;code&gt;'standalone'&lt;/code&gt; is set in the &lt;code&gt;next.config.js&lt;/code&gt; file to optimize the build for Docker deployment.&lt;/p&gt;

&lt;p&gt;To test if everything works, open a terminal, navigate to your root directory, and run the following command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up  &lt;span class="c"&gt;#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then let’s testing at&lt;code&gt;http://localhost:80&lt;/code&gt; or &lt;code&gt;http://127.0.0.1:80&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Kill the task in the terminal&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, let’s create &lt;code&gt;docker-compose.ci&lt;/code&gt; and &lt;code&gt;docker-compose.prod&lt;/code&gt; files for the production and workflow purpose in the root directory of the project&lt;/p&gt;

&lt;p&gt;Docker-compose.ci&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="pi"&gt;:&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;website&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;./website&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&lt;/span&gt;
      &lt;span class="na"&gt;cache_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${NEXT_APP_WEBSITE_IMAGE}'&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${NEXT_APP_WEBSITE_IMAGE}'&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./.env&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3000:3000'&lt;/span&gt;

  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&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;nginx&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;./nginx&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&lt;/span&gt;
      &lt;span class="na"&gt;cache_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${NEXT_APP_NGINX_IMAGE}'&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${NEXT_APP_NGINX_IMAGE}'&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;80: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="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;website&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Docker-compose.prod&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="pi"&gt;:&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;website&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${NEXT_APP_WEBSITE_IMAGE}'&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;.env&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3000:3000'&lt;/span&gt;

  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&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;nginx&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${NEXT_APP_NGINX_IMAGE}'&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;80:80'&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;website&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So for this tutorial will use Github packages for hosting the images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Automating Deployment with GitHub Actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a GitHub Actions workflow file at &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; in the root of the project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Please make sure your on &lt;code&gt;main branch&lt;/code&gt; because the workflow’s can only trigger on the main branch other you can it to your preferences.&lt;/p&gt;

&lt;p&gt;Workflow successfully finished :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mfuosio8ujuaqkyset1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mfuosio8ujuaqkyset1.webp" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkzq4l09utjnmp0m6c8s.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkzq4l09utjnmp0m6c8s.webp" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚀 👏💻 By following these steps, you have successfully deployed a Next.js application on AWS EC2 using Docker and NGINX, with automated deployments using GitHub Actions. You can further enhance this setup by integrating SSL certificates with Let’s Encrypt and using AWS services like RDS or S3 for storage.&lt;/p&gt;

&lt;p&gt;If you would like to improve your skills, click on the following link on Master React App Deployment: &lt;a href="https://www.patreon.com/kilamaelie/shop/master-react-app-deployment-aws-ec2-53-1089307?utm_medium=clipboard_copy&amp;amp;utm_source=copyLink&amp;amp;utm_campaign=productshare_creator&amp;amp;utm_content=join_link" rel="noopener noreferrer"&gt;AWS EC2, Route 53, SSL &amp;amp; GitHub Actions,A comprehensive manual which walks you through the deployment process, leveraging the power of Docker, AWS EC2, Route 53, SSL encryption and GitHub actions to build a robust CI/CD pipeline.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep exploring, stay curious and keep building on this foundation to create even more sophisticated and powerful projects. Happy coding!&lt;/p&gt;

&lt;p&gt;Thanks for reading through and I hope you liked what you read here. Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/kilamaelie/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://x.com/kilamaelie" rel="noopener noreferrer"&gt;twitter&lt;/a&gt; and &lt;a href="https://github.com/kilamaelie" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>docker</category>
      <category>nginx</category>
      <category>aws</category>
    </item>
    <item>
      <title>Build and Deploy a ReactJS App to AWS EC2 with Docker, NGINX, and Automate with GitHub Actions.</title>
      <dc:creator>Kilama Elie</dc:creator>
      <pubDate>Fri, 05 Jan 2024 20:40:00 +0000</pubDate>
      <link>https://dev.to/kilamaelie/build-and-deploy-a-reactjs-app-to-aws-ec2-with-docker-nginx-and-automate-with-github-actions-4bk9</link>
      <guid>https://dev.to/kilamaelie/build-and-deploy-a-reactjs-app-to-aws-ec2-with-docker-nginx-and-automate-with-github-actions-4bk9</guid>
      <description>&lt;p&gt;In today’s digital landscape, master the art of building, deploying and automating ReactJS Apps using Docker, NGINX and GitHub actions on AWS EC2. Dive into this comprehensive guide and transform your coding journey into an adventure of innovation, efficiency and cutting-edge technologies.&lt;/p&gt;

&lt;p&gt;Table of Contents&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Setting Up the Project&lt;/li&gt;
&lt;li&gt;Dockerizing the ReactJS application&lt;/li&gt;
&lt;li&gt;Setting up NGINX&lt;/li&gt;
&lt;li&gt;Automating deployment with GitHub Actions on AWS EC2&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;1. Prerequisites&lt;/strong&gt;&lt;br&gt;
Before diving into this project, which involves several technologies and concepts, you need a solid grounding in the following areas to ensure successful learning and implementation such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Web development&lt;/strong&gt;: A good understanding of HTML, CSS and JavaScript is essential, as ReactJS is a JavaScript library for building user interfaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git and version control&lt;/strong&gt;: A good knowledge of Git and version control concepts will help you manage your code base and collaborate effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt;: Understanding containerization for docker is crucial to packaging your application and its dependencies in isolated containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NGINX&lt;/strong&gt;: A basic knowledge of NGINX as a web server and reverse proxy will be necessary to set up the server environment.&lt;/p&gt;

&lt;p&gt;Remember, while these requirements are essentials, you don’t need to be an expert in every area. You can learn and develop your expertise as you go along. What’s more, official documentation, online tutorials and communities can provide invaluable support as you learn.&lt;/p&gt;

&lt;p&gt;Note that you can find the link of final source code in the build and &lt;a href="https://github.com/kilamaelie/build-and-deploy-reactApp-to-aws-ec2-via-github-actions" rel="noopener noreferrer"&gt;deploy-reactApp to-aws ec2 via github-actions repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Setting Up the Project&lt;/strong&gt;&lt;br&gt;
This involves several steps which including the installation, project structure and other basic configuration, here’s a step-by-step guide helping you get started :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install Node.js and npm&lt;/strong&gt;:&lt;br&gt;
Before you begin, make sure Node.js and npm ( Node Package Managers) are installed on your system. You can download them from the official website: &lt;br&gt;
&lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;https://nodejs.org&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a New Project&lt;/strong&gt;:&lt;br&gt;
Open your terminal and navigate to the directory where you want to create the project, for the purpose of this tutorial we’ll create a directory with a name &lt;code&gt;react-tutorial-app&lt;/code&gt; or you can name it anything depending on your project name. This folder is considered as the root directory for our application which contains all folders and files structures.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;In software development, “project structure” provides a clear and logical layout that helps developers understand and navigate the codebase more easily. A well-designed project structure promotes maintainability, collaboration, and efficient development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, we will change our directory into the &lt;code&gt;react-tutorial-app&lt;/code&gt;folder and all other parts of configuration files or folders will be in this folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; mkdir react-tutorial-app
&amp;gt; cd react-tutorial-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside the react-tutorial-app folder, to create a new React project runs the following command using &lt;code&gt;create-react-app&lt;/code&gt;and for more details on how create-react-app works and other staff, please visit the official website: &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;https://create-react-app.dev/&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app your-app-name # website is our app-name or folder name 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;create-react-app&lt;/code&gt; is a command-line tool that allows you to quickly and easily set up a new &lt;strong&gt;Reactjs&lt;/strong&gt; Application, this boilerplate setup generates a basic project structure, including essential files and configuration needed to start building a React application .&lt;/p&gt;

&lt;p&gt;Let’s change the directory into &lt;code&gt;website&lt;/code&gt; as our app-name for React application.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd website # app-name 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To start the development server and preview your React app, use the following command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;This command will initiate the development server, which will compile your React code, bundle assets, and start a local web server to serve your application. It will also open your app in your default web browser automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the development server is up and running, you can access your React app by visiting &lt;code&gt;http://localhost:3000&lt;/code&gt; in your web browser. So well done 👏 for creating a React application and setting up the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Dockerizing the ReactJS application&lt;/strong&gt;&lt;br&gt;
Docker provides a standardized way to package and distribute applications, making it easier to deploy and manage across different environments. In this section, we’ll containerize our ReactJS application using Docker.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you haven’t already, install Docker on your machine by following the instructions for your operating system: &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;https://docs.docker.com/get-docker/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your React app’s root directory, create a file named &lt;code&gt;Dockerfile&lt;/code&gt;. This file defines the instructions to build your Docker image.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use an official Node.js runtime as the base image

FROM node:19.6.0-alpine
# Set the working directory within the container
WORKDIR /website

# Copy package.json and package-lock.json to the container
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code to the container
COPY . .

# Build the React app
EXPOSE 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since in this project will have more than one container , then we ‘ll need to use &lt;code&gt;docker-compose&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker-compose&lt;/strong&gt; : allows you to define and run multiple Docker containers as a single service, making it easier to manage complex applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you haven’t already, you’ll need to install Docker Compose. Follow the installation instructions for your operating system: &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;https://docs.docker.com/compose/install/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your project’s root directory as &lt;code&gt;react-tutorial-app&lt;/code&gt;, create a file named &lt;code&gt;docker-compose.yml&lt;/code&gt;. This file will contain the configuration for your multi-container application.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
  website:
    build:
      context: ./website
      dockerfile: Dockerfile
    command: npm start
    ports:
      - "3000:3000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This defines only one service website but a bit later more will be added. So in order to test it, just go in your terminal, navigate to the directory containing the &lt;code&gt;docker-compose.yml&lt;/code&gt; file and run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Docker Compose will build the required images and start the defined containers. You’ll see logs from both services in the terminal and also depending on your configuration, you can access your ReactJS app at &lt;code&gt;http://localhost:3000&lt;/code&gt; . Kill the task in the terminal where &lt;code&gt;docker-compose up&lt;/code&gt;is running in order to stop the. To remove the containers and their associated resources, run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose rm -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;4. Setting up NGINX&lt;/strong&gt;&lt;br&gt;
NGINX is a reverse proxy to serve the built static files of your React application. This setup is commonly used to improve performance and manage routing in production environments.&lt;/p&gt;

&lt;p&gt;In the root of your project directory, create a Nginx directory named nginx then create a configuration file named &lt;code&gt;nginx.conf&lt;/code&gt; . So inside &lt;strong&gt;nginx.conf&lt;/strong&gt; file adds the following configuration:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
     listen 80;
     server_name localhost; 
     #server_name can add your domain or Ip address for your server in the production

     location / {
         root /usr/share/nginx/html/website;
         try_files $uri $uri/ /index.html;
     }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the same directory as your Nginx, create a &lt;code&gt;Dockerfile&lt;/code&gt; which defines how to build a custom Nginx Docker Image:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use an official Nginx image as the base image
FROM nginx:latest

# Remove any existing config files
RUN rm /etc/nginx/conf.d/*

# Copy the custom Nginx configuration
COPY nginx.conf /etc/nginx/conf.d/

# Expose port 80 for Nginx
EXPOSE 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Before testing in development, let’s update our &lt;code&gt;docker-compose&lt;/code&gt;file by adding the Nginx service&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
website:
  build:
    context: ./website
    dockerfile: Dockerfile
  volumes:
   - website:/website/build/  # build files will be added at this directory !!!

nginx:
  build:
   context: ./nginx
   dockerfile: Dockerfile
  ports:
    - "80:80"
  volumes:
     - website:/usr/share/nginx/html/website # Copy React App's build files to the Nginx directory

volumes:
   website:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And also update the &lt;code&gt;Dockerfile&lt;/code&gt; for the react app in the website directory&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use an official Node.js runtime as the base image

FROM node:19.6.0-alpine
# Set the working directory within the container
WORKDIR /website

# Copy package.json and package-lock.json to the container
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code to the container
COPY . .

# Build the React app
RUN npm run build

# Build the React app
EXPOSE 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For the purpose of testing if everything works, we’ll open a terminal and navigate to your &lt;code&gt;root&lt;/code&gt; directory. Run the following command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up  # 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then let’s testing at &lt;a href="http://localhost:80" rel="noopener noreferrer"&gt;http://localhost:80&lt;/a&gt; or &lt;a href="http://127.0.0.1:80" rel="noopener noreferrer"&gt;http://127.0.0.1:80&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kill the task in the terminal where the container is running. To remove the container and its associated resources, run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose rm -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, let’s create &lt;code&gt;docker-compose.ci&lt;/code&gt; and &lt;code&gt;docker-compose.prod&lt;/code&gt;files for the production and workflow purpose in the root directory of the project&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Docker-compose.ci&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
  website:
    build:
      context: ./website
      dockerfile: Dockerfile
    cache_from:
        - '${WEBSITE_IMAGE}'
    image: '${WEBSITE_IMAGE}'
    ports:
      - "3000:3000"
    volumes:
      - website:./website/build/  # build files will be added at this directory !!!

  nginx:
    build:
     context: ./nginx
     dockerfile: Dockerfile
    cache_from:
        - '${NGINX_IMAGE}'
    image: '${NGINX_IMAGE}'
    ports:
      - "80:80"
    volumes:
      - website:/usr/share/nginx/html/website # Copy React App's build files to the Nginx directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Docker-compose.prod&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
  website:
    container_name: 'website'
    image: '${WEBSITE_IMAGE}'
    ports:
      - "3000:3000"
    volumes:
      - website:./website/build/  # build files will be added at this directory !!!

  nginx:
    container_name: 'nginx'
    image: '${NGINX_IMAGE}'
    ports:
      - "80:80"
    volumes:
      - website:/usr/share/nginx/html/website # Copy React App's build files to the Nginx directory

volumes:
  website:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So for this tutorial will use Github packages for hosting the images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/features/packages" rel="noopener noreferrer"&gt;GitHub Package Registry&lt;/a&gt; is a package hosting service provided by GitHub. It allows you to publish, share, and manage software packages directly within your GitHub repositories.&lt;/p&gt;

&lt;p&gt;It lets you host your software, either publicly or privately, for use in your projects on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s start by setting up an EC2 instance to deploy our application. To do this, and you’ll need to open an &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt; account (if you don’t already have one).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don’t know how to set up AWS EC2 , please visit this link which will guide you through how to :&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/get-set-up-for-amazon-ec2.html" rel="noopener noreferrer"&gt; launch EC2&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once we’ve finished configuring AWS EC2 and the instance is up and running, we can install Docker on it.&lt;/p&gt;

&lt;p&gt;So connect via SSH into the instance using your Key Pair as :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh -i your-key-pair.pem ec2-user@&amp;lt;PUBLIC-IP-ADDRESS&amp;gt;

#example 
# ssh -i ~./ssh/react-tutorial.perm ec2-user@54.201.189.94

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After accessing the instance, start by updating the server package and installing the latest version of &lt;code&gt;Docker&lt;/code&gt; and &lt;code&gt;Docker-compose&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ec2-user]$ sudo yum update -y
[ec2-user]$ sudo yum install -y docker
[ec2-user]$ sudo service docker start
[ec2-user]$ sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
[ec2-user]$ sudo chmod +x /usr/local/bin/docker-compose

[ec2-user]$ docker --version
Docker version 20.10.23, build 7155243

[ec2-user]$ docker-compose --version
Docker Compose version v2.18.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Add the &lt;code&gt;ec2-user&lt;/code&gt; to the &lt;code&gt;docker group&lt;/code&gt; so that you can use it without running the Docker command with sudo :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ec2-user]$ sudo usermod -a -G docker ec2-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, let’s generate the SSH KEY&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ec2-user]$ ssh-keygen -t rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Please! save the Key without setting any password&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, copy the public key into the &lt;code&gt;authorized_keys&lt;/code&gt; file and set the appropriate authorizations.:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ec2-user]$ cat ~/.ssh/id_rsa.pub 
# Copy this public key into ~/.ssh/authorized_keys
[ec2-user]$ vi ~/.ssh/authorized_keys
# After adding the public key into authorized_key then exits  from vi text editor 
# Then add these commands are used to change the permissions of these files 
[ec2-user]$ chmod 600 ~/.ssh/authorized_keys
[ec2-user]$ chmod 600 ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, copy the contents of the private key&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ec2-user]$ cat ~/.ssh/id_rsa

# Copy the private key somewhere for later use
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;*&lt;em&gt;5. Automating Deployment with GitHub Actions to AWS EC2&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is an integrated continuous integration and continuous deployment (CI / CD) platform provided by GitHub .&lt;/p&gt;

&lt;p&gt;Since our workflow will use GitHub’s core features (GitHub packages), GitHub requires you to create an access token, which will be used as a GitHub user ID for all your interactions.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;Personal access tokens&lt;/a&gt; area in the Developer settings of your GitHub profile and click Generate new token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffl2gk7liidfoc88y5ik2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffl2gk7liidfoc88y5ik2.webp" alt="personal access tokens" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To configure&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt; GitHub Actions&lt;/a&gt;, start by adding a new directory called &lt;code&gt;.github&lt;/code&gt; to the root of your project, then within this directory, add another directory called &lt;code&gt;workflows&lt;/code&gt; . Now to configure the workflow, which is a set of one or more jobs, create a new file in the workflows directory called &lt;code&gt;main.yml&lt;/code&gt; . You can also run the following command in your root directory.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir .github &amp;amp;&amp;amp; cd .github
mkdir workflows &amp;amp;&amp;amp; cd workflows
touch main.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;main.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;So, in the &lt;code&gt;main.yml&lt;/code&gt; we define :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;set the environment variables&lt;/li&gt;
&lt;li&gt;Three jobs to run:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a. build job consist of:&lt;/p&gt;

&lt;p&gt;Set various environment variables&lt;br&gt;
Login in the Github package by using the PERSONAL_ACCESS_TOKEN created&lt;br&gt;
Pull images for caching, build the images and push to Github Registry&lt;/p&gt;

&lt;p&gt;b. checking-secret consist of :&lt;/p&gt;

&lt;p&gt;checking if the deploy variables are existing&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Notes about the secrets&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;secrets.PERSONAL_ACCESS_TOKEN&lt;/li&gt;
&lt;li&gt;secrets.NAMESPACE&lt;/li&gt;
&lt;li&gt;secrets.PRIVATE_KEY&lt;/li&gt;
&lt;li&gt;secret.AWS_EC2_IP_ADDRESS&lt;/li&gt;
&lt;li&gt;secrets.AWS_HOST_USER&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the secrets need to be set in your repository’s &lt;a href="https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-encrypted-secrets" rel="noopener noreferrer"&gt;secrets &lt;/a&gt;(Settings &amp;gt; Secrets) and use your Github username or your organization name as your NAMESPACE and your personal access token as PERSONAL_ACCESS_TOKEN .&lt;/p&gt;

&lt;p&gt;c. deploy job consist of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the environment variable to .env file which will be created on the AWS EC2 instance.&lt;/li&gt;
&lt;li&gt;Add the private key to ssh-agent for password less.&lt;/li&gt;
&lt;li&gt;Deploy the build the images on AWS EC2 instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you are done, commit and push your code to the Github to trigger the workflow to run, so now you should see the images in the Github Packages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Please make sure your on main branch because the workflow’s can only trigger on the main branch other you can it to your preferences.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Workflow successfully finished :&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffl2gk7liidfoc88y5ik2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffl2gk7liidfoc88y5ik2.webp" alt="workflow" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once all our jobs have been executed, navigate to the IP of your instance, and you should see the React application running :&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmul4hggv9r1a8v34bq0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmul4hggv9r1a8v34bq0.webp" alt="react-app" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you would like to improve your skills, click on the following link on Master React App Deployment: &lt;a href="https://www.patreon.com/kilamaelie/shop/master-react-app-deployment-aws-ec2-53-1089307?utm_medium=clipboard_copy&amp;amp;utm_source=copyLink&amp;amp;utm_campaign=productshare_creator&amp;amp;utm_content=join_link" rel="noopener noreferrer"&gt;AWS EC2, Route 53, SSL &amp;amp; GitHub Actions,A comprehensive manual which walks you through the deployment process, leveraging the power of Docker, AWS EC2, Route 53, SSL encryption and GitHub actions to build a robust CI/CD pipeline.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👏 And there you have it, this guide has equipped you with the skills you need to successfully build, deploy and automate ReactJS applications using Docker, NGINX and GitHub Actions on AWS EC2. Keep exploring, stay curious and keep building on this foundation to create even more sophisticated and powerful projects. Happy coding!&lt;/p&gt;

&lt;p&gt;Thanks for reading through and I hope you liked what you read here. Feel free to connect with me on&lt;a href="https://www.linkedin.com/in/kilamaelie/" rel="noopener noreferrer"&gt; LinkedIn&lt;/a&gt;, &lt;a href="https://twitter.com/kilamaelie" rel="noopener noreferrer"&gt;twitter&lt;/a&gt; and &lt;a href="https://github.com/kilamaelie" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
