Taking a web application from local development to a live secure production environment can be daunting if you've never looked under the hood. In this comprehensive step-by-step course, you will learn to manually configure an Ubuntu server, set up essential runtimes and search services, and establish automated CI/CD pipelines. By doing everything hands-on before introducing automation, you will gain a deep foundational understanding of full stack deployment and how to reliably host complex systems. Immad Sadi developed this course. Hi everyone, I hope you are all doing great. In this course, we are going to talk about full stack deployment. This is a long course but trust me if you stick around and follow each chapter slowly you will learn a lot of things because I try to do things manually and to go step by step even if you haven't tried anything and the problem that here that I'm trying to solve and that I will try to teach you is how to take a web application that you have developed locally and deploy it. I have done this exercise three times for websites that I developed for myself and that I deployed for users to use and I will try to teach you all the things that I have learned across different domains. So let's look at the slides and let's see what we will learn. Let's define the problem in detail. First you have your web application. That's the prerequisite for watching this course. You can develop it using Python, Vue.js JS or other web frameworks and you want this application to be used. Here we have users so they they need to access the web application but they can't because you are running it locally on your laptop so they can't access it. What do you need to do? You need to take your source code and deploy it somewhere. So this is what we call the production environment. Here you rent a server where you can launch your application code. You can use platforms like Digital Ocean, Cloudflare, Lenode or others. There are plenty of platforms that we can use. And now your users can access the application that you have deployed and they are going to be happy. This is the problem. How do we do this? We have the web application. So we need a few steps to follow in order to deploy the web application and this is exactly the problem that we are going to solve in this course. What is the solution? In our case, we are going to rent as I said a server from let's say digital ocean, but you can do the same thing with other platforms. And this server can be either virtual or physical. We are going to explain this later. And some of these managed platforms simplify the process for us. So for example, you can have your code on GitHub and you just connect it to this to one of these platforms and they will deploy the code for you. You have platforms like Netlifi, Cloudflare, Lenode as I said and others. But the problem if you are a beginner and go directly to one of these managed platforms is that you won't understand exactly what is happening under the hood because they have a layer of abstraction that removes all the burden and all of the things that you need to configure manually. And this is great for people that have the experience with Linux and have deployed things manually across the years. But if you are be a beginner, I will highly suggest to not start with these platforms and try to do things manually to understand what is happening because transitioning from a manual process to using one of these platforms is very easy. So we are going to look behind the scenes here. As as you can see from the slide, I am banning these platforms because we are going to rent a server, a brand new server. We are going to install the operating system on it. In this case, we are going to use Abuntu and I will show you how to install the required software. So, we can install Python, NodeJS and other software that we need to run the application. And we will focus also on making sure that the web server is secure and that no one can access it. And we will see other things as well in this course. And here I want to mention or I want to emphasize this core philosophy that we will follow during the course is that before we automate anything we have to do the process manually because that will teach us why we need automation because if you don't do that you won't see the benefit of automating processes and also I have developed a book that goes uh with this course is uh not something the theoretical as I said I have applied this three times s on three websites that I have deployed and I am trying to give you all that knowledge in a distilled format. So I used a lot of resources like Google, I read books, I also found some code snippets and I filtered all of that into one simp one course that you can follow with all the things that I have learned and also the book if you want to read it. And as I have said, I have worked on this course for more than 8 months now to try to give you something that you might find helpful and to go in to see everything manually and to learn about everything that happens behind the scenes. And in this course, I will focus on deploying a specific web application that I have used and that you can find the source code online on GitHub that uses fast API in the back end and Vue.js JS in the front end and as an extra layer we added a search engine using M search but this is just an example to show you that you can deploy a complex system but if you are using Ruby or other programming languages the process will be the same you just need to change the software that you need to install and the commands to run it. This course is divided into six modules. In the first one which I call the foundation we are going to as I said in this case we are going to use digital ocean but you can do the same thing with the other platforms. We are going to rent a server. I will teach you what is an SSH key. This is something that we are going to use in order to authenticate to uh the machine that we are going to rent. And here something very important. This is for security reasons. We are going to remove the ability to connect using a password. Instead, we will only allow using an SSH key. And we will also block users from connecting as the root user because if someone is able to use that, they might do something nefarious in your server and we don't want to allow that. Instead, we are going to create a new user that you can use in order to connect to the virtual machine. And on top of that, we are going to configure firewall to block anyone who is trying to access your your web server. Why is that? Because once you once you create a web server, it is going to be available online and anyone can see it. So we need to set up the firewall and block people that are trying to access your web server. In this second module which is called the application runtime, here we are going to work with our code. So we are going to take it from let's say GitHub or from our laptop and we are going to deploy it to the server. So we are going to copy it and paste it there. Because we are using Python, we need to install we need to install it and configure the virtual environment. And something very important here. We are going to start with a small server. It's not it is not going to have a lot of RAM. So we will end up having some problems with memory. So we will eventually when we try to install software that needs compilation that will that will make the uh Linux run out of memory. So in that case we are going to learn something new which is swap memory which will allow us to take some some space from the SSD and help to in order to help us to install some some software that requires a lot of RAM. And in order to manage the back end we are going to use a mix of these software. We are going to use supervisor, J unicorn and UV core. Don't worry about that. We are going to learn why do we need them in module 2. So I'm just showing you what the things that we are going to learn. And after that we are going to use engineext as a reverse proxy and we are going to connect it to the back end using Unix sockets. In the third module, we are going to focus on media search because this is the data that we that the web application needs. I will show you how to export and dump the data that we have locally so that you can take it from your local machine and put it on the server. We are going to self-host mail search. We are not going to use a cloud offering that will give us the ability to get me search. No, we are going to install it and we are going to manage it on the same server. And here we are going to create another user that will manage me search for us. And we are going to use systemd to create a background service that will launch me search for us and that we can use in order to manage me search. After that we are going to use or we are going to create a batch script that will automatically create backups for us so that if something bad happens we can just read the backup and load it in order to continue. And finally, we are going to I will show you a nice dashboard that you can use in order to manage the data you have on me search. And we are going to use something very awesome which is called SSH tunneling that will allow us to connect this dashboard to your server without having to open a port or something like that. In module four, we are going to talk about global delivery and app security. And here we are going to start by registering a domain and configuring DNS. After that we are going we need a certificate so that we can get HTTPS that will make uh the connection between your users and your web server secure because if you don't do that you they will only see HTTP and their browser will flag that connection as insecure. And with let's encrypt the certificates are only valid for 3 months. So here I will show you how to create a script that will automatically renew the certificate for you without having to run any commands. After that we are going to use cloud player to serve our front- end assets because it is static and that will help us reduce the latency across uh the world because when when you pick a server you pick a location in in my example I picked Germany. So that means people from China or other countries that are very far away they are going to see high latency. In order to fix this problem, we use CDNs and in this case we are going to use cloud player. In module 5, we will focus a lot on automation. Here we are going to connect to GitHub because up to now we have cloned the project using a command. So we copied the code and we pasted it on the virtual machine and we ran the commands to start the back end and front end servers etc. We did everything manually. But in this module, we are going to automate that. So we will push the code to GitHub and we will just hit a button to deploy the web application without having to go to the server. So this is what we are going to learn here. But first we need to learn how to protect a branch. So if you are working on a codebase, you have let's say your main or master branch, you should never push the code directly to it. You need to create a new branch and then merge the code. So I will show you how to protect that main branch so that you cannot so that you don't push code that is broken accidentally. After that we are going to use a project called pre-commit that will allow us to create some rules that we can run when we commit code. For example, one of the rules that we can add is checking linting and formatting so that if your code is not formatted well, you will not be able to commit. After that, we are going to create a pipeline using GitHub actions. And that will allow us to run the same um tests like linting, formatting, running unit tests, etc., but on GitHub. And that will allow us to block a merge request or a pull request if it doesn't satisfy all these tests. And that's how we can detect if our code is broken or not. If it isn't, if everything is green, then we can merge to master and from that we can deploy and we will do other things. Uh we will add integration tests, end to end tests, etc. So don't worry about that. We will focus a lot. Uh this this model is going to be very long, but it is going to be very very useful. And finally in the last module, we will focus on optimization and maintenance. Here I will show you how to read the enginex logs and display that data nicely using a project called go access. After that I will show you how to monitor the memory usage using btop++. This is an amazing project. I really like it and you will see once we once you reach module 6 you will see how btop can help us understand what is happening on our web server and I will show you how to ban IP addresses that are hammering your website. So sometimes when you analyze the logs using go access you will see some IP addresses that are trying to reach your server thousand um more than a thousand times a day or a minute and that's that is not human. So a human will not will not go to your website and try to refresh the page a thousand times. That's impossible. So these are automated bots that are trying to steal credentials or to see if there are weaknesses in your web server. So we are going to block them in order to first not waste time serving them things that they they are not going to read and to yeah reduce the the burden on our server. And we will also use an amazing tool called locust. This is a Python library that allows us to simulate users trying to access our website because this will allow us to estimate how many requests per second or per minute we the server can sustain before crashing or not being able to serve as many users as possible. So this is going to allow us to understand the weaknesses of our server and we will do other things. is we will analyze the disk usage um perform some log retention policy changes etc. So as you can see this course is packed with information. So it is going to be more than 10 hours long. So go through it step by step and don't try to rush. I I have tried to make it accessible to anyone. So even if you are a beginner don't worry about that because this is why I have made the course in order to teach you how to deploy things. And at the end you will see that you can also go automate uh more stuff but I just try to pick your interest and show you what you can do. And after that as I said if you don't want to do this process manually at least you did it once. So now you understand after that you can use platforms like Netifi get GitHub pages to deploy your applications without having to worry about renting a server or something like that. And in terms of resources don't worry about that. Everything is open source. I will make sure to put the links in the video description. You have two GitHub repositories. The first one, which is what I'm showing you right now, contains the code that we will deploy and the second GitHub repository contains the content of the book. So if you want to just read and you don't prefer watching videos, you can go to that repository, click on the book folder and you'll find all the chapters right there with the code. Everything everything is there in a nice format. And if you need some help or if you just want to say something after watching the course, feel free to connect with me. You can find my LinkedIn on the video description or you can send me a an email if you want to. You will find all my information in the video description. I hope that I have motivated you enough to watch the course. I hope that you enjoy it and that you learn a lot from it. Now, let's start with the first module. Welcome to module one where we will set up the foundation of the cloud environment. This is where your code will live. In this sub chapter, we will focus on cloud setup and access. Let's get started. Before we deploy any code, we need a secure environment. In this course, I will be using digital ocean. But these concepts apply everywhere. Whether you are using AWS, Lenode, or even a Raspberry Pi at home, the Linux commands remain the same. By the end of this video, you will have a server running Ubuntu 2404 LTS. We will generate secure SSH keys, connect to the server, and most importantly, we will completely disable password authentication. This ensures that your server is locked down from the very first moment. Now let's go to digital ocean in order to create our first droplet. Before creating the droplets, I want you to know that we have a GitHub repository where you will find everything I'm sharing with you in this course. So for example here inside the book folder we have the first subchapter and you have all the steps here. So if you prefer reading articles you will find those inside the book folder and the slides and everything else are available in the repository as well. You will find the link in the video description. If you have never used Digital Ocean before, if you click on the link in the video description, you will be able to get $200 for free just to test their platform. And that amount is available for 60 days. Click on get started. That will take you to a login page where you can create your account. In my case, it it logs me directly because I already have an account. And here we need to click on projects. As you can see, I already have two projects. I have my personal website is hosted here. And in your case, this list will be empty. First, click on new project. In this form, give the project a name. Select the environment type and the purpose of this project. I will call the project test. The environment type, let's say development. Here in the purpose drop-down menu, I'll select it web application and the description is optional. So click on create a project. Let me zoom in a little bit. Here they say move resources into test. So here we don't have any resources. So we will skip this for now. And as you can see the project has been created. And now we can create resources. And the resource that we are going to create is a droplet. Droplets are machines that that can run our code in the cloud. This means that we will take our application's source code put it on this droplet that is hosted by digital ocean and the code will be running inside inside of that machine. So now you can click on create droplets or you can do it from here. Create droplets. It's the same thing. Now we are in the create droplets page. The first thing that we need is to choose a region. For example, in my case, I live in Morocco. So I will choose Frankfurt because Germany is close to Morocco. That's why I chose it. you can do the same thing. So for example, if you live in Europe, you can select either London or Frankfurt or also Amsterdam. Those three will work perfectly. But if you are trying to deploy this application next to users in another country, then try to choose the the region based on where your users are going to be uh located. And here for example in Frankfurt, we have just one data center. So there there are there aren't other options. Next, choose the operating system. Here we have a lot of options, but I will select Ubuntu. And here we have plenty of versions. I will choose the LTS version, and I will select the latest one. Now you need to choose the size of your virtual machine. You can pick between shared CPU and dedicated CPU. Inside each option, you must decide the type of of storage, how much memory you want, uh how many CPU cores you want to have in this machine, etc. And here if you select dedicated GPU CPU, uh you will see that the price increases drastically because inside this option, the resources are reserved only for you. But shared CPU, you will be sharing resources with other people. That's why it's not as expensive as dedicated CPU. Just for testing purposes, select choose shared CPU, regular, and then select the lowest tier, $4 a month. And if you have just a simple web application that you want to host, then this option is fine. uh for example I have de deployed many websites using just this plan and it works wonderfully if you see that the application can't handle this the high traffic that you are seeing after that it's possible to to to get more resources so this doesn't if you select this now it doesn't mean that you are stuck in this offer and you'll have to recreate everything from the beginning no you you can still uh upsize the the droplet and add more resources If you need next, so here we have additional storage. You can see that here we get 10 GB of storage. If you think that this is not enough, you can click on add volume and select the amount that you want to add. But this is not free. So you will be paying $1 per month per 10 GB. In my case, I don't need this, so I'll just remove it. 10 GB is enough. Uh if you want to have backups, you can click on this. And again, this is not free. So uh if you need this enable it and choose the right options but in any I don't think this is needed for testing purposes. So uncheck it. And now we arrive at the authentication section. So here we have two options. We can use SSH keys which is as you can see the default option selected by Digital Ocean or if you want you can use passwords. But as you can see here, we have a warning that says please store your password securely because if because passwords can be cast if they are not strong, but SSH keys are much stronger. So let's use SSH keys and I'll show you how to create one uh next. So now we need to create an SSH key. So I already have one here, but I will create one by clicking on this new SSH key button. You can see that they give us here the steps that we need to follow. I will show you how to do that. Now one once we generate the SSH key, we will put it here. Give it a name and add it. But before I create this, I want to go back to the slides in order to explain how uh authentication with SSH keys work. Let's look at what actually happens when you try to connect to your droplet. As I said, we will be using SSH keys instead of passwords because they are much harder to steal. The process starts with two files that we call private key and public key. And as you can see, we have two containers. This one represents your machine, your laptop that you have next to you. And here we have the server that we are creating on Digital Ocean or you can create on AWS or other cloud providers. The private key lives on your laptop but the public key will be inside the server. So the private key here think of it as your physical house key. You never share this with anyone. So this needs to be kept secret. Then you give the server a public key. Think of this as the lock on the door. Anyone can see the lock, but because they don't have the key, they cannot open it. When you run the SSH command, your laptop contacts the server and says, "Hello, I would like to connect using this specific ID." When the server receives this request, it replies with a challenge because the server is suspicious. It it doesn't know if you are the real person or if this is a hacker that wants to hack the the server and extract valuable information from it. So the server sends a challenge basically a random string of characters that we call a nons and basically says prove you own that private key. sign this random string for me. So if the the requesttor is legitimate, that means that it has the private key, it will be able to basically sign this string and send it back to the server. So here the the laptop will say or the real person will say I signed this using my private key. Here is the signature. Now the server will receive this and will try to do the math to verify if this is uh if the private key is correct. If that's the case, the server will grant access because it verified that the signature is correct. Otherwise, it's going to block this request and the anyone except you will not be able to log to your machine. So this is how SSH SSH authentication works. And now I'll show you how to create the these two pairs of keys uh the private and public keys. Paste this command. As I told you if you go to the to GitHub you will find this markdown file and here you will have everything. Um, so if I scroll down a little bit, you can see that I'm here we we have the same steps that we followed and here you can see that we had the commands. So don't worry about them. You will find the everything in here so that we can just copy copy them easily without typing them manually. So the SSH key gen command is used to generate SSH keys and we have plenty of algorithms that we can use to generate these pairs of keys and one of those is called ED25519. So this is the one that we are going to use. It's faster and more secure than RSA but you can also use RSA if you want. And here - F is basically the file name. All SSH keys are stored in the SSH directory. And if you don't give it a name, you might risk overriding your keys. So here I will call it test SSH key. And here we can add a comment. This is helpful if you want to identify the keys. And now I can hit enter. And as you can see it says generating public key with this algorithm. And here I need to enter a passphrase for this specific key. And here they tell me if I don't want to add a passphrase, I can just hit enter to move to the next uh step. When should you add a passphrase and when should you ignore this? I strongly recommend adding a passphrase for your personal key. So if you want to log to the machine, type some commands etc. do do some maintenance in that case you will be logged in manually. So in that case it's great to have a passphrase because even if someone was able to get your key they won't be able to login because the server will ask for a passphrase. Basically it's another layer of security that will prevent anyone from using your ser or accessing your server. But if you want to have a a machine or a script that will automatically deploy your application, in that case you don't want to have a passphrase because the script will be blocked. I will show you how to create two keys. So this one that we are creating is going to be used manually but we'll have another one that will call it let's say GitHub or something that we will use in GitHub to deploy our application automatically and in that case we don't need a passphrase. So for now I will add the passphrase and I will show you that we will be asked to enter it when we want to log the droplets. I have entered the passphrase and here they tell me to enter it again. Great. So as you can see the key random image is so that means that the the keys were generated. As you can see I have listed all the keys inside the SSH directory and I search just for this and you can see that we have two files. This one pub means public key and this one is the private key. This one should stay on your laptop but this one we will copy it and put it inside the droplet. Great. So now copy this and we will use the catch command to see the content of this public key. So here make run this and copy everything and after that I will show you where to put that uh on digital ocean. I have copied the key and now I will put it here and I will give it a name. So I will start by entering the name. So test SSH key manual. So here you paste your SSH key and after that click on add SSH key. I have done that and as you can see I have another SSH key here. And now if I want to add another key, I can click on new SSH key and repeat the same steps in the terminal to generate another one. But make sure to have another name and to not enter a passphrase because that one we will use later to deploy the application automatically. Or if you don't want to do this here, I will show you how to do it within the droplet. Here we can select this first option add improved metric monitoring. It's free so why not? And here finally we have this section. So here we can create multiple droplets if you want to but in our case uh one is enough. You can change the host name. So this is the name that will appear inside the droplet. We can we can change that. Let's call it uh let's keep it as Ubuntu test project. If you have multiple droplets, using tags will help you filter them quickly. And after that, we should assign this to the test project that we created. So everything is okay. Now click on create droplet. Open your list of projects and click the project that you have created. As you can see here is the droplet and here this is the running indicator. So this means that this droplet is in the creation phase but once this turns green it means that it's running. You can click on the name and as you can see now it's running. We have no data because we have just created this. You have your IP address. You have everything here. And as you can see, if you want to upsize this later, you can click on the upsize droplets button. And here you can in add more memory, more storage or in uh increase the the number of CPU cores if you want to. So I have refreshed the page and as you can see now it says active instead of uh new. Great. Now I want you to copy this IP address. Just click on the copy button. And you might be wondering why am I blurring the IP address. Even though this is just a test droplets that I will delete later, it's a good habit to keep your server IPs private to prevent unwanted traffic or attacks. So, copy the IP address and open your terminal. We are going to use the SSH command to SSH to the droplet. So first we type SSH I and here we need to specify the private key and the keys are stored in the SSH directory and here I have called mine test SSH key and after that we specify the user that we want to log in. So in the machine we have uh by default we'll have just the root the root user but after that we'll I will show you how to create other users and here we type at and we paste the IP address. Now hit enter and you should be able to login. So because this is the first time we are trying to login, we get this warning. And here you can just type yes. And as you can see, I'm not able to log in uh because I have added a passphrase. I need to specify it before logging in. So now I will add my passphrase. And here on Ubuntu there, they we have this option that will automatically unlock the key. Um so if you want to login quickly, that's fine. and just click on unlock and as you can see I am in uh let's verify so this is the memory usage it's has 40% we don't have any swap memory this we are using 22% of the disk space and yeah it seems to be working you can see that we have some updates that we can perform and that's great so as you can see we are logged in as the root user and this is the name that we have specif ified when we created the droplet. So everything seems to be working. Okay. So since we are logged in as user, we can type in sudo app update. So this is a an abuntu. So this is a machine running Ubuntu and this is how we update it. Okay, good. So now we will type sudo upgrade. Okay, the type yes and hit enter to start installing the packages. If you see a configuration screen during the update, select keep the local version currently installed. Don't don't select any other option because we want to preserve the working SSH configuration that Digital Ocean set up for you. In the Digital Ocean page, we added a SSH key manually. So that updates the SSH configuration and we want to keep that but also add the new modifications that this update process is trying to perform. Keep the differences and yeah let the the updates continue. Great. So the update has finished. Now we need to reboot the machine. So just type the reboot command. This will complete the upgrade process and start using the new kernel and packages. So you can see that the connection is closed. So I was uh ejected from the server because it needs to reboot. But don't worry, I can run the SSH command again to login back to the machine. So let's wait a little bit or let's go back to Digital Ocean just to check uh if it is ready. So I'll refresh the page. Okay, it says it says active. Let's hit enter. And voila, I am back. And as you can see, it says zero updates can be applied immediately. This confirms that the update process works perfectly. Now we are going to create a nonroot user and configure SSH access. Why do we create new users instead of using the root user? Running everything as root is dangerous because sometimes you can make mistakes or if you don't know Linux a lot and you might be using LMS without knowing the commands that they give you, you might do something that will maybe corrupt your file system or do something irreversible and that's not good. And if a hacker was able to log into your machine, he can do every anything. We want to limit the things that we can do inside the VM by creating a nonroot user. And if you want to perform something that requires pseudo privileges, we can give the user that ability momentarily. So the command to create a user is add user. I will call that user Emad. So that's my name. Uh make sure to change that. And as you can see now we need to provide a password. This means that if I try to to run the same command sudo opt update the Linux machine will ask me to enter my password. So this is really great because let's say that someone wants to login with this specific user that I created, they will not be able to do commands that require sudo privileges because they don't know the password. So create the password uh retype it again. And here you can add additional information like the full name. So let me just add that uh room number. I don't need that. For the things that you don't want to add anything, uh, just hit enter. And finally, let's type yes to create the user. So, as I said, we want this user to be able to run pseudo commands. So, we will use the user mod command. And here we will type the the user. And now we can see that we are still logged in as the root user, but we want to log in as this user. As you can see that I'm inside the the droplet. In order to exit, I can type exit and hit enter or I can just hit control and D. Good. So the connection to this server is closed. Now I can go back and instead of uh login logging in as root I will use my new user. Hit enter. And as you can see we were we weren't able to connect. Why is that? And here it says too many authentication failures. In your case, you might see just permission denied public key. In my case, I see this because I have a lot of public keys. So the the server was or my laptop was trying to log in with multiple keys and it wasn't able to do that. So why aren't we able to connect as this new user? This happens because the SSH key that we added in digital ocean lives in the root user. This means that we need to take that SSH configuration and also give it to my new user. So let's log back as the root user and we can verify this. We can go inside the root user and we have again the SSH folder and here we have authorized keys and authorized keys is a file. It's not a directory and we can verify this by typing ls-la and you can see that here we have a dash that means it's a file. If it says d that's a directory. So authorized keys contains all the public keys that we want to use on this droplet. In our case, it contains just one because uh when we created the droplets, we added one key. But we can add more if we wanted to. But my new user doesn't have this directory. So if I go to home and this user new user, you can see that there isn't an SSH directory. This means that we we won't be able to login as this new user. But luckily, we can solve this issue by just copying this SSH directory from the root user to this new user. And the command to do that is cp preserve mode. And here we want to copy this directory to this new user. And preserve mode is an option that we can add in order to preserve the file permissions. So you can see that here. Uh so let me just run this command. And instead of running ch mode 600 700 etc on this new directory. Preserve mode will do that for us. So in just one command we preserve the the file permissions. Who can access this file? Who can read it? Who can execute it etc. And if now I can look at this. So let's use ls and let's see what we have inside the sh folder. You can see that we have the authorized keys. That's good. But the problem is that if I look at who owns that file, you will see that the root user is owning this file. So we want to change the ownership and give that to this new user that you have created. And in order to do that, we use the ch own. And here I will also add capital R. And I want this new user to own that file. And here I'll go to home and SSH folder. And now if I go back to ls-la, you can see that we have that this user is owning this file. Now we can exit. Uh again hit Ctrl D go back to the previous command and here type in the new user not root and now hit enter and as you can see we are logged in and the prompt shows my user and not root and if I want to let's look at root dosh I'm not I'm not able to do that because that's not my directory but Because I have the pseudo privilege, I can type in pseudo and enter my password. And as you can see, I'm still able to look at things that I'm that I'm not supposed to look at or per do maintenance uh stuff like that. But if someone hacks your machine and is logged in as this user, new user, they won't be able to do this because they need to know the password. Awesome. Uh just one note, if this didn't work for you, if uh for example, you weren't able to uh look inside the SSH directory, that means that you did something wrong. Just rewind and make sure that this step works because in the next step, we are going to disable root login. We will not allow even us, we won't be able to login as root. We will only login as this new user. This will uh help increase security a lot because no one will be able to login as roots because as you saw root doesn't require providing a password and that's a big security problem. If someone is able to login as that super user let me show you how to stop this from happening. We will use pseudo again. I'm going to use nano as matx editor and I'm going to go inside the shd config. We want to change uh the following I will say parameter and in nano in order to search you can click on or press ctrl w paste the thing that you are looking for and hit enter. You can see that by default this value is set to yes which means that we can login as root but we want to change that to no. We also want to disable password authentication even though we have specified in the creation of the droplets that we want to use an SSH key but it's it's also a great idea to set it here and not rely on the cloud provider to do that for you. So these are the two things that you need to change. You need to change permit root login to no and password authentication to no. Now in order to save this you can click on Ctrl S or if you are on an older machine you can hit Ctrl O just hit enter and after that Ctrl X. You can verify that again if this works. Just enter. And you can see that the the values are now set to no. So now we need to restart the SSH service. And now let's test this. So I will type exit. And now I will try to login as root. Okay. Let's see what will happen. Uh so as you can see um my request was rejected because we have blocked root access to or we blocked logging in as root. But if I go back and try to login as my new user I am able to do that. So that's awesome. So now we have lots of layers of security. We have disabled password login. We are using SSH keys. And even if we use an SSH key, we need to provide a passphrase. And within the machine, we created a new user in order to prevent us from doing things that are unreoverable. And if we want to do things that require sudo privileges, we need to provide a password. So this means that if someone was able to breach a layer, they won't be able to uh do something harmful because if even if they have the SSH key, they need to provide a passphrase or even if they logged in, they need to provide uh the password. I want to explain to you how the SSH configuration works. We have this main configuration file that we have changed. Remember we have disabled root login by setting permit root login to no and we also changed password authentication to no. So this is the main configuration file. But if I go up you will see that we have this line include and here we include all these configuration files that are inside this directory. We call this drop in configuration files. Now let's try to look at this folder to understand what we have there. As you can see I am using tmox. This is why I have two window panes so that you can see everything uh in one place. Here I have copied that path and let me enter my password. And here I need to list all the configuration files. You can see that I have two of them. And if I use this command, as you can see, let me zoom in a little bit. So here I am searching for this property that we have changed to no. And you can see that it we already have it in these two files. What does this mean? When we created our droplet, we selected SSH authentication and digital ocean created this file for us because on Linux we treat these drop-in configuration files before the main configuration. Let me explain. You can see that here we include all these files before changing all these fields manually. This means that even if I come here and set let me see so if I set this to yes this will not take effect because if I go for example inside this file so let's look at it here we have this command and here I will just try to print what we have inside that file and here I need to type 50 yes so as you can see this file contains just this configur this property and it is set to no. So this means that this will override any value that we have in the main configuration file and you can see that we have also another file which is called 60 cloud image and here we also have this property set to no. So as you can see we have two files that set this property to no. Which one should we take? Because this is the SSH service, we are going to order the files alphabetically. So here in this example, 50 cloud in it will come before 60 cloud image. We are going to read the properties inside this file and we are not going to look at the other ones. So here if we find that password authentication is set to no, we are going to take it and we will drop the other files and the main configuration file. This is why I said that the values that we have in these drop in file configuration are going to override what we have in the main SSH configuration file. So you might be asking why did we edit the main file if Digital Ocean has already created this for us. That's a good question. We edit the main configuration file as a safety net. If you ever want to do this in the future without using a cloud provider, in that case you will not have these files configured for you. So you will need to either create one uh manually and put it inside this directory or you come inside the configuration file the the main one and you change the values manually. This way you guarantee that your server is secure regardless of the hosting provider. So now I want to simplify the way we access the machine. So if I hit again Ctrl V, I need to always specify the SSH key and then the user and uh the IP address. That's a lot and there is a way to simplify this and I'll show you how to do that. On your local machine, you can see that I'm no longer connected to the VM. I have closed the connection. I want you to type this command nano uh SSH config. You can see that here I already have some entries but I want you to go to the end of the file and I want you to copy and paste the following. So here we have placeholders that we need to change here. Uh the host. So this is just a name. Uh this is a shortcut that we will be using in the SSH command. So here I'll call this test website. Here I need to provide the droplet ID. Uh sorry the droplet IP address. I'll copy it from digital ocean. Uh okay. I will replace this placeholder with that IP address. And here I will replace this with my new user. Here the identity file is going to be the private key. I think I've named it test ssh key. Uh but I'm not certain but I I can check this. So save the file again. You can do it by hitting Ctrl S or Ctrl O and then enter. And after that hit Ctrl X. So let me check. Yeah, it's test SSH key. Okay. And let's go back to the configuration. And yeah, it's uh it's correct. Awesome. So you can see that now after adding this to the config file we will be able to log to the website just by typing SSH and the shortcut name that we have created. So now I can just type this and as you can see I am in and this is easier to remember instead of uh remembering which key you use and what is the IP address of that droplet. And now I can just type this and I'm in. You have successfully configured a clean Ubuntu server and secured it with SSH keys. However, your server is still exposed to the open internet. In the next subchapter, I will show you how to work with the firewall to lock down the network. You will learn how to configure UFW to block unwanted traffic, set up fail toban to stop brute force attacks, and use the recovery console if you ever get logged out. Welcome back. In the previous video, we launched our server and set up secure SSH access. However, right now our server is like a house with the front door locked, but every window is wide open. The server is sitting on the public internet and by default it will accept connections on any port. In this sub chapter, the firewall strategy, you are going to close those windows and lock the parameter. Here's our plan for this session. First, you will address the problem. A fresh Linux server is too open. It accepts traffic from anywhere. To fix this, you will configure UFW, which means the uncomplicated firewall. You will set it to block all incoming traffic by default and only open these specific ports you trust. Next, you will install fail to ban. This tool adds a layer of intelligence to your firewall. It automatically bans bots that try to brute force your SSH keys. Finally, I will show you the digital ocean recovery console. This is your safety net. If you ever accidentally configure the firewall wrong and block yourself out, this tool will save you. Now, setting up a firewall is great, but it doesn't stop people from trying to get in. If you look at your server logs, you will see bots trying to guess your password. thousands of times a day. We need a way to stop them automatically. This is where fail to ban comes in. I will try to explain this using this diagram that I created. We have our server and we have an attacker. So this is a user who wants to get access to our server. You can see inside the server we have the uncomplicated firewall. We have fail to ban. We have the SSH service that runs on this port. And we have this log file. So the first thing that that this attacker will try to do is to connect to this server using the SSH service. This is going to fail because they don't know the our SSH key and maybe they don't know the user. In the previous video, we have blocked access to this server using the root user. So if they try to SSH as the root user, they will be blocked. So if that happens, the service will write to the log something like this invalid user Ubuntu from this IP address using this port. So yubuntu is the user that the attacker tried to use in order to connect to this machine and this is uh his IP. So if we see this we know that there are some bad actors who are trying to log to our server. So fail to ban is a service that we need to install. I will show you how to do that. And it's it's going to monitor this log file. And if this attacker is making multiple requests, we can detect that. And if fail to ban sees that uh we have too many errors coming from this attacker, it's going to tell the firewall to block this IP. And now if this attacker tries to login again, we are going to block that connection. It's not going to enter the server. And this way we limit attacks like these just by reading what's happening inside the log file. That's it for the theory. Now let's open the terminal connect to the machine and let me show you how to set up UFW fail to ban and other things. Before I start the practice session, I would like to mention that I updated the first chapter and now it contains the second subchapter and it's called the firewall strategy and you have all the steps written here and we are going to follow them in this session and this GitHub repository is going to be shared and the link is going to be in the video description. Now let's open the terminal. Last time we configured the SSH configuration to allow us to login directly without specifying the user and without specifying the IP address. So now I can run this command and I am in. Let's start by looking at the authentic the.log file. To do that I will use sudo. I will use the cat command to print the content of that file. I need to go to var log and art.log. It's going to ask me for my password. I will provide it. And as you can see, we have a lots of stuff. And we see that invalid message. Let me try to filter the output to get only uh the rows that mention this word invalid user. And as you can see, we have a lot. Remember that's my user is IMAD. It's not Oracle, it's not Solana or other stuff. And as you can see, we have plenty of IP addresses that are repeating. Let's take this one as an example. You can see that this attacker is trying to connect as a test user after that. Yeah, it's it's trying to connect uh again. And if you look at the time, so yeah, they are making hundreds or maybe even thousands of requests a minute or a day. And that's not good because they are here the the log file is going to be to get bigger and it's going to consume uh storage and we don't want that. It's going to be great if we can block these to not fill this log file. So you can see that I have just created this uh specific droplet for this course and as you can see I'm getting bombarded by uh attackers who are trying to log the machine but luckily we have services that will automatically ban them. So let's get started. First, I would like you to look at the status of the UFW or the uncomplicated firewall. To do that, we can use pseudo UFW status. And as you can see, it's inactive. This is the problem that we have. The firewall is is not active. We need to activate it in order to block the open ports and allow only the ones that we care about because the web application that we are going to deploy is going to be running on one one or two ports. We don't need to open every single port to the public internet. But before activating the firewall, we need to allow SSH connections. If you don't do this step, you will lock yourself out of the server immediately. So run this command sudo ufw allow open ssh. And this is going to allow port 22 to be accessible. Otherwise, if we enable the firewall without allowing SSH, we will not be able to run SSH uh the SSH command to connect to the server. and that's not going to be uh good. In that case, we need to go to digital ocean, open the recovery console and fix this problem. But if you do this, then everything is going to be fine. Now I can go back and run enable to start the uh firewall. So as you can see, we get this message. It says command may disrupt existing SSH connections. You can uh let's say yes. Okay. So it says firewall is active and enabled on system startup. Let's validate this. We have a command that we can run. It's called verbose. And this is the output that we are getting. We have default deny incoming, allow outgoing and disable disabled routed. And the status is active. That's what we want to see. And we have uh we have this table. So by default you can see that we deny everything. This is the most important rule. It means that every single port on your server is blocked unless you explicitly allow it. The the exception list is allow in. So here we allow open we allow SSH on port 22. This is the exception. But the other ports are all closed. We cannot use them. This means that port 80 for HTTP and port 443 for HTTPS are blocked. But in a future episode, we are going to open them. But for now, we only care about SSH. Great. So now we have the firewall. We can test it. I will exit the machine like this. And now I will try to SSH again by using the SSH command. And as you can see I am in. I hope that you followed the steps in order. And if somehow you are unable to reach the server, don't worry. I will show you how to use the digital ocean recovery console to fix the issue. But for now uh we are going to install fail to ban to deal with this problem of attackers trying to log to the machine and b yeah yeah filling in the disk space because the SSH service is writing a lot of stuff uh into the log file because here we are using luckily fail toban is available in its default repository and can be installed told by using the following command sudo up install fail to ban dashy means yes confirm everything. Let me enter my password. Awesome. So we can see that the installation was successful and now we can verify if fail to ban is running or not. As you can see, I run this command pseudo service and the service that we in that we want to inspect is fail toban. And they want to look at the status. And as you can see here in the active row, it says running. Awesome. That's what we want to see. Now, hit Q. By default, fail to ban only bans attackers for 10 minutes. This is too short. and bots that are hitting the web automatically are going to come back later. So let's increase this default value value to one day. Let's create a local configuration and edit this default value. So if we try to run sudo nano etc fail to ban, we can look at the jail.coniguration file. Awesome. And as you can see uh we have some default values and here they are telling telling us how to override this. So we can see that they say provide customizations in a jail. file. So this means that we should not updates the jail.config because if the system updates the values are going to be uh overridden and we don't want that to happen but the local file is uh not going to be changed. And if we scroll a little bit, yeah, we can see that we have plenty of values that we can change and the band time is set to 10 minutes. Uh I will explain the other values, but let's start by creating the local file. Here is the command and I want you to copy and paste the following text. Let me explain to you these parameters. So enabled means that we want to turn on protection for SSH band time. As I said, it's going to ban attackers for a specific duration. Here it is set to one day. Max retry allows a user to fail five times before being banned. This is important. Don't set this to one or a low value because you are you can also get banned. Let's say that I forgot to pass the SSH key and I did that let's say three times. If max retry was set to three, I will be banned from the server. I will not be able to log in. Keep it at around three or five. Don't set it to one. If you do once one mistake will be logged out. And if that happens, you need to go to recovery console, open the terminal, and unban yourself. But we don't want to do that. That's a lot of work. Find time is set to 10 minutes. And this is the window of time in which those five failures must occur to trigger a pan. So let's say that t is equal to zero. Time is equal to zero. Uh so the interval is 10 minutes. If we get an attacker who failed to authenticate five times during that duration, we are going to block it. But if that attacker failed only four times, we are not we are not going to block it because uh the minimum value is set to five. uh if we get five failures during that 10 minutes, we can ban them otherwise we will wait for the next interval and start the count from zero again. Now save the file and exit. I also want you to enable the failtobend service to start automatically when the server reboots. So do this and start it using the systemctl start command. Now we can verify that the ban time is equal to 1 day by running this command failtoban client get shd ban time and it gives us 600. 600 is basically 60 seconds time stamp which is 10 minutes. Let me retry running the enable and start commands again. Let's go back to get SHD band time and it still gives us 600 seconds. The problem here is that I forgot to restart the service and in order to do that we need to run the restart command. So this will allow fail to ban to read the new configuration that we created. Now if I go back and as you can see we get 86,400 seconds which is 1 day in seconds. Now let's see if fail to ban is working. We have this command status SHD. As you can see we get this output. We have the status for the jail SHD. Great. Currently failed total failed. We have the and currently banned. So we can see that fail to ban has banned these two IPs and here we have total banned currently banned. Now these two attackers are not going to be able to reach the SSS SSH service because the firewall is going to block their requests. Awesome. So this means fade toban is working as we expected. Now I'll keep this running. I'll now transition to showing you how to access the digital ocean recovery console and I'll come back and see if we catched some bad actors. You might be wondering why do we need to use the recovery console. I'll give you an example. If you accidentally run UFW enable before allowing open SSH or if you configure SSH keys incorrectly and lose access to your private key. In that case, you not be able to log in to the machine and the recovery console is provided to us by digital ocean to give us direct access to the machine through the browser and this bypasses SSH entirely. To access the recovery console, click on the droplets, click on the access tab, scroll down, and here you have the recovery console. Let's launch it. Okay. So, here it tells us that we need to log in. First, I want you to start typing randomly just to verify that the keys that are pressed matches what you are seeing because sometimes if you have a different keyboard layout, yeah, this might not work well. So in that case you need to basically start typing your login username and password and after that delete everything and start typing your user. So here I want to login as a and the password is going to be this one. As you can see I am logged in and now I can type in the same command that I've showed you earlier. sudo ufw allow open SSH it's already allowed so nothing will happen um because I am using pseudo I need to provide this and as you can see it says skipping adding existing rule but in your case it's not going to say this it's going to add it it's going to add this rule for SSH now we are fine we can exit and let's close this tab and now you can run the SSH command and you can login without any problem. Let's go back to Digital Ocean again. If for some reason you want to reset the p the root password, you can do that. But we don't have that, so there is no need to. And you can open the droplet console by specifying the user and you can launch it. So if you don't want to use your terminal, you can use everything through the web page. And as you can see I am connected as this user. Let's try to connect as root. Awesome. So it failed because in the previous session we blocked accessing this machine as root. Awesome. So everything is working as expected. What is next? You might be wondering. That's a good question. So far you have a solid foundation. You have a secure server that blocks unauthorized traffic and automatically bans attackers. As you can see, I have I rerun the status shd command. As as you can see, now we blocked four people. This is the new IP address that we blocked. So, this means that fail to ban is working. And as you can see, it's automatic. We don't need to do anything manually. And that's awesome. And you might be wondering why this IP was not added to the previous two. So these two got unbanned because the band duration is set to 10 minutes before I changed it to 1 day. And we can verify this by running this. As you can see it says unban. So [clears throat] if I look at the fail toban logs I can filter by this word unban. And as you can see these all these IPs were uh were unbanned. And you can also look at this specific new ID. And as you can see it was banned. So that's that's confirms it. And here I just want to explain this. If you will see that these two got unbanned because before changing the configuration to set the band time to one day, these two were already banned. That's why they they were unbanned in 10 minutes. But this one will stay here and will be unbanned the next day. Let's just verify that it is still there. Yeah, it is still here. And four, this basically keeps a total count of how many IPs were banned. It doesn't mean that we have four IPs that are currently banned. The currently banned is what you need to look at and it shows us that we have just one IP that that is banned from accessing or from trying to communicate with the server. In the next module, we will shift focus from security to software. You will learn how to prepare the environment by installing Python and NodeJS. Setting up swap memory to handle builds on a low RAM server like ours and configuring J unicorn and supervisor to keep your application running reliably in the background. Hi everyone. In this video, you are going to put your server to work by turning it into a web server. You will learn a lot from this video. I'll show you how to move files from your local machine to the virtual machine, use SSH tunneling, find and kill zombie processes, and more. In this video, you will be deploying a full stack application that uses Vue.js JS in the front end and pass API for the back end. The source code is available on GitHub and I will make sure to put the link in the video description. I am using my personal website in order to show you the exact same steps I took to deploy it. The goal here is not to deploy my specific website on your server, but use this project as a reference. Especially if you don't have your own project. If you have your own app, but you don't use Vue.js and pass API, it's fine. You can adjust a few commands in this tutorial to make it work with your specific setup. Now, let's go back to the slides because I want to show you the road map for this session. We will transfer the project to the server using the SCP command and I will show you how to organize the file permissions securely. Next, you will install the necessary runtimes. We will need Python for the back end and NodeJS for the front end. I will show you how to set up swap memory. This is an important step for smaller servers in order to prevent out of memory crashes during the build process. We will use SSH tunneling to securely preview the application on your local browser before opening it to the public. And finally, we will configure J Unicorn and Supervisor to make the backend robust and production ready. Now let's go to GitHub because we need to copy the source code. Click on the code button and copy this link. Now open your terminal. Since this is just a test, uh for me, I will put this in the download folder, but feel free to clone this repository in any directory. Run the get cloned command and here paste that link that we copied and hit enter. As you can see, we are cloning the repository and the command finished. Great. Now we need to move inside this folder. So I will use the cd command and I will type in my this name. And now I am inside that repository. And as you can see we are on the master branch. Now let me type ls. I want to see what we have inside. As you can see we have the backend folder and front end folder. These are the two folders that are that contain the s the code for the the website. The other stuff are not that important for now. These folders are inside my laptop. I need to move them to the server that we created on digital ocean. We could move them one by one, but that is slow and repetitive. Instead, we are going to compress these files and folders into one compressed file. In order to do that, we will use the tar command. So here I am going to call this compressed file source code and we need to exclude some folders that are not important and that will take a lot of space. For example, the node modules contains the dependencies that the front end needs. So we don't need this. we can generate it directly on the server. Same for Python, we have the virtual environment. We need to exclude that. We also have pi cache and the getit directory. So we don't need this these folders. That's why we exclude them. And here we have a dot and this means that we want to compress everything inside this directory. So if you wanted to compress just the back end folder, we you need to change this dot and put back end or if or something else. But here we want to take everything and put put it on the server. Now hit enter. And as you can see, we were able to compress everything inside this compressed file. Now we need to copy this from our laptop to the server. In order to do that, we are going to use the SCP command, which stands for secure copy. And here, because we configured SSH alias in the previous chapter, we don't need to type the IP address of the server, we can just type in the name. So here I will take the source code. So I'm going to copy this and I'll put it on the website. And here we don't need to type root uh the user at and then the IP address. We can just uh use the alias the alias that we created before. And here I will put it inside the temp directory. Now hit enter. And as you can see we have a progress bar that shows us that we are uploading this to the server. And we it's finished. So now I can go SSH to that website or to that server using this command. As you can see I am in let's verify that we have that zipped file and as you can see we have it inside the temp directory. Now let's organize the project files. We should move this compressed file somewhere. But where exactly? The best practice for web applications is to use a neutral location like /var/www or a dedicated folder like web app. This approach keeps your application logic separate from your personal user files like SSH or bash history and prevents permission issues. So let's create this folder with the make directory command. So I will use a make directory command and I'll put everything inside web app. Now we need to move the compressed file into this new folder. For that we are going to use the move command. I will take the path to that compressed file and I will move it to web app. Great. Now we can um yeah yeah yeah let's change directory let's move to web app uh let's type ls and as you can see we have that compressed file now we need to decompress it using the tar command again and here is how we do that hit enter and let's see yes we have everything now we can delete this compressed file because we no longer need it let's type its okay great so we have everything here that's great now Before uh we continue, let's type ls-la and hit enter. Here you need to see that your user is owning these files. As you and as you can see, my user is owning them. If you see root is owning these files and folders, make sure to run this command sudo chown. So change ownership dash r. We want to do this recursively. And here you put in you put the user and the group. in in in my case it's going to be a MAD and here you specify the folder but since we are inside web app um we need to yeah change directory but or we can just use dos okay and hopefully when you when you run ls-la you you'll be able to uh see that the ownership has changed and your user is owning these files. This is very important so make sure to do that. Awesome. We are now ready to install dependencies. Let's start with the back end. First we need to install Python tools because those are required for the back end. Here we are working with the with an Ubuntu machine. So this is how we can install them. So we are going to install pip the virtual environment and dev. And here dashy will just accept everything and we don't need to yeah confirm this operation. Awesome. We installed the those tools. Now I'll clear the screen. Let's go inside the backend folder. And now let's create an virtual environment. So we will use Python tree dash M. We will use the VNV module and we will create a virtual environment called VNV. Hit enter. Okay, great. Let's activate the environment. So we will use source bend/bin/activate. If you see the name of the virtual environment here before your name is the user's name that means that you activated the virtual environment successfully. Now if I let's type ls because I want to see yeah we have the requirements.txt file. Those are all the the dependencies that we need to launch the backend server. In order to install them, we will use pip install-r and we will provide the requirements.txt file. Let's wait for this to finish. And here make sure that before you install the dependencies that your virtual environment is activated cuz we want everything to be contained inside this project. Awesome. We were able to install everything. That's great. Let me uh clear the screen. And now we can start the back end to yeah make sure that everything runs without any issue. And here we are working with an with a fast API application which means that we need to use uh u core. Uh this is this command or this library is installed will be installed after running pip install-r requirements.txt. And here we have uh so the entry is the entry point is inside this file and the yeah the app is called app. We are going to provide 0000 to the host because we want we want to make this the server accessible outside the virtual machine. And finally we want port and let's use 8,000 because this is what the front end expects. So now hit enter and yeah it seems to be working. If you see application startup complete then that means that you are in a in a good shape and you can see that u is running on this IP 0000 and yeah if you try to click on this link nothing will happen because uh let's see let me show you as you can see nothing happens because this server is running inside the virtual machine it's not running locally on our computer uh close the page go back to the VM but if We can't access this the server outside of the VM. How can we test it? Well, in order to test it, we need to to open another terminal. I will use again the sh command and I will go inside the VM. But before I test it, I want you to run the uh UFW status command. uh provide your password and I want you to make sure that the firewall is active and that you have just SSH. So these are the ports that are allowed. But we don't have port 8,000 or other ports. That's why we were not able to access this server outside of the the VM. But we can at least test using the curl command. So we are going to send a request to this to the server and here 127.001 means local host basically inside the VM inside the server and 8,000 is the port and this is going to make a get request to the server running on this port. So yeah, let's see what we'll get. So it says detail not found. Uh maybe because we need to do health. Uh let me go inside the web application back end and let's look at the main file. Let's use uh or let's use maybe nano main.py. Let me make the screen smaller. I want to see the end points that we have here. So yeah, we need to use / API/hel in order to get status is okay. Okay, let's zoom in again and here we need to before help we need to add API and as you can see we get we are we get status is okay which means that the backend server is responding and if you look at the logs you see that we were making get requests this we we were getting 404 because the endpoints didn't didn't exist so the root endpoints or the /hel endpoints don't don't exist That's what we got 404. But when we try to talk to this endpoint API/halth, we got 200. Okay, because it exists in the source code. Great. So this confirms that the backend server is running and that's the we installed all the dependencies that we need. Hit Ctrl C because we want to stop the server. We no longer need it. Now we will move to the front end. We we are going to install the dependencies there and do the same thing. We need to verify that it works and after that we will combine them together. Uh you can see that we are inside the backend folder. Let's move uh one layer up. We have the virtual environment is still active. We can leave it like that or if you want you can um write type deactivate like so. And now we need to go inside the front end folder. I am using Vue.js in the front end. This means that we need to install NodeJS. And the best way to install NodeJS is with NVM. Uh if you don't know, NVM is a Node package manager. So just type nvm GitHub. There it is. And the installation the installation is easy. You just have you just run this curl command. So copy it, go back to the server and paste it here. So this is the latest version of NVM. Now in order to install it, as I said, we just hit enter and voila, it's done. Now the the problem is that when you install NVM, you need to create a new terminal to basically get the command. Because now if I run NVM, you can see that it's not recognized. But we have a solution. we can uh use source uh and here I am using bash so I will target bash rc and this so if when you run this it's going to basically load in nvm so now if I type nvm you can see that uh it works and let's verify that the version is correct yes it's 040.4 4. Awesome. Now we can we need to install the late the latest long-term support version of node. And in order to do that, we run nvm install and we we uh yeah we provide LCS because we that's what we want to install. And this is the latest LTS version from node. And if you want to yeah verify if that's correct, you can go to nodejs.org. or uh click on get NodeJS and as you can see so the latest LTS version is 2413.1 let's verify that and yeah that's correct so 2413.1 we also need to install PNPM this is the command that I use to install the front-end dependencies I don't use npm and this is how you install that make sure to install the latest version of PNPM version 10 and not prior prior ones. Hit enter. And here we want to install it globally. Okay. So, it's it's working. Uh it says that there is a new minor version of npm available. That's not important. So, let's click that. And now let's verify we are inside the front end directory. Type pnpm install and hit enter. And as you can see, yeah, PNPM is very fast and it's uh I really like to work with it. And voila, we have everything installed. And now in the front um so when working with Vue.js or any front end framework, uh you need to run the the build command to ensure that everything builds. So let's do that. And here we are going to hit a out of memory problem. So this is why we need to create a swap memory. But let's verify if that's what will happen or not. Okay. So as you can see the terminal is stuck. Uh but let's wait a minute. If you want you can also go back to the other terminal and maybe run hub. But as you can see the yeah the the VM is struggling. So the I can't run things smoothly and that means that I'm running out of memory. And yeah, this this this command will fail at any second. So let's just wait. Yeah, so this one is stuck. Let's go back to here. And let's wait. Uh I am back to the other terminal. And as you can see, the memory is full. Uh and this is not updating because it's uh struggling. And as I told you, the command failed. And that's because we don't have enough memory to build uh the front end. Is that the end? Uh we have a solution. So when uh on Linux we have something called swap memory. Basically we take some portion from the SSD and we use that as additional RAM. It's going to be slower but it's going to help us uh deal with these situations where we run out of memory. Let's run the df command. Going to show you uh so all available and yeah let's type it h to make it human readable. As you can see here we have 8 GB of disk storage and we have 5 GB available. I'm going to take two GB from this partition and I'm going to create a swap file because we don't need all this storage uh to run the front end and back end. And this is how we create a swap file. So we use the f allocate command. We specify the size and this is the the file that we are going to create. And here so let's just run the command first and let me provide my password. It worked and we didn't get any error. That's great. And I want to explain something this. So when you see / swap file, this doesn't mean that we this is a folder. No, it's a single file acting as a virtual RAM. So this means that you can just you can't do this. You can do this. Um just type in in / LA. And as you can see, it's a file. If it's that D that means that it's a directory but yeah it is a file and it's owned by root. Why did we create a file instead of a dedicated disk partition? Because we can easily resize or delete this file if we wanted to if we don't need it anymore. This is the flexibility of working with files. But we need to set up uh some permissions. Uh again let me type in ls-la. You can see that these are the permissions. We need to change them and uh to do that we are going to use sudo ch mode 600 and swap file. I will explain what the 600 means. Now if I type ls-la you can see that some of these characters have changed. So w means write, r means read and x means execute. But here we don't have x. And the first three let's say letters are related relative to your to the root user. The second three are relative to uh where is the yeah the second tree are here are uh for your user. So in my case it's emad and the last ones are for other users. So here uh so we did 600 we which means that root will be able to read and uh to read from swap file and write to it but the other users are not able to touch this at all. So we created the swap file. Now we need to make this file as a swap space and to do that we are going to use the make swap command and we are going to provide that file. Hit enter. As you can see, it says setting up swap space and here is the size and it gives us an ID uh which is not important. Now let's enable it. So we created the swap space. Now we will use swap on and um swap file. Hit enter. How do we how do we verify this? We are going to use the swap on command. And let's type show. And yeah, we have it here. So this is the file name and this is the size. It says that the file the type is file. That's good. And here we have priority. The priority level is set to minus2. In our case it doesn't matter because this this is um used only when we have multiple swap files. So if um we had another swap file that had a lower priority level, then Linux will prioritize this one. And when it's filled, it's going to move to the other one. But in our case, it's not relevant because we have just one swap file. By default, this swap configuration will be lost when the server reboots. To ensure that the swap file loads automatically every time the system starts, you must add it to the FS tab file. Uh let me show you the FS tab file that I'm talking about. It's this one. Now let's try to uh let's see the content. Okay, so this is what we have. Uh and we are going to yeah put uh this string inside there and we are going to use one command to do that to make things quicker. Here we have echo. It's going to spit this string. We use the pipe command to take the output from this command and give it as input to the t command. Let me let's hit enter. Okay, great. So it worked. Let's go back and as you can see we appended this string to the end of the fsst tab command. Now that we have added extra memory let's try to build the project again. So let's run pmppm run build. Uh I will also yeah let's uh open another terminal and let's go ssh to the server and here I want to run htop. Okay. And as you can see htop also detects the swap memory. So currently it is empty but look what will happen when we start the build process. Let's see what will happen. As you can see we are already filling the swap space and look how the the physical RAM is quickly filling up. Nothing is freezing. That means that it we it works. So if you look at the total uh space consumed it's yeah greater than the total memory that we have inside this uh machine and it seems that it worked. Yeah it built. So yeah that's that's awesome. Before making this tutorial sometimes node was not using a lot of memory so the build failed due to an error called heap limit error. If that somehow happens to you, then before uh running pmpp run build, we need to pass this variable. So here we are going to set max all space size to 2 GB. Uh we do that before uh starting pmppm. Um in my case it worked but if for some reason again uh you get that heap limit error, you can solve that by using this command. Awesome. So we built the project and as you can see we have the dis folder. It contains everything we have in the front end. Uh let's move inside that directory cd dist. Uh let's see we should have yeah we have our assets there. We have the index html some images sitemap uh and stuff like that. Now we can create a temporary Python web server on port 8000. Uh let me use Python and we want to use this module https server and the port is going to be at80. Uh I need to use python tree. Okay great. So we are serving the built files uh on this port. Again we cannot access this. So if you try to open this on your local machine, it's not going to work because the firewall is active and we are not uh letting traffic through this port. But is are we done? Um maybe yeah, we can use the cur command if we want. So we can do something like this. But yeah, we are we have port 8080 this time. And as you can see, we got uh some HTML back. Uh so that's that means that it's it's working right but we want to see this we want to see the UI because the back end uh it's it's fine if we run cur commands on the back end but when it comes to the front end we need to see it with our eyes to validates that it is working but you might say well we cannot do that because the firewall is active but I have a solution for you and it's called SSH tunneling. Let's go back to the slides because I want you to learn about this amazing feature. Okay, here we are and let's look at SSH tunneling. I have this diagram that I made in order to explain this concept for you. We have our laptop is sitting here. It can be running maybe Linux, Mac or Windows. It doesn't matter. We have the SSH key pairs that we created. Uh here we have the private key and we will be uh accessing the the website through a browser and here on the other side we have the server. It's can be running on digital ocean, Azure, GCP, AWS doesn't matter. We also we will have our front end running here. Uh in our case it's Vue.js JS but it can also be react or any other framework or just pure HTML CSS and JavaScript and we have our public key in the middle we have a tunnel that we call SSH tunnel how do we see the app without opening uh let's say port 8080 the we use SSH tunneling for that think of this as a secure private subway line that runs inside your existing SSH connection we don't need to change the firewall because we are already allowed to access the server via SSH to create a tunnel. We use the -l flag. And this syntax tells SSH to bind port 8080 on your laptop. It's this one colored in pink to port 8080 on the server. After running this command, a tunnel will be created. Now if I open my browser and type localhost and specify port 8080 in my browser the request is not going to stay on my computer. It's going to travel through the tunnel. It's going to reach the server the this uh and here it's going to reach the server on this port 8080 because this is what we specified here. It's going to see that we have a server running. This is why we have uh yeah we run that Python command to create a temporary server. Okay. So it's going to forward that to the front end. The front end will respond with that HTML. It's going to go back through the tunnel. It's going to hit the browser and the browser uh sees HTML. It's going to load it. So this is the concept uh of SSH tunneling and it's really great. And as you can see, we didn't update the firewall configuration to allow port at it enabled directly because we are already using SSH. But if we stop this command, the tunnel will go yeah will go away and these ports are going to be are not going to be connected. Awesome. So yeah, let's do this. Uh uh let's create. So here I'm already inside the machine. I need to exit by using Ctrl D or exit. And now yeah, let's run let's write that command. So it's SSH-L. Uh let's type the local port on my machine. Here we need to specify localhost AT80 and let's use my alias my website. As you can see, we are inside and now we have a tunnel. Let's go back. You can see that we have the server running. Now let's go. Yeah, let's open Firefox. And now we can go to localhost at80. And voila. As you can see, it works. You saw that we we got some errors because we are making uh requests to the back end, but we don't have a back end running. Uh yeah, so you can see that if I go to the blogs page, yeah, nothing will show up because we don't have the back end running yet. But that's not a problem. What we want to see is uh basically the UI. Does it look right? Um did we load the images? Um is the font loaded? Do we have animations running? Stuff like that. This is what we wanted to verify. But connecting the front end to the back end or to other services uh should be uh easily done when we connect it to the back end. Awesome. And yeah, let's go back. Uh now what will happen if I do this? So let's close this. Now we have no SSH tunnel. Let's go back and let's refresh. Can see that it doesn't work because now the the two machines are not connected. Great. So everything is working perfectly. Nice. We installed the Python the back end and front end dependencies. We made sure that both servers run correctly. Uh everything worked really well. Now we need to move to installing J unicorn and supervisor. But why do we need them? Let's go back to the slides again to explain this. Here is the diagram that I created for this slide in order to explain this. Uh as you can see, we have three components. We have supervisor, junior and uvicorn. We have used the uvicorn command to run the python server. After that, we used curl to ensure that the the back end responds correctly. So that's done. But we did this manually. What will happen if the server crashes? We need to again SSH to the server and run the UV command again. Is this scalable? No. What will happen if your server crashes at 4:00 a.m. or something? Are you going to uh wait until 8:00 a.m. when you wake up and launch it? No. We want the system, we want the server to do this for us automatically. And this is why we need these three components or this architecture. At the bottom, we have our workers. The worker he's is is Uicorn. The the worker is going to run your fast API code. So if someone visits the website and wants to get blogs, the worker will get that request. It's going to talk to me search and it's going to get the response and send it back to the user. Or let's say that someone wanted to maybe do a search query or maybe uh like a blog. U yeah that that worker will also do that. And let's say that we had a huge traffic um let's say that we we have maybe 100 people trying to access the server at the same time. Then the load is going to be balanced through these workers here. Let's say that we have three workers. So this one is going to get maybe 33 requests. The other one is going to get 40 and the other one gets 30. So yeah and J Unicorn will do that. It's going to balance the load through all these workers. Now we know the role of Uicorn but why do we need J Unicorn? J Unicorn acts as the manager. It spawns multiple Uicorn workers and this is can be configured. So if you want to you have just one URON instance or worker you can do that. If you want to have three or 10 or whatever yeah you can do that. Um you can just change the configuration. We will see this after explaining this slide. Let's say that this worker crashed for some reason. Jorn will detect that. It's going to delete it and replace it with a new with a new uh worker. And this is going to be done automatically for you. You don't need to enter to SSH to the server and start the worker manually. That's not scalable. But this this is above that we have the supervisor or we can also call it watchdog. Uh in engineering this term means a hardware component that basically will hit the reset button if the system freezes for some reason. Let's say that something bad is happening. The unicorn is not doing good. The unicorn curs are also crashing. Supervisor will detect that and uh hit the resize button which means that uh we are going to recreate everything from uh scratch just to ensure that yeah the the our application is healthy again and is ready to receive requests from users. So yeah, this is the this is why we need J unicorn and unicorn and also supervisor and you saw we tested manually. Always do that. Try to test uh your application manually by running the commands like what we did. So we run uh uicorn to start the backend server. After that we moved to the front end server. After validating that we can automate this process. Let's go back to the server and let's do this. Okay. Uh now let's stop the front the temporary front end server because we don't need it. Uh let's also go back to back end. Activate the environment and let's install J Unicorn. I'm not sure if I already have it in the requirements file. Let's just double check. And yes, we have it. So yeah, that's great. So we don't need to install it separately. Uh let's make sure that it is there. Yeah, it is there. So we it just didn't receive any application. So that's fine. So we have J Unicorn and we also have UV core. Now let's try Yeah, we need to create a J Unicorn start script that will tell J Unicorn how to run uh your application. Let's call it J Unicorn start. And let's use nano. I will paste all this directly here. But don't worry, you you should be able to find this on my blog. Yeah, I will make sure to deploy it uh in the future. So if you go to blogs, you will be able to see the individual articles here. So if you want to copy paste them uh quickly, you can do that. Don't worry about it. Or you can go to GitHub. I have this repository where I yeah basically documents these uh videos in a in text format. So we have foundation. So now we are in chapter 2. So you can click here and you'll should yeah will find everything here. So can just copy the commands and uh paste them uh in your VM. So okay let's continue. We forgot to add the shebang. So let's add it. Uh we are going to use bash. We have some placeholders that we need to change. Let's start with the user. So my user is IMAD and the group is also IMAD. So let's provide that. We have the number of workers. Let's set it to one because we don't have a lot of traffic and we are just testing. So as you can see I have this comment. increase this if you have more traffic or or a more complex application. But when you increase the number of workers, you will consume more RAM. So keep that in mind. The application directory is web app back end. That's correct. And we have this stocket file. We'll come back to this later. And here we need to give the project a name. Let's call this my website or something or a mad website. And here the worker is going to be Uicorn. That's fine. here forward allow ips. Yeah. So here here we just want to pass the IP of the user directly uh because yeah we will talk about engineext and unique sockets later so don't worry about this. Here we are just showing that we are starting this website. We move to the directory. We activate the environment. Here we create the socket file. Uh let's go back to sock file. We need to create this. Uh so this is important because it's going to communicate with engineext but we will talk about this later don't worry about it and here start junicorn so here we have the execute command j unicorn is inside the virtual environment we could have just uh yeah since we activated the environment we could just type j unicorn that's fine the application is inside the main file and we pass these arguments so the name of the project the number of workers the worker class which is uicorn. Here it is. Uh time out the user and the group. We bind this to the socket file. Uh we forward the IPs and we have some login. Great. So now save this. Uh I'll hit Ctrl S and then Ctrl X. And now we need to make this executable. So let's do sud ch mode plus x or before that I want I want you to understand the ch mode command. So before that let's run ls-la j unicorn start. You can see it's not executable. But if I run ch mode plus x j uh unicorn start and run the ls-la you can see now we have x and x is is available in the three users. So ruse can execute this file my user ID can execute this file and other users can do the same. Great and you can see that I am the owner of this file. So we configured the unicorn. Now we need to configure supervisor. We could again we could run this uh script manually but if it if for some reason J unicorn crashes we need to come back and uh rerun it again. We want we don't want to do that. We want supervisor to do this for us. Supervisor is not installed by default. Yeah, it's not recognized but we can install it using the op command. So let's do that. Supervisor. Let me provide my password. And it is installing. Great. Let's wait a little bit. Uh let's also create the logs folder because we we want supervisor to save logs um there so that we can uh if something hap bad happens we can uh see what happened there. So that's important and again verify that you are owning that uh directory. So run this and yeah we are running that since we don't use the sudo command we will be uh owning everything but just in case uh it's a good practice to keep checking the ownership. In order to configure supervisor we need to create this configuration file. So supervisor lives inside the etc folder and here we want to create our own configuration and let me paste this. Uh okay so we have so as you can see we want supervisor to run the J unicorn start start command. Uh so the only thing we need to change here is the user. It's going to be emad and here we need to change this placeholder and let's call it a mad website. Okay. And the log file we are going to store to yeah to store logs inside this. This is why we created this directory. And yeah um save this. Hit ctrl x. Great. Now we can start this service. Let's use pseudo supervisor ctl. And here we are going to reread the new configuration. It says available. It detected this uh new config that we created. And now we need to let's run this. Great. So we added this to the process group. And now let's check the status. It should be running. So if I type status uh and here you need to type that project name. Great. It says run in. It didn't crash. That's great. Let's see. So, we have logs. So, let's look at logs. Supervisor log. The file exists. Awesome. Let's do cat or maybe tail. Awesome. So, it's working. You can see that. Uh, let me let me look at the full output. So, these are just this is just configuration. Um, yeah, great. And here the application started. So we are starting J unicorn. Uh it's listening at this socket and here we have just one worker and the application startup complete. So this means that it's working. We can try and uh yeah let's do some curl commands. Oops, we don't have it here. Uh that's fine. Okay. Uh this is the curl command that we used earlier. Uh let's try to run it. And it fail. It says fail to connect to local host. Why this command failed? We have tested this before and it worked fine. Let me show you why this failed. Remember we have uh yeah let's look at the G unicorns tart uh file. You can see that we are using this socket file. So in order to communicate with the server, we need to use the socket file that was created here in back end. And let's verify that it is there. Yeah, as you can see, we have JU Unicorn stock and if I look at it, yeah, it's it's it's a different type of files in Linux. You can see that it says S, which means socket and this is used um it's like a pipe. It's used to uh by different applications to communicate with each other. In our case, we want uh the backend server to communicate with EngineX. Uh but we don't have EngineX yet. We are just preparing for that step. uh but uh to fix that we can do this. So with curl we can specify the Unix socket file and here here is the the socket file that we want to use and same here we are going to use this endpoint and curl will use this socket as a way to talk to the backend server and the backend server will send the response through the socket to curl and curl would display that. As you can see, I I run that and I got status. Okay, awesome. Now we can uh run tail again just to verify and yeah, we got this. So we uh so this is the get requests. We got 200. Okay. And yeah, it took less than 2 milliseconds. Awesome. So yeah, everything is working. Now if we run htop or I want you to install this. So we have a replacement um we have many uh applications like htop with good UI. One of them is called Btop and I really like Btop. Uh it looks like yeah this is a Btop and it's available on a lot of Linux distros. It's also available in Auntu. So that's great. And to install it we can as you can see it looks really really nice. So we are going to use this in order to monitor the processes, memory usage, etc. And to install it, we can run the APC command uh sudo up to install btop. Awesome. Now run btop and as you can see uh it looks really nice. So here you have the CPU usage, you have memory usage. Uh let me decrease the yeah the the font size. Okay. So we have uh so we have 8 GB of storage. We are using 65%. This is the swap um space that we created. It's not used. So we are just using 6%. This is the available memory. It's uh yeah almost 500 megabytes. We are using 35% of the memory. Uh and yeah we have also the network. Uh so we have the download and upload speeds. Um and here we have the processes. This is what we care about. And here you have filter. With the arrow keys, you can uh let's go to memory because that's what we want. And this is going to sort everything by memory. If you want, you can hit R to reverse this. But we want to yeah, we want this sorted in a descending order. And here type filter and run uh and type J unicorn. Why? We want to see the processes that are running. Here we have just one worker but you you can see that we have two processes. Why is that? So one J unicorn creates two services. So one will manage the workers. So one of them is the worker and the other one is uh the one controlling those workers and as you can see they are using uh roughly 24 megabytes. That's not a lot because the application is not doing anything right now. But this is why I mentioned that if you increase the number of workers, you are going to consume more memory. Why I why I installed bet? Because I want you to I want you to learn about zombie processes. Um yeah, this is something very important because sometimes uh this happened to me. I didn't pay attention and I my memory usage was rising up quickly because I was I was not restarting J Unicorn correctly uh because sometimes when I do when I work on a new feature uh I push that to the server and uh restart the back end that created zombie processes. Basically some uh G unicorn processes were left here. they weren't doing work but they were consuming memory because they were from the old deployments. So let's see uh we don't have this problem right now. Uh we we have just created this uh but I will try to do uh to basically create zombie processes just to show you how this looks like and how to deal with this problem. Uh but first we need to go and SSH through the website. Great. So if you don't want to look at this, we can run um this command ps and we are going to grab and we are going to use I think aad websites. We have two of them. So yeah, this one and the other one. Uh this looks ugly because we have a lot of text but no problem. It confirms that we have two processes running. Now let me manually let's first go to web app back end and I want to run this manually just to create more processes. Uh let's do this. So J unicorn now hit enter. This is going to create uh yeah this is going to run again the the back end and it's going to create more processes. Uh let's wait a little bit. You can see that the memory usage is getting higher and higher every single time. Okay, good. And now if we want to we can also go inside uh that file G unicorn start. And um yeah let's change this from 1 2 3 save exit. And now we need to restart. We will run this command because we changed the configuration. We need to read that and restart again. Let's do this. So restart and let's uh use my project name. Uh I need to pass my password. Okay, it says stopped. Let's look at this. Okay, so uh the old processes were gone and now we have three new ones. Uh we should have just the manager is going to be controlling the the workers and we should have three workers because this is the new configuration that we created. But we also have how many? So one we have 1, two, three, four. But we have three orphaned processes or we can also call them zombie processes that are look consuming memory but not doing any useful work. And if you get this somehow uh this is how you solve this issue. You can't just come here and hit okay to kill the process because it's not clear which one is uh doing work. I mean you can sort of uh get that information by looking at the memory because the the workers that are busy are going to be using more memory but yeah the you need to confirm this and I'll show you how to do that. Uh let's run uh ps. As you can see we have uh yeah we have plenty of processes. Great. Now I have this uh command that we can run. uh it it's basically giving us the same info that we have here in Btop. I'm just showing you that running commands like these, it's not that fun, but looking at this uh in a terminal app, it's really uh useful and it's fast. But you can do the same thing with just Linux commands. And here are the processes and how much memory they are using. We can also try to uh sum the memory. So we are using almost 300 mgabytes. But yeah, we have some processes that we need to remove to free some memory. So the quick fix is to kill all J unicorn uh processes. And to do that you can either do you can do it here uh by hitting K and and you say yes I want to kill this but uh if you want to kill everything you can use uh process kill and this is the name of the process. uh but this will cause downtime. Your your application your users won't be able to access your application. Uh the front end will try to make requests to the back end but it's it's not going to respond. So they will get errors. So this is the easiest uh you do this and then supervisor will start everything up because it sees that something bad happened. Where are the J unicorn services? It's going to panic but it's going to uh yeah run them. Let's actually do it. So let's look at this. So we have uh all these J unicorn services. Let's kill them. Now let's go back here. You can see they are gone but supervisor create them created them quickly. You can see that we have four. That's the correct amount. We have the manager. Uh one of them is the manager and the other ones are workers. And you can see this is why we used supervisor in order to uh solve this issue for us. Great. Yeah. This is the the easiest uh solution and the quickest but if you it will cause some downtime but yeah it depends on your application. If you don't have a lot of users just do this one it's fine. Uh but if you have more users then yeah we shouldn't do it. Let me show you the other option. So I will again run the J unicorn start command. Uh okay, we created more or more processes. Let's restart. Okay, so you can see that the restart command is the one that's causing the issue. It's yeah, it re it reads the new configuration, but it doesn't delete the old processes. It just creates new ones. So this is the the big issue that we need to solve. And this is what I was doing uh in my websites running currently on production. I was just pushing new changes and I run restart before removing the old ones and this created a lot of orphaned uh processes. So we can see that now we need to remove uh three more processes. Okay, I show you uh I have showed you the easy method. Now let's try to solve this uh surgically without causing any uh downtime. First we need to ask supervisor for the process ID of the J unicorn process which which we can also call master because it is controlling the other workers and to do that we use sudo supervisor ctl P ID and here uh you type your project name so this is the P ID of the master process this is very important in in your case it's not going to be the same it's going to be a number But just uh remember it. Now let's look at the process hierarchy. We can use this command. Okay. So yeah, we have a lot of text. We can also do the same thing here uh in Btop. You can see that you have this tree column. Just uh hit E and as you can see it groups everything. So this is the master process. You can see that it's uh it is controlling these three processes and these ones they don't have a parent. So they are just here they are orphaned processes that are consuming a lot of memory uh but uh doing no work. So we need to kill them. So now this makes it easy. So I can just hit K and kill them one by one. But we can also see this in the ps-f command. Uh yeah I know this is this is a lot of text. So let me just copy this and let me open a text editor. So I'll open them in VS code. Uh so yeah this this stuff is not needed. So you can see that these are all the commands that we uh are used in order to run the s the yeah the junior core service. We don't want them. So let's just delete this stuff. Uh we don't need this two uh and this. Okay great. Let's zoom in a little bit on this part. Okay great. So let me also go back to the terminal. Let me also copy the master ID. So this is the master process ID. It's this one. You can see that how many we have three of uh we have 1 2 3 4 5 6 7 and eight. Where is the the process the manager? Here is the manager. So uh and here I forgot to add the column names. Uh I think uh let let me go back. I just want to confirm something. Let's go here. This is the process ID. This is the parent process ID. RSS. Yeah, we don't need this one. So let's remove it. We just want these two. Let's see. We have uh yeah, these are the three UVON workers and they are attached to this master process ID. And if we look at this, it's going to point out to this one. And this is that fourth J unicorn service. It's this this is the same as this. We have these. So let me double click on this one. As you can see, here is the process ID. It's 531 402 531 402. And it is attached to the parent. Uh let's look at the parent. Yes, it's this one. 531399. This this is your group. It's exactly it's exactly this. You have one process ID and one master process and their children. But we have these three strugglers, these three orphaned processes. They are these ones. So 274, 272 and 273. Here they are. And the PP ID is equal to one. Hm. What does this mean in Linux? P1 is the system in it process. This means these processes lost their parent but at least they were adopted by the init process because they they need to be attached to something. This is how you detect orphaned processes. If they are not attached to a process ID uh if they lost their parents they will be attached to in it. And now we can safely delete these. And we have the process ids. So we can do it uh here with the help of the top. Let's do it once for this one. So we hit okay. And yes. And it should Yeah, it's gone. Bye. We no longer have that zombie process, but we have the other two. Uh if you want to do this with the terminal, you can just uh use pseudo kill. And here you provide the IP addresses or the process ID sorry. Uh you pass them like this one after the other. Now hit enter. Go to Btop and they are gone and we freed memory. You can see that uh we we had to do a lot of stuff in order to detect the processes that uh are not used by uh our back end. But this this is useful if you don't want to cause any downtime at all. Your application code is on the server. We built it. We run it and we everything went smooth. The back end is managed by supervisor and listening on a private socket file. However, no one from the outside world can access your application yet. This is a problem that we will fix in the next sub chapter. We are going to talk about engine X security headers and stuff like that. See you in the next episode. Hi everyone. In the previous section, you successfully started your backend and set up a process manager. Your API is alive and listening on a Unix socket file. However, your users cannot access your website. Your front end is also just a folder of static files sitting on the server waiting to be served. In this sub chapter, we are going to talk about reverse proxy and security headers. First, we need to sync the front end and backend URLs. After that, we will install EngineX to act as a reverse proxy. I will explain this once we install Engine X. I'm going to explain to you what does 502 bad gateway means. So this is an error that we might get if we don't set up enginex correctly. And finally we will move to the security part. In this sub chapter we are going to focus only on the HTTP security headers mainly CSP and there are other ones but CSP is the one that that requires a lot of work and we will focus on it at the end of this video. Now let's SSH to the machine because we need to start by syncing the front end and back end and I will come back to the slides in if I want to explain some diagrams. As you can see I am logged in to the machine and before we configure engine X you need to prepare your code. You want your front end and back end to act as if they live on the exact same server both in local development and in production. This avoids complex cross origin issues. Let me show you what I am talking about. As you can see, I need to go to the web app directory because last time we moved the source code here. I will change ls to cd. Now I am inside that directory. Let's start with the front end. Let's go to front end. And here we need to go to source. And we have main.js. So we need to open that file. Let's use nano and let's see. As you can see here we are using an environment variable v a base url. Let me show you the content of that variable. Let's do ls. And here I need to go back to front end because here we have the environment file as you can. So let's do ls-la. Yeah, you can see that we have env.ample and we also from that we create the env file. But let's look at env.ample by using cat. So let's provide the file and as you can see this is what we have here. The back end runs on this port. When you are developing this locally the back end will be running on a different server. And in the front end we use Axios to make HTTP requests and it needs a it needs to know where the the API lives and here it lives in port 8000. This is good but it introduces cross origin issues. If we try to send a request to this port, the browser will block it because it sees that you are trying to communicate with a different origin and even though we are running on local host, it it's not going to be allowed because the front end runs on port 8080 but your back end runs on port 8,000. So that's how I started developing this but I will show you a simple way to avoid this problem. We are going to use vit and with vit we have something called proxy and it will make this very simple. So this example we no longer need it. That's why I wanted I wanted you to open the main file and change it. So let's do that quickly. Now you can see that here I am using axios and the base URL is we set it here. I I will remove all of this. I don't need it. And here instead I will just put slash. By doing this the browser will automatically send requests to the current host. So if I am running on local host this will will tell the browser to send it to local host. If I am running on my domain it's going to be that domain. That's the first change that we need to make. Now save the file. Exit uh with Ctrl X Y and enter. Great. Now we need to configure vit. So let's look at vit. It's not here. Uh it's it must be here. Yes. So we have vit config. Let's open it with nano vit config. Now I want you to scroll down here. You can see that we have server. And this is uh where we set the port. By default vit starts on on a different port. I think 4,000 something. But we uh I usually change it to 8,080. And here we can add another field to the server block and we we call it proxy. Here is the the field that we need to add to server. You can see that it's called proxy. And here the backend API is yeah it has this prefix / API and we tell V to send that request to this URL or or to this target. This means that if the front end tries to send a request to the API, vit will intercept that and target this URL. This way we can change origin without having to worry about cross origin issues. Let me explain this with another diagram that I created. You can see that here we are on a local machine. we are developing the the web application and here I I am using let's say Chrome and I want to make a an HTTP request. So I I am in the browser. If I want to load static files basically the web page I will call I will be here. So the path will not be API. I will just talk to the front end. The front end will send back the HTML, CSS and JavaScript files. Vit will take that and send it back to the browser and the browser will display the web the website for me. Good. But let's say that I am that I want to search for an article or something that that request will need to talk to the back end. So vit will see the the request that I'm trying to make or the browser is trying to make. If it sees that it has this prefix / ai, it's it's going to send it to the back end. So, VIT sits between the browser and your front end and backend servers and this implementation solves the cross origin issues. Awesome. Now, we need to go to the to the back end. Let's do that quickly. So, we are in the backend folder. Let's see what we have. So, we need to look at main.py. Let's open it with nano. And here we need to remove some things. You can see that here we have a coarse middleware. This is what we needed before using the proxy feature that vit uh gives us. So we need to remove this class. And now let's go down a little bit because here as you can see we used that middleware here because we we allowed the following origins. So this block need we need to remove it. And in order to do that, hit alt a go down and hit ctrl k. That will remove the those multiple those lines. Now we need to yeah save this and go back. Okay. So now the we don't have course issues. everything will the front end will be able to communicate with the back end because as as you as you saw we used uh the proxy feature that v comes with. So after making this change the back end we don't need to to do anything in the back end but the front end must be uh built again. So we need to build the front end. So go back to the front end folder and run pnpm build. Why we need to do that? because the build process would regenerate the the HTML, CSS and JavaScript files and it will bake in the changes in those generated files. But the back end doesn't need that because it doesn't have a uh build step. So just run pmppm build and let's wait for the build process to finish. Awesome. The build finished. Now the first step in this video uh is done. We synced the back end and front end URLs. And now we can move to using EngineX. But why do we need enginex? EngineX, you can think of it as a front door for your server. When a user types your domain name into their browser, the request will hit X first before loading HTML files or before talking to the back end. So engine X is going to intercept that request and it's going to act as a reverse proxy meaning it will decide what to do with that request. Let me show you another diagram that I created to explain this in detail. I am back and let's let's go to the next slide. Here is the diagram that I created in order to explain why we need engine X. As you can see, it's similar to the diagram that I showed you that explains vit proxy. Here we start with the user with a browser. We send a request before talking to the front end or back end. We in EngineX will intercept that request. If it sees that the path is slappi something, engineext will talk to the Unix socket and that Unix socket will send the request to J Unicorn and J Unicorn will send it to the UVORN worker which runs our back end. You can see that this is the part that we talked about in the previous sub chapter. Let's go back here. When we talked about SSH tunneling, we mentioned that the front end is running here and the back end is running on the server and we explained how that works. We have the supervisor, we have J unicorn and our workers and here we mentioned that we created a Unix socket file and I told you that I will explain to you where that will be used and in this video we are using that to communicate with EngineX. Let's go back. You can see that we have the unicorn. Here I simplified the diagram. I am showing just one worker, but you get the idea. You have your Unix socket. It is used in order to allow EngineX and J Unicorn to communicate between each other. You can see that here in the description I I said it's a two-way pipe connecting EngineX and J Unicorn. Uh so let's say that the user is searching for an article. Let's say that the endpoint is / API/ articles. That request will be intercepted by EngineX. It sees that it has the / API prefix. It's going to send that to Unicorn, but it's going to do it through the Unix socket file because here we are inside the VM. And in order for two programs to communicate with each other, we used Unix sockets files. So now the J J unicorn receives a request to the / API/ articles endpoint. It's going to give that to the fast API endpoint. If we have an endpoint, great. The code will run. We'll we'll try to fetch articles. Now we will send that response to engine X. So we will move uh the response will go to G unicorn. We'll will go through the socket and back to EngineX. engineext sees the response. It's going to send it to the browser and yeah, you will see articles. So that's why here we use unique Unix sockets and you can see on the other side we have just the static files and if we if if we have a request to an end points that doesn't start with / API we will go here we will fetch the static files which are HTML, CSS and JavaScript and the browser can display that. So that's why we need enginex and as you can see it works like uh vit but this runs on production. You use vit locally when you develop your application but on the virtual machine or on the digital ocean droplet we use engine x. It's the same concept but one runs on your laptop and the other one runs on the virtual machine. I hope that my explanation was clear. Now let's install enginex. We will do it this way. We are we are on Ubuntu. So we can install this directly from the repository. I will provide my password. And let's wait for a few seconds. Awesome. We installed EngineX. You need to understand that EngineX uses configuration files to know how to route traffic. Let's create a new configuration file for your website in the sites available directory. Now let's do this. pseudo nano etc engine x and we have a directory called sites available and here you put your project name let's call it my website maybe.com uh com we are going to build this configuration block by block so that you can understand everything let's start with the upstream block as you can see I just copy copied this from GitHub and you just I we just need to change this placeholder. I will use the same name that I used for the configuration file. And if you are wondering where to get the code or yeah the the things that I copy paste, you will find them on this GitHub repository. Here it is. So if you go to full stack deployment handbook, you will see that here I am documenting everything. Click on the book folder you have and here you have the chapters. So far we completed chapter one. Now we are in chapter number two. So if you click on the uh the second markdown file let's search for upstream. You can see that here is the upstream block. I you can copy it from here and paste it in nano and uh go from there. So so that you can uh go quickly. So don't worry about the code. Everything will be shared in the video description. First, as I said, start by changing that placeholder here. I use my website com, but you can use any other name. So, this acts as a variable that we can reuse in other blocks. Instead of typing every single time Unix, web app etc., we will just use this variable to make the code clean. Here you can see that we are so here this is a variable. Here we have fail timeout is equal to zero. This is an important setting because it tells engineext to never mark the back end as down even if it fails to respond. So uh if you have a lot of traffic and your back end is starting to respond slowly. Sometimes the engineext will drop the request if it if it doesn't respond quickly. But here we tell engineext don't worry uh even if the back end is down don't fail. Okay, now let's add a different block. It's called the server block. This tells EngineX to listen for incoming web traffic on port 80, which is standard HTTP, and to respond when someone asks for your specific domain. Right now, we haven't created a domain name, but that's not a problem. We will use the droplet IP here in the server name. But later this will be changed from the droplet IP to the domain name. So let's do this. If you don't know how to get your droplet IP, we have different ways of getting this but you can go to digital ocean. Make sure to log in here in the projects. Make sure to select the project where you created the droplet. And here as you can see we have the IP address. So copy it and let's go back to nano and let's paste it here. I will remove this and replace it with the droplets IP. You can see that we also have some log files. And here we need to change this. Uh these are also placeholders. And here I will use again let me copy this my website com and let's place it here. Let's also do it here. This is helpful because if something breaks, we can check the error logs to understand why that happened. Here I have a comment that says we will add the location block some other blocks here next. But as as you can see we are doing this step by step to understand every every line that we are writing. Okay. Now I will remove this comment again. I will use Ctrl K to do that. And let me place the location block. Remember here we have slash which means that we want to serve the front end. If I open the browser and go to this IP address and I add a slash at the end, engineext will catch that and will serve my front end to the user. Now let's try to understand what we have inside here. We have root. This tells engineext where to look for files. In this case, it points to the dist directory because when you run pmppm build, the files that are generated are going to be inside the disc folder. Try files is important because here we have a single page application. It tells engineext to find the exact file the user asked for and that file is going to be here dollar uri. If it is not there, it's going to look for a directory. So if it this is a directory, it's going to look inside it to find the file. If it can't find the file, it's going to use index.html as a fallback. Let me explain this. So here I have my personal website. I can open it. You can see that here we have slash at the end which means that we are serving the front end files. If I want to open something else like maybe my resume, I can do it. So I can do /resume and you can see we get it. But how does this work? If I go back to the machine or to the VM, let's save this file. Let's hit Ctrl X. Let's look at the this folder. What do we have here? We can see that I have a lot of files. So we have some images. We have robot txt. Let's let's also try to get robots txt. robots txt. You can see that I am able to see this file. Why? Because engine x knows where to find those because of this. Uh let's go back to nano because of this location block. So try files will uh URI will be robots txt and uh we know that we should look inside the disc folder. If we find it good, we will show it. But if we can't find it, we will show index.html. Okay. And uh yeah, index html is also should be here. Uh you can see here here it is. So that's that's how that's uh the reason why we added this location block. And this will serve the your front end. We will do another one for the back end. And it's going to look for / API instead. I will copy it and I'll paste it here. You can see that it is similar. we will use the location block and instead we will look for / okay now we have um a lot of directives the these are called directives so proxy pass is a directive and here we have we need to change this placeholder from your project name to this string so proxy pass will the request over to your J unicorn socket that's what I explained in the slides we are here. So we use the unique socket to allow EngineX to forward the request to J unicorn and this is what we are doing here in this directive. The proxy set header we have two of them are crucial here because they take information about the real user who is trying to access your website because if we don't do this we will all we will send the IP address of your virtual machine and we don't want to do that and finally we have proxy redirect of tells engineext to trust the redirect URLs exactly as uh your backend sends them. So if your back end tells EngineX to redirect, EngineX will trust it. It's not going to do uh something else. Okay, great. So now we have the we we have two two locations, one for the front end and one for the back end. So we can save the file and let's exit. We have two options to test this configuration. We can do it live here on the server. But if you aren't unsure or if you already have a working website and you want to introduce changes to your configuration, if you do if you have a mistake or if the configuration is false, that will cause downtime and we don't want that. There is another option which is to use an a website called EngineX Playground. EngineX playground is a website that allows you to simulate your routing rules safely in your browser without uh having to do this on your production server. You can see that here we have we put the engineext configuration. Uh here we have events HTTP. We as you saw we didn't write these. I'll explain to you why. But we need to have we need to add them for the configuration to work. the the the blocks that we created are these. We created server location. We created two of them. Here we are listening on 80 because we we used uh HTTP. Here we are serving the front end and here we are serving the back end. This playground has a built-in testing back end running on port 7777 and we can use this as our back end. Here you write your curl commands to yeah send HTTP requests. We'll start with local host. Make sure to use local host. You can't use your own domain because this is a playground and we are testing things internally. To test the cur command you hit run. And as you can see the output says serving view.js JS files which is correct because here we have slash and the this location block will catch that and it will in this case return this string. Uh but let's add uh maybe something else. So let's add API v2. Will this work? Let's see. Let's hit run. And that works because here we have / API and that catches this. And here you can see that we have u yeah this is a response from the back end. Uh let's do something else maybe let's do API v1 or slash articles. Uh let's remove this. Let's send the request and it works. So this block will catch this. But maybe let's do something else like maybe resume or something. Yeah that will be catched by this block. So everything is working. And if you add complex um I don't know if you work on complex configurations this playground will be very useful. So we know that our configuration works. Now we can close this go back to the VM and I want to tell you why in our configuration we don't have the events block and HTTP. Okay, let's close the file. And here on Ubuntu, we use a modeler system for EngineX. There is a master configuration file located here. Uh let's look at it. Let's do cat etc. EngineX enginex doconfig. Hit enter. So this is uh this is the master configuration file. Let's scroll to the top. You can see that we have events. We it's already here and we have the HTTP block and if we scroll down uh we include some things and what do we include? If you look at this path it's etc engineext sites enabled here we have an asterisk to load every configuration inside that directory and if you remember the configuration that we created is inside this folder. So that means that this master file will load our configuration. That's why we didn't we don't need to add events or HTTP. It is it's already there. And one thing that you need to understand is that the engineext will run as this user. It's not going to run as my user or the user you created. No, it's going it has its own user. It's going to use it. So, it's ww data. Now, we need to enable this site. Um, let's see. So, we have pseudo nano. Okay, good. But I want to use uh ls instead of nano. So let's see what we have inside the engine x. You can see that we have two folders. We have sites available and sites enabled. So sites available uh we define the configuration. So just so that engineext knows that there is a maybe a configuration that's that that it needs to take into consideration. But for it to work we need to put it in enabled. And here we are not going to copy paste it because that's that's not [clears throat] a good bracket because you would need to maintain both. Instead we are going to use a symbolic link. Uh this is uh just a shortcut that will put that file here and if we want we can just change the configuration inside sites available and that change will uh take effect in the other directory. So we we'll have it in two places but we can change it once and it will take effect in uh other places. Here is how we create a symbolic link. We use ln dash s here we need to change this. Uh let's see I need to put my configuration my name the name here. Uh let's go here and let's do it in size available because I can copy it easily. Let's copy this and let's paste it inside sites enabled. So we are creating a symbolic link from here to this directory. Now hit enter. Great. Let's look at let me copy this uh or let's copy the other one. Let's do etc. And here I will add ls-la. And as you can see here it says l which confirms that is a symbolic link. As you can see we have this it is pointing at this file which means that if this file change changes this uh file will also change because it is a basically a shortcut. We are almost ready. So now we need to make sure that the configuration is correct. So run this command and what you should see is this. It should say it should say that the syntax is okay and the the test was successful. If you see an error or something, please fix it because if you use a configuration that is not correct, it's the your websites will not work or people will not be able to access your website. Awesome. So now let's reload X so that it it can read the configuration. And now we cannot access the website. So I can take my IP address. Let's copy it. Uh let's go here and let's paste it. You can see that it's not going to work because we have the firewall. We need to allow traffic through port 80. And in order to do that, we have we can do this. We can allow EngineX. You we use sudo here. We use the uncomplicated firewall and we tell it to allow EngineX. Let's do that. Okay. And now let's verify the status. Sudo UFW status. Uh before as you saw as you can see we we had just open SSH so that we can SSH to the machine but now we also have enginex which means that we can talk to port 80. Now if I go back if I go to the website as you can see it's already worked. Before it it it wasn't uh working. It couldn't find the the machine or the the firewall was blocking it. But now as you can see I can refresh the page and I uh I see the front end. You can see that the website is not secure because we are on HTTP. We will solve this in the upcoming chapters and you as you can see we have some errors because we don't have articles yet in the brows in the application but the front end is running and the back end is also running. So if I click on blogs or courses you can see we cannot we cannot get them because they are not here. And in my web application I am using mill search and we will talk about that in the next chapter. But we also have a problem here. So uh let's see. So if I open the console, let's zoom in a little bit. You can see that I am not able to make a request to the API endpoint. Interesting. So let's go to the VM and let's understand why is this happening. Remember we added log folders. So here let's do this. You can see that here we have error logs for our website. Let's take this and let's put it here and let's look at what we have here. We don't have anything. Okay, that's interesting. And let's look at access. So here access log that's me trying to access the website. So you can see that I made a post request to these endpoints. So here we have tracking and here we have search and yeah and I was using uh Mozilla I'm using Firefox. So yeah that's uh what you see in the access logs but we don't see any errors. Let's look at the supervisor logs. For that we need to go to back end. Let's see. Yes, we have logs. Let's go inside. What do we have here? We have supervisor.log. So let's let's do tail supervisor.log. And as you can see we see the problem. So here it says that uh failed to establish a new connection to me search. So here it is me search communication error. That's what that's why uh what I said but we will fix this in the upcoming chapter. Let's make sure that we can also reach the back end. So here the back end starts at API slash and let me do health because we have an endpoint that tells us if the back end is alive or not. So as you can see we are receiving okay which means that this is working. Uh we can confirm this in uh the main file we have this endpoint API/halth and yeah it means it is working. If you followed the previous sub chapters, you shouldn't get a bad gateway error. But if you do, that might happen because of the engineext user doesn't have the full permissions to to access the Unix socket file. Uh remember the Unix socket is here. You can see that it is called junicorn.sock. And if we try to let's look at the permission. So if we do ls-la juniors sock, here is what we have. Uh I want to correct a mistake that I made in the previous video. So here the first three characters are for the current user. So the current user is IMAD. If you are connected as root then it's going to apply to root. The second tree are for the group and the final three are for other users. So the mod here is 777. Why? Because here uh we have three of them. So every person or every user can read, write or execute this file. This is not good. We need to make sure to restrict this. This is why the we didn't get bad gateway because everything is open. Everyone can uh access read this file. But we need to change this or maybe if you have a different mode then you might get a bad a bad gateway. Let's try to do this. So uh I'll change this intentionally just to show you how to debug this problem. Let's use ch own and let's change the ownership to 700 and let's point it to let's do it for j unicorn.stock. What does 700 mean? So this is in binary and let's take these three. The first character is one, the second character is two and the third character is four. If you sum those three numbers, it's going to give you seven. So if all of them are on, if you want to allow read, write and execute, you put seven. If you don't want to allow anything, you put zero. So here it means that for the current user, it can do everything anything with this file. But for the the group they can't and for other users they cannot uh do anything with it. If you want to allow people in in the group to just read the file maybe read and execute in that case we will put five because four + 1 is five. Let's do this. So ch okay so it said uh operation is not permitted. Let's use pseudo. Okay that works this time. Let's go back to ls-la. Sorry, I did ch own. I I should have um changed this to ch mod because these are called mods. Uh so yeah, this time it worked. Uh let's do ls- array. And as you can see it changed. Let's verify that. So the three characters here for other users are not available which means they are set to zero. The second three are read and execute. That's that's uh five. And uh the last three for my user is seven. So I can do whatever I want with this file, but other users cannot. So this is very important. And remember you engine x is not inside this group. It's not my user. It's ww- data. Okay. Now let's try to do this. Uh, as you can see, we get the 502 bad gateway error because EngineX is trying its best to send this request that I am trying to make to API/halth to J Unicorn. Let's go back to the slides. It received / API/halth. It tries to send it to J Unicorn, but the Unix sockets, we changed the mode now to not allow EngineX to do anything with it. So that's why it was blocked and uh if it is blocked then it's going to display this bad gateway error to the user. Now how do we solve this? So we can add engine X to my group that will solve the issue. If you want you can set it to 777 but that's uh yeah that's not secure. Any user can do whatever they want with the file. Let's add uh ww data to my uh group. And let's let's also uh let's look at the error logs. Uh I want to see what we have there. Uh as you can see we also get uh something in the error logs. Great. So this confirms as as you can see it says critical permission denied. So it it tried to connect to the socket but it failed because it doesn't have the full permissions. Awesome. So this make uh this confirms that changing the mod will either allow engine X to be able to connect to the socket or not. So let's fix this. We have different ways but I will do it in this way. So I will just use this command sudo user mod- a g and here I have this you put here put your user my user is emad I will put it here and I will add ww- data to my user now we have another command called get end group and here put your user and as you can see you should be able to see ww- data awesome so this confirms that it works Now let's run uh let's do that ls- array and ww data is part of my group so it is a uh let's see so we have this tree so it's able to read and execute okay let's go back to the browser let's refresh uh it is still not working maybe let's try to reload engine x so here hit ctrr and start typing reload it's going to find it in the history Let's reload. Okay, we still we are not able to connect to the socket. I want to go back to uh ls-la and let's do it on J unicorn. And here we have a problem because before I ran ch mode and that changed the yeah the user to 750 and that's not correct. So we need to fix this. So let's do uh ch own. I want to own this file, not 750. 750 is not correct. So that was a mistake that I did because instead of typing chod, I typed ch own. And here, let's do it on genicorn. Let's run ls-la. Okay, now that's fixed. Okay, let's go back again to the website. Okay, it's still doesn't work. And maybe because we also need write permissions. So that's how you debug this. Um, let's do it. So let's go back to here and let's change this to 770. Let's look at ls-la. Let's go back to the web application. Okay, now it worked. We need also write permission because we need to write to the Unix socket file. That makes sense. We write data to this file so that G unicorn can read it. And again, G Unicorn will write data to Unix file so that engineext can read it. Okay, great. So that works. And as you can see, the other users will not be able to reach this file. If you followed uh the same steps as I did, you should be uh able to get this to work. Awesome. So, we have finished with the first part of this video. Now, we will talk about security headers. We saw that we were all already secure because we have the firewall. it was blocking port 80, but now we allowed port 80 so that we can access the website. But security doesn't stop at the firewall. Browsers have built-in security features, but they only activate them if your server tells them to. And if you want to read more about this, you can go to HTTP security headers, and you will find this documented here on the developer Mozilla website. And as you can see, they have a lot of headers. They will explain them to you in detail, but I will show you because we have a lot of uh headers. I'll just show you the main ones that we need to have. And there are also websites like security headers.com and the http observatory from Mozilla that will test the security headers to see if they are uh on your website or not. If they aren't, you will get a low score. But if they they find them, then they will tell you that your website is secure. Same thing with security headers. You just need to paste here your domain name or uh in our case, we don't have a domain name. So we could try to put uh just the IP address. Uh let's try this. Let's try it here. I'll try it on HTTP Observatory. Ah, so it says that it cannot scan IP addresses. Okay. So that means that we need to do it with a domain name. Okay. But here let me actually take the full the full link. Yeah, it doesn't work. So with HTTP Observer 3, we need the full domain. But I can uh use my own website. Let's try to scan it. And as you can see, I get A+, which is great. It's even higher than the 100. So uh the security here is great. But let's go back to security headers. This is the websites that you are that we are working with. We have created the new configuration, but you can see that it lacks a lot of headers here. The great thing is that they show you the main ones. They have the list of missing headers. They tell you why you need them. And here F. So F is the lowest score that you can get, but you can climb uh from F to A+. And here what I like about HTTP observatory is that if you click on scan history they show you the progression. So in the beginning I got I got the because I wasn't aware of these headers or I didn't add them in the beginning but after that I started to add them gradually to make uh my website secure and now I have the highest score which uh yeah which which gives me the A+ grade. We will do the same thing for this website that we created now. Yeah, let's start working with these simple ones because the the complex one is CSP content security policy. That one we will leave it at the end because it requires a lot of iterations to get it right. Now let's go back again. Hit CtrlR. Let's do pseudo nano. Okay, we find this is the configuration. We will start with strict transport security. This one forces the browser to always use HTTPS when talking to your website. And here it will uh force that for the next two years. So this value uh is 2 years. You might be asking we don't have HTTPS yet. We are just on HTTP. That's not that's not a problem because this header is ignored by browsers if they see that you are uh accessing the website over regular HTTP. That's fine. But this will be active or it's going to be used by browsers when we switch to HTTPS. So keep it like that. And here we want to also include subdomains. Uh we don't have that but if you plan to add subdomains then uh we also want to include them. Okay. So this is the first one. Now we can yeah let's let's save the file. Uh hit Ctrl X. Remember we need to do sudo engineext-t. Okay. It says successful. Now we can reload. Reload EngineX. Now let's go back to the browser. Let's scan the website again. Okay. So yeah, it didn't change because that header wasn't in this list. That's fine because now we will add X frame options, X content type options, and another one called cross origin cross origin resource policy. I'll copy all of them. I'll go back to nano. Let's go back to nano and I will add them here. So here I have some comments if you want them. Uh I will just remove them from here but they are in the markdown file. Let's start with corp. This one allows sharing. So if you want to allow other browsers to share your resources then you can leave it this way. Otherwise you uh you you might need to block it. And to do that you just need to copy this header. Go back to the browser, search for it. You will find the Mozilla uh the documentation there. And here as you can see you have uh you have an explanation and you also have the directives that you can that you can set. So you have same site, same origin or cross origin. In our case, we are using cross origin because we want to allow other websites to maybe link our articles, images or something like that. But if you want to block them, you put same sites which means that you you allow only yourself to yeah reuse uh some of your resources. Okay, let's see what we have in nano. And as you can see, we allow other origins and origin means other uh domains. Uh I will remove the comment because I don't need it. We have here anti- clickjing. Uh we use Xframe options for that. If you want to read more about clickjing, you can find this Wikipedia page which explains it. Basically, this is uh how the attack works. So, the user is presented with a false interface where their input is applied to something they cannot see. You might visit a website, they will show you this page where you see they are telling you claim your prize. But this page is layered on top of another page. So when you click on claim your prize, you are not clicking on this button. You are clicking on another one which is invisible to you. And here they say confirm bank payment. So they can do uh shady things. They can take your data or something like that. So that's a problem and we don't want to do that. We we want to to prevent this. To do that we add xframe options and we tell same origin. As you can see we are not using cross origin uh or other directive. Uh let's look at the other directories quickly. Let's search for it here. Um Mozilla. Okay. So here this is what we have. So we have either deny sim origin or allow from origin. Awesome. So we have that. We also have MIM sniffing. This one forces the browser to strictly trust the file type declared by the server. Sometimes attackers might upload malicious files maybe like a script but they might tell the browser this is not a script this is an image and this is called MIM sniffing. MIM means uh the the format types. So let's search for MIM. If you are not familiar with MIM let's search for MIM type and yeah it's uh media types. So this uh uh yeah it can mean pawns, images, text, scripts etc. So attackers will trick your browser to think that that script is an image and in that script they might load some uh malicious code or something like that. So we want to deny this. We don't want them to sniff the file types. We added three extra headers. Now save the configuration. Let's do engineext-t. Everything is good. Let's reload. Nice. So, let's go to the browser. Let's close these tabs. We don't need them. Let's scan. Great. Now, we have D. So, you can see that we have two green check marks, but we need to add another ones. Uh, these two are simple. So, let's add them. And let's leave CSP at the end. Okay. And let's add the headers just below them. So, here we have this one is for privacy. When a user clicks a link on your website, uh for example, if I link a maybe a Wikipedia article on my website, if a user clicks it, the browser will send a referral header to the new site. So if you want to protect user privacy by only sending your your root domain name, not the full URL path, you can do this. So this uh then wiki here if they click on that Wikipedia link they will Wikipedia will only see my domain name they will not be able to see the full link. So yeah we can add this and here this one is simple. So if your website doesn't require using camera microphone dual location payment you can disable them because these are things that I don't want and that's great. So if someone tries to maybe hack your website and enable the user's camera that's that will not happen because we are blocking blocking that with the help of the permissions policy header. Now save exit the file verify that the configuration is correct. Now reload let's go to the website and let's refresh. Awesome. So it says C and in order to jump from C to A+ we need to work on CSP which is the final boss. This one is complex but I'll show you how to do it uh step by step. Okay, let's go back to our website. So this is the front end. Everything looks good. Uh let me open another tab. I want you to search for this. So we find it here and we also uh you can also see it here. So yeah, uh if you want to read more about cross sites, that's not what I wanted to search for. I wanted to search for content security policy CSP. Uh yes, here it is. You can see that this article is very long because in order in order to build it, there isn't a one there isn't one CSP that everyone can uses. This depends on what you are using in your website. That's why you need to build it progressively. open your uh website. So I will delete uh the tab that we don't need. Okay, we just want these two. Now we are in Chrome because here we have the ability to change this CSP very quickly. I'll show you how to do this. Now I want you to look at the very top of the network request list. Here we don't have a domain. That's why we have just uh the IP address. Right click, show all overrides. Uh here I will start fresh. Now select let's click on select folder for override. So I will create one here and select it and let's yeah allow this. Now go back to network. Here I want to click on override headers. Yes, that worked. And here let's add content security policy because this is the one that we want to work with. And here in the header value let's start with default source and let's do none. And let's not forget the semicolon. Now hit enter. Now I want you to click on the dot headers pile here. It should say star because we want to apply it to everything. And let's refresh the page. Okay, you can see that we cannot view the website. H and we have a lot of errors. So we can see that here it says refused to load the image, refused to load the stylesheets, refused to load the script. So now that's what we need to do the and also make sure if you want you can open this uh headers file in a text editor so that you can change it here easily. I'm opening it in sublime and yeah I see the same thing. So I'm applying it to everything and I am just playing with this header and default source none will block everything. So this is the strictest CSP and as you can see it doesn't work but we start from here and we start relaxing it in steps. So you can see that there are many things that are blocked. There are images that are blocked some styles. You can see that this image comes from our origin. So this one is fine. Uh let's see we here we are blocking this side sheets because we are it comes from cloud flare so this is not our website it's coming from another place but we know that we need this font it's awesome so we can keep it that's not a problem but this will show you the things you will decide do you want to keep it or block it uh same thing for scripts these are all coming from our assets folder so yeah these are these things can can be kept but uh if you if we see that there are other strange change things we need to decide if we should keep them or not. So let's start with the uh with the images. So I will come here. You can see that here default source I will change it from none to self. I saved the file. Now I'll go to the browser. Let's refresh. So that's solved a lot of problems. Why? Because you see that we don't have assets. The image was loaded because those things were in our disc folder. So when we built the website we already had those images uh those stylesheets everything was there. So when you use self it means our origin everything in our origin uh should be allowed to load. Now we aren't we have uh different errors. So for forget about these ones uh because as we saw before we don't have media search that's why they are failing. But focus on these. Now we cannot load this font. So this is the fonts that we are using in our website. Yeah, let's fix this one. Go back here and we have another directive. We need to come here. Let's add a space and let's put style source. This one will allow us to load styles. Again, we can start with self. And if we want, we can add more things here. But let's save this. Let's go back to the website. Uh or instead, let's let me put it here. And let me load Chrome like this. Yeah, that's better. And I will move this docs this panel uh to the bottom. Now I've saved this. I will refresh the page. Uh we still have some problems with uh Google. So we will copy this link and paste it here. We we we need just the the origin. We don't need the full URL. So we can see that the origin is fonts.googleapis.com. That's what we need. Now save. Refresh the page. And as you can see now we are able to load the font. We don't have any any error. Now we have another stylesheet issue but this time we will just take this domain. Take it copy and let's add it after Google. Sorry let's add it here before the semicolon. Save. Refresh the page. Awesome. So now we are we are able to load font awesome icons. We are able to load the font. And as you can see, we don't have those errors, but we have different errors. So here we are not able to load the font URL because it violates the following default source self. Okay. So you can see that here we don't have uh let me decrease the font size. We have default source. We have style source but we haven't yet added font source. So let's do that. I will come after the semicolon. I will add this and I will add another semicolon. Save this. refresh the page. Okay. So, let's open this and let's see what's happening. So, again, we have the same do origin. Let's copy it. Let's paste it after self. Save. Reload. Okay. So, we have a different domain that's coming from Jstatic. That one is fine. Again, if you see that there is a domain or an origin that is suspicious, then you feel free to block it. But if you think that this is something that you are using and it comes from a reputable website, it's not something suspicious, then add it to the list. So now we solved the problem with fonts. Now let's look at images. Okay. So here we have some X SVG files and in order to keep those we can just add data. I will come where is it? Do I have image source? No, we don't. And here they are telling us. So note that image source was not explicitly set. So again let's do the same thing. Uh I will copy font source. I'll come here and here I'll need to change it to image source. Save. Reload. Okay. That still doesn't work because we need to add this here. We don't have a website but uh we can add where is image. Uh let me make this smaller. Yeah, it's here. I can come after this and add data. Let's refresh the page. And voila. As you can see, we were able to fix uh all every error. And now these are just coming from the back end. But we are not done yet. What we what you need to do is to test your whole website. Make sure to click on every single page. See if the errors are uh if you don't see the goal is that we we should not see errors coming from here. Okay. So as you can see, we have other problems. Okay. So we have inline style issues. Let's go back to sublime text. I want you to find style source. Do we have Yes, we have style source. So we have self font. Google and here. So I will add a space at the end because here they say that we are using so yeah we are using inline styles but that this is blocked by default. So we need to add unsafe inline. Let's save it first and let's reload the page. Okay, we have zero errors. Great. But this is unsafe. Here's the thing. Even though this is unsafe, it's fine here in our case because in our Vue.js application or maybe you uh you might be using React or other frameworks. You you are using inline styles or inline scripts and those are fine because if you don't do those, your application won't work. But yeah, by default they are blocked but we can add them. Let's continue testing the application. Let's go here. Okay, that's fine. So, the other pages are not working because they need to communicate with me search. But at least uh every other page is working uh correctly. Good. Now, after doing this, we can let's let's copy the value. I'll hit Ctrl X. I no longer need this. So, I'll clear this. Uh I'll just keep this. Now, I'll go back to engine X. I need to update the configuration. So let's do that. I will come down and here I'll type add header. It's going to be content security policy. I'll add quotes and I'll add the other ones. As you can see, I have added the CSP. I'll save the file. I'll exit. Let's verify that the syntax is okay. And it isn't. So this is why we need to do this. Let's go back. I forgot to add something at the end. Yeah, I forgot to add always. And why do we do this? Because we want to send this header in all the response codes. So if we get 200, 2010, all of them. So uh this header will be there. And we can also add something here at the end. I will add another semicolon. I will add upgrade insecure requests. This one again forces the browser to change from HTTP to HTTPS. Now we have HTTP. So this doesn't matter but later uh after adding SSL certificates stuff like that we will be able to have HTTPS. Now we are just preparing save the file exit. Verify okay the syntax is okay this time and let's reload X. Okay so this is the moment of truth. I will take this. Let's put it here. We need to make sure that the the website loads correctly. And it is it is loading. Let me move this to the left. Uh I will clear the logs. I also go to the other pages. Yeah, everything seems to be working perfectly. Okay. So now let's scan again. And yes, we got uh a um maybe uh on Firefox might have gotten a plus, but this is the highest score that we can get. And as you can see, everything is green. And the yeah these are the this is the response that uh we received. So as you can see this is what we have added here is upgrading secure requests and yeah they're also telling you if you want to add other requests but this is the bare minimum you need to have these and as you can see most of them are easy but content security policy is the one that takes time because it is unique to each website. Your application is now live. Engine X is serving your view front end and routing API calls to your fast API back end. However, we haven't yet configured search. This is a search engine that I am using to s to allow users to search for articles. It's similar to elastic search uh yeah to elastic search but it doesn't consume a lot of memory. So we are going to configure that in the next chapter. But if you don't care about mail search, you can skip to the to the other chapter, chapter number four where we are going to create our domain name, add SSL certificates and make our websites secure by accessing by making um making users access accesses through HTTPS instead of HTTP. See you in the next chapter. Welcome to module 3. At this point, we have a secure server and we have deployed the front end and back end successfully. There is one missing piece if you are using my project which is to include a search engine. In my case, I am self-hosting M search because it is lightweight, doesn't consume a lot of memory and it is fast. A quick note on this module. If your specific project doesn't need a search feature, you can technically skip this chapter. However, I highly recommend following along anyway because the skills you are about to learn like creating a system user and deploying background services that are completely hidden from the internet are essential server skills you will use anytime you need to host your own database or yeah something else. Here is our road map. First the search engine that we are going to download is me search. There are other options like elastic search but those consume a lot of memory and here we are playing with a small virtual machine and we can't just host elastic search. So we are using me search because it's lightweight it's fast and most importantly it doesn't consume a lot of memory. Next we are going to log this database by creating a dedicated system user. I'll explain the difference between a regular user and a system user in the upcoming slides. Then you will configure systemd to run me search as a background service. This will allow us to perform daily backups. Make sure that me search is running even if it if the service fails and other things. As I said, we are going to schedule automatic snapshots. I'll show you how to do it. You can specify the duration. Do you want to do it daily, weekly? It depends on how much your data changes. Finally, we are going to use SSH tunneling again to access the daily search database uh using a dashboard. Now let's go back to GitHub. I want to show you what we are trying to do. We are now in module 3 data and search. You will you'll find the commands here. So even if you get lost or if you find long commands, don't worry about them. Uh you will find everything inside this markdown document. As I said, if you don't have a project and you rely on my own inside the backend folder, I have everything for you. So even if you don't have data that you want to put in me search, you can just come here in the seed folder and I have created this documents list. As you can see, we have some documents that we can index uh in mill to me search. Okay, so don't worry about this. Everything is inside these two repositories. Great. Now, open your terminal and let's SSH to the machine. I will use my alias. Uh the alias that I'm using is my website. I have successfully logged into the machine. Uh let's run ls. Yeah. Uh let's go to web app. Okay, so we have everything. Great. Let's start by installing me search. If you click on the curl option, I mean you have a lot of options, but we'll go with this one. Copy this command. Paste it here inside the machine. It shouldn't take a lot of time. Yeah. So it worked. Let's see. So we have me search. Uh let's see if it is executable. Yes. If we are the current yeah for my current user it is executable. Now we need to move this binary into user local /bin. Let's do this. So we have mainly search here. Let's move it to user local bin. I need to provide my password. Awesome. Why we do this? Because it allows us to run search from any place. So I can now just type med search d- version and it works. I don't need to go to uh say say I downloaded the binary in the downloads folder. If I don't move it to user local bin, I have to always go to downloads in order to interact with the binary. But now when you put it in uh user local bin, it allows you to run it from any place like we did. Now if I type ls as you can see we don't have the mely search binary but I was able to run the me search uh version command without any error. So now we have me search. Let's create a system user. Before we do that I would like to come to go back to the slides to explain the difference between a regular and a system user. I have created this table to explain to you the difference between these two types of users. Here are the features and here are the types of users. So what is the purpose of a standard user when we started this course? We created a user. We are not using the root user but we created another user that we can use to do maintenance, install software. Yeah, stuff like that. We are using a standard user to administer to do maintenance. So this is the purpose of this user. But a system user is used to run background services only. So if I want to run M search uh in a background service, I need to run to use a system user for that. Can we use the shell in a standard user? Yes. As you saw, I was able to SSH to the machine and uh type commands. But if I try to do the same with a system user, I won't be able to because we deactivate that. This means I can't login as a system user and start typing commands. So this is the same thing that I that I mentioned here. So login is possible with a standard user but it isn't allowed with a system user and generally the standard users have their uh home folder inside/home but system users are yeah put somewhere else. So this is the difference between them and because M search is a software that we want to run as a background service we want this specific system user to be responsible of handling that. Okay. So let's create uh a system user and we can call it med search because it uh its job is to uh uh run this software for us. And in order to do that, we will use the user add command. Here I will uh specify the directory where we want to put this user. As I said, we are not going to put it in home. We will put it here. var lib mail search. We will disable login. So we won't give it shell access. And finally, we will call it search. Mele search. I hope I didn't make a mistake. Me search. Me search. Yes. So now let's hit enter and it worked. Now let's try to look at this directory. As you can see the I don't have the permissions. Why? Because I am using my standard user aad and this folder or yeah this directory is owned by me search the system user we created and we can verify this. And if we do sudo as you can see this user mini search owns this this folder. So we cannot go inside it. But yeah with with root we can do that and we can also verify it here. As you can see the permissions are they are they are constrained. They are they are allowing just this user to write to this directory. Great. So this confirms that everything is working. Now we need to create a folder inside uh that will hold data. We need to create the data folder because me search needs somewhere to save data. So let's do it. Uh let's verify the ownership. This is very important. ls-la as you can see. Uh let me go back. Okay. So data is owned by root and we want it to be owned by med search. So let's do it. We we have used this each own command before. Here we are uh changing the user and group from root to M search and we want this user to own everything inside this directory. That's why we are using dash r. So now if I rerun this command as you can see now the data folder is owned by mini search. Awesome. So now we installed the mini search binary. We created a system user. Now we need to put some data inside. So if you already have your own instance, you you don't need to use let let me go back to GitHub. You don't need to use this JSON file. You can just uh export the data from your local main search instance and use that. If you if you don't have data, then you can use this. I'll show both options. so that uh everyone can uh follow along. Let's start by exporting data from a local me search instance. For that I will create another terminal window. Now I am uh on my local machine. I will start the mearch service and I will show you how to export data from it. I will use T-Max to create two panes here. Okay, I moved where I installed me search and this is how I am using it. Um, okay. So, as you can see it started. Let me show everything and here. Yeah, it is listening on this port. Maybe search by default uses port 7,700. Uh, in this other terminal we can talk to this server. We can use the terminal or if you want you can also use their SDKs. They have one for Python, JavaScript and other programming languages. But for now, to keep things simple, we will just use the yeah the terminal and we'll just run a curl command. And as you can see here, we have something that we call dumps. And here I need to provide my master key. And since we are on a local machine, yeah, I am just using their default master key. Plus in a production environment we will change this into a secure key. But for now it's fine. Uh when what you are working with this locally no one will steal data from you. So it's fine. Keep it like this as the default. Yeah. So now I can't talk to this. But before running the command I just want to explain the difference between dumps and snapshots. If you search for me search dumps snapshots, you will find this page where they explain the difference between these two concepts. A snapshot is an exact copy of your database. You use a snapshot to create backups. So this is what we are going to schedule later. We are going to uh allow me search to save a backup of the database daily. So that's let's say some if uh for some reason something bad happened to me search and you lost data you can recover by using these snapshots. A dump on the other hand is just a set of instructions that are used to recreate the database from scratch. So this is the difference between the two and dumps are the safest way to migrate data across different machines or versions. This is why in uh my local terminal I am using the dumps. I'm going to create a dump with this curl command. So now let's run it. As you can see we get a task uid. Uh in m search tasks are asynchronous which means that they take time to finish but search at least gives us a task u so that we can track the task and the task that we are performing is creating a dump. And if you want to follow a task, what you need to do is to change here dumps to tasks and you provide the U ID and in in our case it's 4,94 and you yeah keep keep the authorization key and hit enter. And here I forgot to change post to get. So let's try to do that. And as you can see the status is succeeded which means that we were able to create a dump. That's great. And we can also if you want you can also verify this in the mini search server. Uh so here we started with 20 workers. And here as you can see we the server received the the task and here it says a batch of task was successfully completed with one successful task which means that everything worked great. Now we just need to find this dump. As you can see, I have my mini search binary inside this folder. If I hit if I try to run ls, you can see that we have a dumps folder. Let's go there and let's see what we have. Let me also remove the first pane. We don't need it. As you can see, I have created a lot of dumps. Let's look at this one. So, we uh as you can see, they label them by date. So we are in 2026 which means this is the one that we created. Today is March 20 which is correct. So this is the one that we created. Great. Now we need to take this dump and move it to the virtual machine. As I mentioned you can if you don't want to use curl you can use one of their SDKs. If if you go to this page I just searched for create dump and I got I got here. If you click on curl, you can get as you can see they have a lot of programming languages. Let's select Python. And as you can see when you create the client, you can just call the create them. Uh the response will be a task u uh same same as what we got in the terminal just by using the curl command and after that you can just wait for it to finish. In the beginning it's going to be incued but after uh a successful execution it's going to return succeeded. Uh so they talk about authorization. So yeah, everything you need to know about uh how this works. Now let's go back to the terminal. We need to move this to the machine. Uh so let's use the SCP command. Let's paste the name of that dump. I will use my alias, my website, and I will put this uh temporarily in the temp directory. Okay. Okay. Great. So that worked. Uh let me copy the name again. I'll go back to the virtual machine and let's find that if if the dump is uh inside the temp deck directory or not. So let's use ls temp and let's paste the name of that file. And as you can see we have it great. Now we need to move this dump. So let's uh go up. I will remove ls and replace it with mv to move this file into var lib mail search. As you can see, I cannot do this because I don't own that directory. So, let's use sudo. Okay, it's it's there. And again, don't forget to use ch own. Uh let's search for it. Hit ctrlr. Start typing ch. We found it. Now hit enter. Let's verify that the permissions are correct. If we copy this, let's use ludo ls-la and paste that directory. Let's see. Yes. So this dump is owned by mail search. We could have created a dumps folder, but for now this is temporary because we just want to transfer the data once and remove this file. We don't we don't need it later. That's why we we didn't create the dumps folder inside the virtual machine. Now let me show you how to run me search as a service because right now if uh it's not it's not running we need to manually do it and if a problem happened that cause many search to stop we need to SSH back to the machine and rerun the command again and this is not good. We want the system to do this for us like when we used supervisor J unicorn to manage the back end uh the fast API back end for us without doing any work. So this is what we are going to do now. We will use the mini search system user to run search as a service. Here we are going to use systemd to create the service. We are not we are not going to use supervisor like we did with J unicorn because uh systemd sits at a lower level. Let me explain this. Uh I have a slide that explains this. So here we are talking about systemd versus supervisor. As you can see we have many layers and here I'm just showing the operating system level and the application level. So operating the operating system level is lower and here we have the services that are running and system D manages these services. We are going to create a service that we will call mini search and the goal here is that we want system D to start this service whenever we reboot whenever a mini search crashes. Uh so yeah systemd will be the manager and we use systemd in uh when we work with things that are low level at the application level for example with our backend API we use supervisor for that. So yeah this is the difference between the systemd and supervisor. Okay now let's go back to the terminal and let's create this service. If you want to learn more about systemd, you can just type systemd and they have here their website where you can learn more about it. It's really awesome and uh it's also easy to use. Let's go back to the terminal. Now we need to create a secure key. Uh if you remember here when locally I am just using this dummy master key that uh yeah the med search team uh shows you uh in their documentation but only do this if you are working locally because no one will steal your data because everything is local in production. We need to create a secure master key and that this is what we are going to do in uh at this stage. Okay, let's go back to the BM. Uh, in etc., we need to create millarch because we don't want to put the master key in the service file. We want to secure this so that we can just call a variable that will contain the master key. And we are going to do that here in this search directory. And inside we will put uh an env file where we are going to create uh two environment variables. I'll put them here but we will change the values. And by default me search will search for any environment variable that that uses this prefix melee. So here the environment is production. This is important. And now we need to change this placeholder to a secure key. You have many options to generate a a secure uh key. Uh one of the options is to use OpenSSL. So when you run it, it gives you this random number that you can use. So yeah, just do this. Copy it and put it there. I will do this and I will come back. Okay, I have created the key using the open SSL command. I have opened the env file and I have pasted that key there. I'm not I am not going to show you the key and you should also do the same. Keep your keys secret. No one should see them. Now we need to make sure that the me search user is owning again these files because we are going to use that user in the service. We know how to do this. We are going to use the chall command and yeah we want to own this file. We also we are also going to change the permissions. Uh I think by now you know what this what 600 means. It means that for the search user we wanted to be able to read and write from this file but we want to block access to any other user. Let's just do ls-la to confirm this ls-la. As you can see everything is prohibited. So we just uh the mail first user will be able to read and write. won't be able to execute. But uh and also for the the group and the other users, they are blocked. They they don't have the permission to even see read this file. This is very important because we want to keep things isolated. We don't want anyone to touch the NV file or to read the secrets from there. Now we are ready to create the service file. And as I said with systemd it's it's really easy and this is how we do this. We are going to create a service file. You can name it whatever you want but at the end you need to add dots service and you put it inside the systemd folder. I will paste this text and I will explain what it does. And again uh we have two options. The first one deals with users who have data and they extracted it from their local uh med search instance like what we are doing now. And the other option is for users who don't have uh data and they just want to use the the JSON file that I provided. I'll come back to that to that second option. But for now, this is what we have in a systemd service. We need to provide a description. So here um provide something that is that explains what this file is doing. So here we are going to run the mail search engine and here what does after mean? This line tells Linux do not attempt to start this service until the server's networking stack is fully active. This sometimes is important for things that need internet access. But if you if you are running something that doesn't require it, you can remove this. This is what it means. So if you want your service to start maybe after some other services you you do that uh here. Now we have the service. So the type here is set to simple and here is where we put the user. As you can see we are allowing only me search to run this service for us. This is why we were creating all these env files. Uh this is why we created this working directory and we made sure that me search is the own is the is the owner of uh these files and folders that we created. Uh let's say that someone hacks your server. They won't be able to look inside for for instance the env file. This allows us to isolate media search as much as as as possible. Okay. Uh so I have this comment here. uh we will load those environment variables. The working directory is here and here we have the execute start. This is the command that will be executed. As you can see we are pointing to the M search binary. We can uh so here I'm putting the the full path but you saw that we can just use M search and it's going to be recognized the database path. So here M search will store all its data in the data uh folder. Here we have dump directory is dumps like uh what I have here locally and these two lines are what we want or this is yeah this is uh what we what you will use if you want to import the data that you exported locally. So here the argument is import dump and here you need to specify the the path to that dump file and this ignore dump if database exists is very important. So if you have data in me search that will raise an exception because this might cause me search to override your data. So just for safety it's going to block it here. What you say is ignore this. So if you have data in me search please don't import this. We don't need it. And finally uh we have this is very important restart on failure. As I said if me search failed for some reason no problem. We don't need to go to the machine and restart it. System D will do it for us thanks to this line. As you can see the the service is easy. I just need to change this placeholder. I need to find the name of the dump and put it here. So here is the name. I'll copy it from here. Let's go back and let's paste it. Let's remove this placeholder. Let's paste this. Save. Exit. Let's run damon reload and enable. So after creating a service, we need to enable it. Uh so it created a system a sim link. That's good. And now we can start it. Okay. Now let's go back and let's do status cuz status will show us if something bad happened. And as you can see, yes, something bad happened. So here it says failed. This is not a problem. This is part of the process. Uh don't worry about it. Okay, let me show you how to debug this. We can use journal CLL the name we put the name of the service and here 10 minutes ago. So yeah, it should be fine. Uh let me remove this. Okay, so it tells me that I am not able to see uh any message. Let's add sudo. Okay, now that works. So this is what we have here. As you can see it tell let's see it says failed to execute user/loc/bin/aily search. Okay so that means it wasn't able to run the binary. Okay let's see why. Let's type- la user local bin and me search. Ah okay. So here the problem is that uh we don't have the uh execute permission. So execute is given just to my user but maybe search is is going to be in the others section. It's not going to be in my group but it's going to be here. And as you can see we don't have X. So we need to provide that. Okay. So this is the issue. Now let's change this and let's use sudo ch mode plus X. Now let's go back. Okay. Now now now it should be working. Now it should work. Uh in order to restart the service we use systemctl restart. Let's look at the status and voila it is running. So it says active running. Awesome. So now let's see what we have here. So here it gives us some info. It says the instance UID. Here it says that a master key has been set but it shouldn't show it. Yeah it's not showing it. So request search won't be authorized unless you provide an authentication key. This is the ex this is exactly what we have done here locally when we when you try to make a post request or something you need to provide uh a key to be able to communicate with the server. So this confirms it uh and yeah they have some other info uh like documentation some stuff like that but what's important is what we have here. So here we have starting one workers uh locally uh it said starting 20 workers. Uh let's verify that. Yeah, it says starting 20 workers because I have a lot of cores. If you we look at the memory, let me show you and resources. As you can see, I have 20 cores. So that's why locally it it's going to use all of them. But we are using a poor VM. It doesn't have a lot of CPU cores. That's why it's going to work with what we have, which is one core, and that equals one worker. Uh yeah, it's it's working and it is listening. If we go here, it's listening on port 7,700. Awesome. So that means it's working. So now just hit Q to exit and let's continue. We have talked about option one. But now let's deal with the with other users who don't have their local M search instance. This section is is made just for those people. So for the other ones who have their own local instance, they can skip this section if they want or they can stick they you will learn um you will learn a lot. First let's verify if I have the JSON here. Uh let's look at web app. We need to go to back end and yeah I have the seed folder. Uh let's look at it. And we have documents.json. Awesome. You can read the readme if you want. Let's let me show you the readme. And here let's look at and it's it's going to tell you what you need to run. And here you you are not going to index them manually but I have a uh I have created a script for that. I called it see the mainly search and it is here. It is located inside the scripts folder and this will uh yeah will index the documents for you. Let's go inside back end or yeah what's what was command? Yeah, let's just take it like this. I just need to go inside web app. And now I can run this. Okay, it didn't work because I need to activate the virtual environment. This is the command source van bin activate. No such file or directory because I need to go inside the back end. Uh let's do it now. Okay, as you can see, we have van which means that we were able to activate the environment. Okay, now let's go back to this command and let's remove back end because we are inside that directory and let's run this. Okay, let's see. So here this is just a text see the mainly search with real data. Okay, here is the art the indexes we are going to create. Okay, so here we are trying to connect to mainly search. Uh so here it says index articles already exists because I have imported the dump successfully connected. Uh but here we have an error but it says that we have an invalid API key. That's understandable because I have created a new master key. So let's see what we have inside this file. Let's use nano. Oh sorry uh I need to keep the full path. Let's use nano. Okay. So here we are using I have a settings. I have a configuration and from that configuration I load the API key. So as you can see here we have API key is set to settings dot mainly search master key. This is why. So here I assume that we are loading a default master key. Uh okay that's why it didn't work. Let's see. So let's go inside settings just to show you what what we have there. Let's go inside core. Let's look at config. Yeah, as I told you, we are using the default master key. That's why it didn't work because because we are using a strong master key. And here, let's see. So, we have an environment variable. We have an environment variable that we can open. And there we can put the new master key. Or if you want, you can do it temporarily. You can just change this and the script will work. Let me change it inside the env file. And the env file should be here. Let's go back to back end. Let's use ls-l and let's see do we have env. Okay. So we just have env.example and from that we need to create env. Let's use cp env.ample and we will create av file. Let's open that file. And as you can see this is what we have. The environment is set to development. We can change that to production. Mainly search URL is correct. The index is correct. But I'll create um uh another one. Let's call it articles too because as you saw from the logs, we already have articles. And here I need to change this. And remember use the same key that we have put inside the env uh inside the env file. Uh let me show you. So we have this file where hopefully you have created a strong key and you put it here. I'm not going to show you my key but I will just open the file again. Copy it and paste it inside this env file that we created in the backend folder. I am back and I have changed the master key inside thisv file. Now we can try to run the Python script again. Let's do it. So, Python scripts seed med search. And as you can see this time, we didn't get any error. It says this is the index that we are going to create. It created it. It successfully connected to my search. And here, as you can see, documents were added successfully. Awesome. And I will show you later how to verify this with a dashboard. We can do it with the with the curl commands or the SDKs, but it's going to be very easy with a dashboard. Now, I want to do something. Uh let's go back to the service file here. We have we imported the dump successfully. Now, we don't need it. We can just get rid of these two lines because I as I said, we import the dump only once. We don't import it every single time. We made we made sure that we have the index the documents were there and yeah after uh verifying that you can safely clean this service by removing those two lines. Now save the file exit. We need to tell the dam that we need to reload the damon because we have we changed the service configuration and we also we need to restart the service itself. Awesome. Remember that we can access the web application using our IP. If you don't remember the IP address, you can always come back to Digital Ocean and find it here. I'll copy it. I'll paste it here. Let's go to the front end. So it's going to be sorry I'm not sure why I did that but let's just put the uh the IP address because we are using engineext and engineext will take care of uh routing the traffic but what why I want you to come back here because last time if you remember we weren't able to yeah we weren't able to talk to the to get articles from the back end because me search was not set up Now we have M search but we need to connect it to the back end in order to get articles cuz right now as you can see we we don't have anything. Now we just need to restart supervisor because it doesn't have the new environment variable. This is why it's not it's not able to connect to the me search server. And to do that we are going to use supervisor DTL. If you don't remember the project name that you used, you can always type status and that will give you the pro the yeah the name of that project and currently it is running and it was running for 10 days without any problem. Now we need to restart that. So let's use restart and let's put the name. You can use restart all but if you have many websites on the same server uh it's not a good idea. So it's always great to yeah just restart what you want. Okay. So it said stopped now it says it is saying started. Let's also follow the mini search service logs. This is what we have. It is saying invade the API key because that's when we run supervisor we use an old keys in the NV file. But hopefully now it should it should work. Uh let's verify this. So I am here. I'll refresh the page and voila, as you can see, I am able to get my articles and if we look at the logs, yeah, this is the latest request that we got. It's a post request. It's coming from Python because I am using the Python SDK and the status status code is 200 and as you can see media search is fast. Uh so it was able to do this in less than 100 milliseconds and also the benefits of self-hosting misarch is that the communication is local because my back end is in local host and misarch is also uh in local host. If I hosted misarch on another server maybe uh that will add some latency but here everything is inside one box. Awesome. Let's also verify other stuff. Let's go to the homepage because we should see some blogs here. Yeah, that's correct. Let's all let's click on maybe this one. Yeah, let's click on this one. We have the content. We can also add some claps just to verify that it the data is saved. We also get recommendations which is great to see. The number of claps should have changed from 0 to five. Awesome. It is working. This is really great to see. We also have some filters. We can sort by popularity. So this sorts by the number of views. We can change that to ascending. So we have seven 7 11 29. Uh we can change that to um number of claps. So these ones these these articles don't have any claps in them. But this one has the most number of claps. It has 15. This one has six five etc. We also can filter by year. And we have yeah we can filter by tags. And here we are using faceted distribution. Now we can let's go back to the machine and let's stop this because we don't need it. And as you can see the the request that we were performing were all successful. They all returned 200. That's all I wanted to share with you in this first subchapter in module 3. You now have a working uh mini search server hosted on your virtual machine. We connected it to the back end. We created a server, a service that will handle managing the mele search service for us. And we tested it in the front end and it worked wonderfully. In the next sub chapter, you will learn how to maintain this database. I mentioned that we can use the Python SDK or just plain curl commands, but it's not enjoyable. I'll show you a better way to maintain your server by uh connecting it to a uh goodlooking dashboard. We are also going to set we are also going to set up M search to automatically create daily backups for us in order to protect the data and we are going to use SSH tunneling again so that we prevent opening firewall ports and this is important because we want we don't want to host the dashboard. You can host the dashboard on your virtual machine but we in our case we don't want to do that. We just want to connect to it. See you in the next sub chapter. Welcome back and today we are going to manage your med search database securely. We are going to use this amazing open-source web dashboard. You can click on this link. It will take you directly to GitHub. Here it is. Uh you can give them a star. uh clone the code, run it locally if you want to, but the project is really awesome and I use it to maintain my database that I am using on my production server. There are other dashboards, but for me, this is the one that I liked. And this is how it looks. In your homepage, you can give your instance a name. It shows you the database size, status, and the version of the mill search binary. You can also create a dump, see the tasks, create keys and view your indices. It's really awesome. These are some some of the features that are present there. You can search documents, upload ones, edit the fields. Uh and all of this is done visually. You don't need to run any command. And again, in order to connect to it, we are going to use the same trick. We are going to use this amazing SSH channel feature so that we don't open open any ports. And this time as you can see we will have we have mill search running on the server. It runs on this port and we want to be able to access or to manage this using this dashboard. The dashboard is going to be running on a browser on our local laptop. And with SSH Simon, we will connect the ports 7,700 from our machine to uh 7,700 on the server and they can communicate freely. But if when we stop the SSH connection, not we can't manage the med search instance. We already worked with SSH tunneling. So it should be uh very easy for you to reuse again uh in this sub chapter. Okay. Okay, now let's go back to the virtual machine. I want to start with automatic backups. So, let's open the server or the service. I want you to add two lines here. SK schedule snapshot and snapshot directory. Here the value in schedule snapshot is in seconds. I set it to 86,400 and this is exactly 24 hours. uh if you want to save every hour or yeah if you want to change the duration feel free to do that just change this value and we want to save data to the snapshots directory but currently we don't have it so we need to create it and also I believe we should also create the dumped directory uh if you want for to to create some dumps yeah let's do this let's save the file we uh we can create the snapshots directory like this And let's also create dumps if we don't have it. Okay, great. And now let's change the ownership. I will change everything inside my search, not just snapshots because I have also created dumps and I made a typos. Uh let me show you how we can do this. We can use move. Let's use sudo move. Let's take this path and let's rename it. Instead of dumps, it's going to be dumps. Uh let's just verify that it it's worked and let's look at me search and yes we have dumps we have snapshots and data and we can delete this dumps file because we no longer need it. We used this we imported the data now it's it's not useful anymore. So let's use sudo remove and let's put the file there. Sorry. Okay. So let's go back. Ah I wasn't able to delete it. Why? Ah, I did sudo ls. I need to do sudo remove. Now let's go back. Awesome. So everything is there. Let's just verify the ownership. Who owns those folders? M search. Awesome. Now we can continue. Whenever you update the service file, make sure to run damon reload and reload the service itself. Now, Med Search will quietly save a safe copy of your database every single day. And I want to mention that we will keep just the most recent snapshots. We won't have a lot of snapshots because that will uh fill up your hard drive over time. You we just need one and that's uh that's sufficient. Let me exit and let's use this command. SSH-L7,700 to 7,700. This will will create a tunnel. Ah, so it says address already in use because I am running Milly Search locally. So I need to stop that server and re retry again. So let's retry. Okay, now it worked. It didn't uh it didn't tell me that the port is occupied. Now let's go back to GitHub because as I mentioned you don't need to host the dashboard locally because this the creators of this project are already hosting it uh on Versel for free and as I said I am using it uh both locally and on my production server and in order to add your own instance you can just click on the plus button give it a name I will call test instance. The host is correct. It should be 7,700. This one is pointing at your local port. And here I need to provide the API key. For that I need to go back here and um copy the code the API key. Again I have copied the key. I have pasted it. Now I click on confirm. Okay. I can save that but doesn't it's not needed. Now just click on your instance. One thing the me search server should be running. So you can always run status if it is if it says running you are good to go. And as you can see I uh here are my indices. So I have articles articles number two and articles test. This was also on my dump. And I have everything. Now I can click on let's say articles. It shows me the distribution. Uh we can click on documents. And here it shows you the fields. You can click on updates. Let's say that I want to update the view count from 14 to something else. I can do it without any problem. So maybe let's just do 999 or something. Submit. It's going to create a task. We can look at that here. So it's it says a few seconds ago and it succeeded. Uh let's go back to here. Let's go up. Let's go to blogs. Let me see. Do we have what was that blog? Let's go back. So, it says elastic search change heap size. Elastic search change heap size. Do I need to refresh? Oh, no. Maybe I used the wrong index. So, let's go back. Okay. Let's click on articles. Yeah, I think I am using articles too instead. Uh, let's look at documents. Okay. Yeah, let's change this one. So, update from 7 to 999. submit. Go back to the website. Refresh. And as you can see, we have 9999. With this dashboard, you can easily manage uh your documents. And here, if you want, you can upload once. You can upload your documents here. Just paste the JSON and hit submit. And that will uh add a new article or product, whatever. Uh for in my case, I am using it to add articles. Uh so that's my content. But if you use it for other purposes, it doesn't matter. You can just just put your JSON here and hit submit or you can drag and drop the file directly. Uh let's let's look at settings here. Uh these are these are the the settings. And this is very important. When you create a dump, the settings needs to be the same. And just like that, we finished module number three. As you can see, using me search is very easy. It's just one binary. It is really fast. uh it's uh we have really awesome dashboards that we can use to maintain this database. We tried to create a system user to isolate the database as much as possible so that we don't leak data, we don't leak keys, stuff like that. And even if someone hacks to your server, they won't be able to extract anything from it. And we used SSH tunneling to access this dashboard. Which means that if I stop the server as you can see I stopped it which means that yeah port forwarding is no uh sorry SSH tunneling is no longer a thing. That means that yes I won't be able to connect to this instance. So if I click on it it says connection failed because we don't we create a temporary tunnel that allows us to manage this. Once we are done, we close it and no one will be able to talk to me research. So even though it is inside the machine, it is isolated. It cannot talk to the outer world. It just talks to the back end locally. In the next module, module number four, we are going to talk about global delivery and app security. This is important because as you can see right now if uh let's say that you deployed this to production your users will have to type your IP address to access your website. This is not good. We need to change this to be something like this. So so that they can just type a name and in behind the scenes this name will be converted to the uh to your IP address. So this is what we are going to fix in the next sub chapter and we are going to talk about DNS records creating custom domains uh adding certificates to uh finally uh have a secure connection because right now it is not secure it is we are just working with HTTP uh in the next module we will finally upgrade to HTTPS and that will add another layer of security to your website. Welcome to module four. Up until now, our application has been running perfectly. But to access it, we need to type the IP address in the browser. This is not user friendly. In this video, we are going to buy our own domain name. I'm going to show you how to transition from HTTP to HTTPS. We are going to use Let's Encrypt for that. We are going to modify the engineext configuration and at the end I'm going to show you how to autorenew your SSL certificate. This is the agenda that we are going to follow. As you can see, we are going to start with purchasing a custom domain. We are going to configure DNS records. We are going to add an A address and a CNAME record. I'm going to explain these. We are going to install three SSL certificates via list encrypt. And finally, we are going to automate the renewal process because by default, the certificates are going to expire after 90 days and we don't want to do the renewing manually. We want the machine to do it for us. We are going to use a chrome job for that. Before we start practicing, I would like you to go to GitHub. You'll find the links for this repository in the video description. Click on the book folder and come here to the fourth file. As you can see, I have committed the changes. Everything is here. You will find the steps and the commands that you need to run. Everything is described in this document. So, don't worry. Just follow with me and uh the resources are on this repository. Let's register a domain. In my case, I'm going to use pork bun but if you want you can use namecheep or god daddy or other registars. There are many but in my case I will use pork bun because this is the one I use and their pricing is transparent. They they show you the real price. Sometimes the other ones they show you a very low price but the problem is that when you try to renew in the next upcoming years they the price becomes higher. So that's not good. And here in pork bun as you can see you can type the domain that the name that you want to register and if it is available you can uh you can take it. I will I will sign in because I already have an account and I will create a I will try to register a new domain name. I am back. As you can see, I have logged in with my account. I already have two domain names. This is the one that I showed you in the beginning of this course. If I go there, it takes me to my personal website. And as you can see here, uh, if you look at at the bottom left corner, the link is HTTP. It's insecure. But when I click on it, it's going to use HTTPS because we reconfigured EngineX so that it takes HTTP requests and redirects them to HTTPS. And as you can see, if you click here, it shows you that the connection is secure. And this is what we are going to do with the new domains that I will create now. Okay, I will close this. This is the domain name that I would like to register and I want a com one. Let's search and let's see if it is available. Okay, let me zoom in so that we can see clearly. As you can see, I have typed the name here and look what we found. As you can see, I can I can get the domain for $11 per year. And this is what I was talking about the renew process. As you can see, the renew is the same as the price that I will pay in the first year. But if you if you look at this one, I am going to pay just under $2. But after this year, I will be paying almost $30. And this is why I mentioned in the beginning that the pricing here in pork one is transparent. You can see everything. In my case, I will take the com one because yeah, this is what I want. And I want to also get ma but sadly yeah this one is uh country specific because I live in Morocco. I would like to also take that one. But yeah, you can try your luck. Uh maybe if you live in another country, you can try it. Maybe you will find uh yours here or maybe use a different uh register. Uh I know that I will not find that one. So I'll just skip and in my case yeah I think this one is is fine. So now what we will do we will click on the plus button that will add it to the cart. Now click there and here again pay attention to what they are showing you here. So the registration here is for one year. I want to pay just one year. Uh after that they will notify me to pay for the the second and the upcoming years if I want to stick with them. So the renewal is the same price. That's correct. And here they are giving you other things. They always do this so that you that you pay more. But don't don't add anything. These are the things that they show you here are not are not important. Okay. So this is uh this looks good. Now all we need to do is to scroll a little bit and here click on continue to billing and finish the payment and you can also ver verify the price before doing this. I will click on continue to billing. Here you have many options. You can use PayPal if you have it, crypto or your Visa card. I will select PayPal and I'll I'll finish the process and I'll come back. I am back. As you can see, this is the message that you should see after completing the purchase. They will tell you great success. Congratulations on your brand new domain. That's uh that's good. Now, we can go back. We can click on account domain management. And as you can see, I got my new domain. Uh they say that it will take some time. That's fine. We'll come back to it. Now, we need to configure DNS domain name system. But before I do that, let's go back to the slides because I want to explain DNS how DNS works. I created this diagram to explain this. Let's say that I am this user. I want to check this website. I will type it on this browser that I have here. I will hit enter. What happens after I do that? The browser will talk to the DNS server. In our case, it might be pork bun. It might be cloud player. It depends on what you want to do. For right now, we have created we have just bought a domain and pork bun. They have their own DNS server. Behind the scenes, what will happen is that we need to to give Porkbun a an IP address. And this IP address is the one that we that we can take from Digital Ocean. It's the machine that we created in the first module in this course. And this will will allow pork bun or other DNS servers to create this mapping. They will map the name the domain that you have purchased to that IP address. After typing this domain name, the browser will tell the DNS server where is this address. The DNS server will look in in its databases. It will if it finds it, if it finds that address, it will reply with the IP address and the browser will type that IP address automatically. It's the same. Before we were typing the IP address directly, but now the DNS server will give that information to the browser. Now the browser has the IP address. It will communicate with your Linux server. So it's going to tell it please give me the content and the the server will reply with the content and you will be able to see the the front end that you built. This is how DNS works and this is what we are going to do to create this mapping. We need to configure the domain that we bought in porkbun. We call this mapping the a record. Let's go back to porkbun and let's see if we can change this. I'll refresh the page. Okay, everything is is all right. As you can see, we have DNS here. If you click on it, it allows you to modify it. As you can see, we have uh a few default ones. Yeah, and I think this is a newer one. I will I'll go back to the classic view because this is Yeah, this is the view that I used here uh on GitHub. Let's uh let's see. Yeah, as you can see, before it was like this. And yeah, we get a model. So I I'll stick to the old one. You can see that we have a few records. These are default records that Porkman created for us. We need to remove some of them. I will start by removing the alias and CNAME by clicking on this trash icon. We will also remove the last two, these text ones. That's what we want to do. Now scroll up and here let's add the first one is the address record. Here host leave it blank but here in the answer value we need to put the IP address. Let's go to digital ocean because I forgot the IP address. Okay, here is the the droplet. Here is the IP address. I copied it. I will paste it here. Here we have time to live. Leave it at 600 seconds. Um, you can add notes if you want. And now click on add. Okay, let's scroll down. As you can see, we have the new record here. That's great. Now, let's add another one, which is going to be CNAME. This record maps one domain name to another domain name. You use this one to ensure that if someone types www in front of your domain, they can still reach your website. So this is going to map www to uh your domain name. I can show you this with my own website. If I type my domain name, I can go there. That's fine. But I can also do let's remove this part and type ww dot. And as you can see that redirects me to my yeah to my website. And it's it's working. To accomplish that we add we add the CNAME record. Here in host we add www. And here in answer you need to type the domain name and we can just copy it from here and paste it and hit time to live. We shouldn't change that. Now just add this record. Let's scroll down. As you can see we have it here. This is the mapping that I was talking about. If I type this domain name porkbang will return this IP address to the browser. So that so that it can communicate with my droplets and that's it. We configured DNS as I said I used pork bun but what we have learned here applies to the other domain registars the only thing that will change is their user interface but other than that everything I think should be should be the same. We need to SSH into the server. Let's do this. I will use this command to SSH directly. Great. I am now inside the machine. We need to configure SSL certificates. How do we do that? First, we need to install the searchbot client. To do that, we can type this command. As you can see, it installs searchbot and this Python 3 searchb engineext. Uh this is a plug-in that allows searchbots to modify the enginex configuration for us. We will see that in action but for now let's just install this. Provide the password and let's wait for a few seconds. Okay. Now let's request certificates for this domain and subdomain. As you can see I have placeholders. I need to replace them with this domain name. I will take it and paste it here. And uh here I will keep the www do and I'll paste the domain name. Here I made a mistake. I need to remove the.com from here and from here. Let's hit enter. The first thing they are asking for your email address. This information is used strictly for urgent security notices and renewal warnings if uh maybe the automation fails. After that, they will ask you if you agree with their terms of service. Type yes. And for this question, they are telling you, do you want to share your email address? Just type no. The account is registered. And now they are requesting a certificate for my domain name. Let's wait for a few seconds. And okay, this is the message that we want to see. Successfully received certificate. And as I mentioned the certificate will expire after 90 days. Today is April 4th and 90 days will be yeah it's 3 months. So yeah 4 + 3 is 7. That's correct. But don't worry we are going to automate the process of renewing the certificates. Let's open the engineext configuration and let's see if searchbot changed the changed it or not. Hit enter and let's scroll down. It seems to me that it's it's the same. Source B did not change it. That's not a problem. I wanted to show this issue because as you can see in the previous chapters, we didn't have a domain. That's why we created just a dummy name here. But the new domain that we created is called this. and searchbot didn't find something uh there. That's why it's it uh yeah it couldn't change it. We can see that search bot because it couldn't find the domain name inside the the configuration. It created the default configuration. Let's remove that because we don't need it. Now open the configuration that we created before. And here we need to update server name. As you can see before because before we didn't have a domain name, we used the IP address. But now we need to change that. Let me show you how we do it. I will come here. Let's hit Ctrl K to remove that line. I will replace it with this one. Server name stays the same, but here we replaced the IP address with the domain name and the subdomain name. Save the file. Exit. Let's ver let's verify that the syntax is correct. Always do that before reloading engine X. And now I will search for reload engine X. And to search you just type something, hit CtrlR and that will show you the previous commands in the history. Hit enter. This is going to reload EngineX because whenever you change the configuration, always check the syntax and after that reload. Now let's run searchbot again with the same command and this time it will find the configuration because we ran this before. Searchbot is asking us if we want to reinstall. If we want if we choose one that will attempt to reinstall and two will just renew. We want to reinstall. So let's type one and hit enter. As you can see this message shows us that third boss was able to find to find the our custom configuration. And now if we open that file, we should be able to see the new stuff that SB added. Let's scroll down. Great. As you can see, it worked. We have some new lines. Let's remove them. Let's go up. Yeah, everything here stayed the same. We have the server name. We We have log files. These are the HTTP headers that we added before. Here we are serving the front end. We serve the API. That stuff didn't change. But here, here is the new stuff that search bots added. And as you can see, this comment proves that we have listen 443 SSL. We have some SSL certificate stuff. Let me explain what we have here. Before in the server block, we were listening for port 80. Searchbot migrated that to port 443. And for this port is the standard port for HTTPS. You can search that on the web to verify that information. But port 80 is for regular HTTP and it is unencrypted. That's why it is it is not secure. Great. So that's nice. And here um these these new lines just point to the to the keys that search B created for us. We are including this configuration. Yeah, this stuff it's not that important because it is managed by search. The most important thing here is 443. It will make our website secure. Now if we scroll down, we have another server block and this one is used for redirecting. Here we have two if blocks. If the host is this subdomain, we are going to redirect to HTTPS and 30. Let's search for it. 301 means moved permanently and it is used for redirection. And this is what we want to do here. If someone visits www do we want to redirect them to https and here we put the domain name and again this is managed by searchbot and here if we type just the domain name we will catch that and we will redirect to this link and as you can see here for this server block we are listening on port uh port 80 which is the regular HTTP which is not secure. Yeah, when we catch that traffic, we will redirect it to HTTPS. Awesome. Now save the file and exit. Now let's automate the certificate renewal. Let me clear the screen. We have this command that we can run to test the renewal system. We want to do this to ensure that it works. And we are adding dry run because this is just a test. It's not going to change anything. Let's verify that this is going to work. They found that the account is registered. That's good. And as you can see here, they are saying simulating renewal. So this is just a simulation. And they are saying congratulations all simulation. All simulated renewals succeeded. This is what we want to see before proceeding. Now let's create a new chrome job. We are going to type sudo chrome tab-e. We want to do this in for the root user. This is the first time we are doing this. So the this command is showing us what text editor to use. I like to use nano. I will type one and hit enter. Now let's go down and let's add this line. If you don't understand the chrome tab syntax, you can use this website called chronab.guru. And here you can paste the chrome tab notation. And this is the one that I added in the chrome tab. Why I like this website is because they show you they try to convert this into a sentence that you can understand. And this expression means at exactly midnight every Sunday. And this is the command that we want to run every sun. Every Sunday we want to run the renew command. And we are adding quiet because we don't want to generate a lot of system logs. We want to do this quietly. Now save the file. Exit. Good. Now Linux will run this job every Sunday. And again, as I said, you don't need to do this manually. The system will take care of the renew process. But we have a small problem. Let's say that before the expiration of the certificates, we were able to automatically renew it. Engine X will not be able to see it because we need to reload it in order to load the new certificates. In order to solve this, we will create a post renewal hook. I will explain what that means. Let's create this file. Look where we are creating it. We have etc. Let's encrypt. This is what we are using for the certificates. And here they have a renewal hooks folder. And we want to do this in the post. So after renewing this is what post means after an operation. After that we are going to reload engineext. This is a simple script that will run this command for us whenever the certificates are updated. Save the file and exit. But don't forget to make it executable so that searchbot can execute this. And you can always verify if that works by using ls-la. And what you want to see is X in yeah three positions and okay you are now set up. You can just kick back and enjoy your sleep. I think we have finished the full setup. Now we can test if this works or not. Let's go back. You remember that before we used to go to Digital Ocean, copy the IP address, paste it here in order to uh see the the website. Now this does not work because we have uh updated the engineext configuration to look for the domain name. Let's do that. I will or I can do it from here. I can click on this and let's hope that it works. Yes, it did. As you can see, I have typed the domain name. This is my website. Uh as you can see, it is working. Uh the front end and back end are both connected. Let's load an article. Let's scroll down a little bit. This is a long article here. I can add some claps. Let's go up. Let's ensure that this works. Yes, it says 11. That's the new claps. Yeah, everything is working. And we can also try to force this to go to HTTP. Uh let's use that. And here I will type the domain name. What will happen? This will go this will redirect to HTTPS automatically. Boom. That worked. As you can see, the connection is secure. Awesome. This is great. Uh, that works. And we can also verify www do. Let me remove this part. And let's hit enter. And that also redirects without any problem. Amazing. Now we have uh successfully created purchased a domain. We changed the DNS configuration. We added the A and C name records. We used let's encrypt to make the connection encrypted. But we are not done yet. In the next subchapter, we are going to talk about CDN layer. Why do we need to talk about that? Because the problem is that for me, this droplet that I created is hosted in Germany. I live in Morocco. So if I want to access this website, it's going to load fast. But if let's say that someone is from China or Australia, if they want to to go to my website, the load time is going to be very slow. They need to wait for seconds instead of milliseconds to yeah to load the the content of the website. That's a problem. We are going to fix it in the next sub chapter with the help of Cloudflare. We are going to use it to cache the static files on their servers because they have servers all over the world and that will mitigate this problem. So that person in Australia that before was used to wait in seven or 8 seconds for the website to load, we now only wait for maybe a second or less. And that's what we are going to focus on in the next uh video. Welcome back. In this video, we are going to talk about the CDN layer and caching. Here is what we are going to work on. In this video, I'm going to show you how to route traffic through a content delivery network. There are many, but we are going to use Cloudflare for this tutorial. We are going to configure strict end toend SSL encryption. I will show you how to unmask the visitor IP address in engine X because now Cloudflare will sit between the user and engine X. This is an important step that we should not forget. And finally, I will show you how to implement caching strategies for single page applications. Let me show you why we need to rely on CDNs, especially for applications that don't require a backend. Last time I bought this domain to demonstrate how to link your domain to your VM. And as you can see, the application loads and uh here is my website. You can use this free utility that allows you to check the load times. Here you can test. Let's test this website. Keep from all checkpoints and click on start test. Here is the results that we got. As you can see, this shows us the result. Shows us a lot of information. But as you can see, it tests the load time of our website in different locations. You can see that here Frankfurt is in Germany because I am hosting this server on a virtual machine in Germany. That's why the load time is basically zero. But if we go um let's see. So here the the worst load response time is almost 5 seconds which is in China. It makes sense because the distance between Germany and China is very big. We we will also see here u yeah as you can see we have Japan we have yeah other countries that are very far but the the countries that are countries that are close to Germany uh especially in Europe Africa those will uh see very uh very low uh load times. This is a good tool that you can use to test how fast your your website loads for users. I wanted to start with this because it shows the problem that we currently have. And if you want to solve this, one option is to host your website on many servers. For example, you can host one maybe somewhere in Europe. That will give people in Europe very high load times. Maybe you put another one in um USA, you put another one in Africa, another one in Asia. But that will drive the cost up very quickly. CDNs solve this issue for us. They allow us to serve static files to users all over the world. And here cloud player, this is what we are going to use. They have plenty of servers uh scattered around the world. And let me show you this diagram that I made that explains how this works. We have two situations. The first one is before applying CDN and the other one is after. Let's now look at the first diagram. Let's consider that the server here is hosted maybe here in somewhere in Morocco. And let's say someone here in Asia wants to check my website. The load time will be let's say almost 4 seconds. And this user will see a smaller load time but still it's not it's not very responsive. 1.2 seconds is bad. But if someone is here in Morocco or Algeria, the the website will load instantly for them. This is the problem that we have distance that we need to travel and that takes time. With CDN, we'll solve this by including edge servers. These servers are located close to the end user. This person who lives in Asia will connect to a server the closest server to them. We call them edge. And that edge server will communicate with the main server. And these edge servers are provided or are given to us by Cloudflare. And here they will cache your static files just the front end so that users can load them instantly. And as you can see, with the help of these edge servers, we were able to bring down this 3.9 seconds to almost zero. This is the benefit. And I will show you how to do this in this video. And the great thing about this is that we have the before situation. After applying the test, we will come back here. We will uh start the test again. And hopefully we will see that for example for Beijing it will go from 5 seconds to maybe uh 1 second or less hopefully and uh we will see that at the end of this video. Now go to cloudplayer.com. Let me zoom out a little bit so that you can see. And here make sure to create an account. If you don't have it you can loging in for free. They have a free tier which we we are going to use in this video. I will log in and I'll come back. After you log in, you will see a add button. Click on it and choose connect a domain. Here I will type my domain name. I will choose this one. Keep import DNS records automatically checked. This will allow Cloudflare to automatically fetch the A and C name records that we created uh in pork bun or in your preferred domain registar. Here we have a section uh about AI crawlers. If you don't want AI companies to scrape your websites, select block on all pages or you can select the other options. I don't want them to scrape my content. So, I will select this. And here we have this option that will instruct AI bot traffic with robots txt. You can read more about this. Uh it's an it's a setting that Cloudfare will add to robots txt to block AI companies from scraping your website. As you can see, they have a few few options. So, they have AI input, AI train. If you want, you can read read it here in the documentation. I'll keep that enabled and I'll hit continue. As I mentioned, they have a free plan that you can use. They also have other plans. If you want to scale your website, you need to move to maybe the pro plan. But for this tutorial, the free plan will be enough. And here they are showing you the how much you will pay monthly or yearly. We don't care about that because yeah we will be on the free pan anyway. This step is very important. Cloudflare here is listing the DNS records. It's found on pork bun. Here we need to verify the status. Here you can see that we they show you two icons. Uh so if the icon is orange like we see here, it means it's proxied which means traffic will go through the cloudflare network network. And this is what we want. Here we have the A and C name records. They are pointing to the correct values. And here the proxy status is proxied. This is correct. This is what we want to see. Email traffic uh for the MX and TX records. Here you want them to be DNS only. We don't want we don't want Cloudflare to proxy them because Cloudflare does not proxy email traffic. If you do this, your email will stop working. So, please keep it as DNS only. And if you have any NS records, in my case, I don't have them. You need to delete them. You will have this delete button. Just click on it and delete those records. Uh I can show you how this looks in the documentation. Click on the book folder. Come here to uh the fourth module. Search for NS. And yeah, here is the page. You can see that here if you see these NS records, just click on the delete button to remove them. Okay, now we can go back. I'll click on continue to activation. And if you are wondering, there is here a warning that tells you this host name is not covered by a certificate. This is not a problem. We created a certificate in the previous tutorial. We used Let's Encrypt for that. So yeah the and as you can see the website is secure we have um as you can see it's verified by let's encrypt just click on continue to activation and at this step we need to connect to porkban because we need to uh update some stuff I'll I'll log into pork and I will come back I am in the domain management page here as you can see we have uh the my new domain I'll click on ns by default We have a few name servers. We need to replace these by the the ones that Cloudflare has given us. Just click on this link. It's going to copy the name servers. Remove these depot ones and start pasting the name servers that uh Cloudflare has given you. And you can follow the steps. You can read here. So they tell you to log into the DNS provider. In my case, it's Porkban. And after that they tell you to find the name servers section. Here it is. And after that add these and delete the other uh name servers. I have done that. Now I just need to click on save name servers and hit okay. Now return to Cloudflare and click on I updated my name servers. In this page click on check name servers now to see if the name servers have been propagated. Uh if you read the text, they tell you that this uh takes around maybe an hour or two or sometimes even a day. But if you want, you can click on check name servers now. And uh as you can see, Cloudflare is saying that they are checking the name servers for my domain. So they are telling me to wait a few hours. But that's not a problem. We can continue the configuration. We will come back and uh verify that everything is proxied through Cloudflare. Now on this left panel we can click on SSL uh /TLS click on overview and here make sure that your domain is selected here I have two uh I will select the the new one that we added and here we need to set this to full strict uh hit confirm. Why do we need to select this instead of full? Because as I said in the previous tutorial, we used let's encrypt to secure uh our website and uh we saw that when we were add when we were verifying the DNS uh Cloudflare was giving us that warning because they don't know if we have a a certificate and uh by choosing full strict cloud player will know that we have our own certificates and will accept it. Now I want to show you a utility called dig. Uh you can search for it. It's it's called as I said dig and you can read about it uh here on Wikipedia. It's a utility that we can use to verify if the internet is seeing the new name servers that we got from Cloudflare. First make sure that it is installed by typing dig-v. It's going to give you the version. If you don't have the utility, you can install it like this on Ubuntu by installing DNS utils and that will give you access to this utility. To use it, you type big plus short. We want to verify the name servers. And here uh I will put my domain name. As you can see, it is showing me the these four that are coming from Porkbun, which means that the new name servers haven't propagated yet. So, we will have to wait for a few hopefully for a few hours and not a full day. You can also go back to Cloudflare, go to overview. Here you should see that this message that tells you that your domain is now protected by Cloudflare. That this is what we want to see. And if you made any mistake, you can always go back here. For example, if you want to update the DNS records, just click there or you can find it here on the left panel. And here are the records. You can see we have all the records and also the name servers. You can also use another website called what's mydns.net. We can do the same thing here. I can put my name uh I can put my domain name here. Here I will select NS because this is what I want to verify. And if you hit enter, as you can see, it is starting to propagate. There are some locations where the Cloudflare name servers haven't propagated yet. But yeah, the it it will take a few hours for uh every location to see the new name servers. Let's go back to dig to see what we get. We are still getting uh yeah we are still getting the old name servers but yeah it's going to uh it's going to show up uh in a few hours with this website. We were we are able to confirm that uh the new cloud player name servers are starting to propagate. Let's go back to this website. I will take a screenshot for this previous test and uh I will rerun it again and let's see if uh things have changed or not. Okay. Uh we see some improvements. Uh obviously uh yeah uh the DN the name servers haven't propagated fully but we should be able to see some uh differences. I'll take another screenshot and I'll put the two images next to each other. On the right side we have the before and here this is after. As you can see let's start with Beijing. Before it was 5 seconds now it is 3.4 seconds and this will keep improving. Um maybe the first time you try to load the website that will take maybe uh let's say 3 seconds but after that the files are going to be cached which means that they are going to load very fast and uh since uh the name servers are still propagating I will maybe I'll try to rerun this uh afterwards uh and just before ending this tutorial and we'll see if things will improve or not. But you can see that there are some locations that improved uh drastically. Uh especially Beijing. Uh let's look at maybe Hong Kong. So Hong Kong is now it takes just 1 second. Soul takes 0.9 seconds. Okay, good. We see some improvements which is a great thing to see. Well, I will come back to this website later. I will leave it here. And now let's go back to the slides. This is uh what I observed on my personal website. You can see that uh I used the same tool and here I I see big changes because I let the name servers propagate for maybe a day before running the test again. For Tokyo, as you can see, we were able to reduce the time from 1.3 seconds to 0.3. So again, same situation. Jakarta, same. and Beijing it went from 4 seconds to 1.5 seconds. This is the benefit of using a CDN especially if your website is doesn't even if it has a back back end uh attached to it there is no problem your front end will load quickly but because we introduced cloud player we now have a problem I have created this illustration to explain you the issue let's say that we have a user this is their IP address now if let's say that they want to check my homepage, the request will go through Cloudflare with the IP of that user. That's not a problem. But when Cloudflare will try to send that request to my application, it's going to send it to EngineX. But the problem is that we are not sending the user's IP. Instead, we send Cloudflare's IP address and this will be sent to the back end. And uh that's problematic if you want to do I don't know if you want to track uh IP addresses. This is bad because let's say that you you received 100 requests from let's say 50 users your backend will see the same IP address and this is the problem that we will try to fix. And luckily, if you read the Cloudflare documentation, they tell you that you need to add CF connecting IP to your EngineX configuration. And that will allow you to unwrap the IP to get uh yeah to get the user's original IP address. And now your backend receive the correct IP and you can store that in your in a database or uh yeah uh use it in your analytics or something like that. Let me show you how we do that. I have connected to the machine. Let's open this file. Let me type my password. And here I want you to copy this from the GitHub repository and put it here. Let me show you where you can get that. Here is where to get this code block. It is available in uh my GitHub repository. Let me go back here. If I scroll down, you can see that we have this line which uses this header to get the real user IP. And don't worry about this. If you want, you can find the link to the documentation. Just click on this that will take you to this page and you can read about this header in detail. Here we have the Cloudflare IP addresses. And here I added a note that shows you these IP addresses and they you can also see the dates that maybe if they change them or if they added more you can just come to this page, copy them and put them in this configuration file. Good. Now we can save the file. Exit. This file will be loaded automatically. So there is no need to go to our website configuration and uh include this. Now we need to verify the syntax to ensure that everything is correct. Good. And now we need to reload uh engine X. But before I do that, I just want to go to the website because now we are using uh Cloudflare. I want to show you the situation before and after. We already we have the logs. So we can check them to see the IP address that we are getting. Let's check the access logs. I want I will do tail instead of cat and let's see what we are getting. This is the the IP that I was talking about. We can see them here. Uh let me go to the website again just to make sure that we are receiving Cloudflare's IP addresses. Okay, I have visited the web page. I am using tail-fs and let's start refreshing the page. You can see that here we get we are using uh some I uh we are receiving some IP addresses. We confirmed that we are receiving one IP address which is coming from Cloudflare. We might receive different ones but all of them are coming from Cloudflare because here we have uh plenty of uh IP ranges. Now let's stop this and let's reload X. Okay, I've reloaded. Now uh let's let's watch and let's see what we get after uh refreshing the page. Aha. As you can see, I am getting my own IP address now instead of the server's IP. And I can refresh. And uh yeah, as you can see, I am getting my IP address. And I'm I'm also receiving other IP addresses. These are coming from either bots, probably bots because no one knows my website and not real humans. Uh these are just automatic bots that are uh targeting your website because they are trying to see if they can get some credentials from your website. I can show you this if we scroll uh let me use maybe cat or something to see the full uh log. I can also take this to grab and search for env. As you can see, this person or yeah, this boss is trying to see if they can find some env files on my uh web server. Um I mean even if they they try they won't be able to get anything because we are not serving those files when we created our engineext configuration. But you need to be aware that if your server is public, it's going to be hammered by bots. Awesome. Now the thing that we need to do is um to start working on caching. When you develop a front- end application, there are some pages that are not going to change. Those can be cached. But the pages uh that are dynamic, we should not change them. For example, if I change my index.html, this is a single page application. If I add another feature, let's say to one of my blogs, I want to I want users to be able to see it. If I cache index.html, they won't be showing they won't be receiving the new updates because browsers they are the they are designed to cache resources so that they load fast for users. But sometimes if this is not implemented correctly uh they might not receive uh new updates from you and this is what we are going to focus on. Uh now let's open my engineext configuration and here we need to start adding some caching. We have a single page application. I'm going to divide the caching strategy into two parts. The first one we will uh we will never cache index.html because the whole application is injected there and if uh something new was added to the front end it's not going to show up if we cache index.html but assets like images logos those can be cached forever. So let's start uh applying this. Let me show you how we do this. I will come here and I will add another location block. This block will tell the browser to always communicate with the server to see if there is a new version of index.html. If if we have one then the browser will try to load it. And here in the caching strategy we are using no cache. uh so that we tell the browser to not store it and instead try to communicate with the server every time uh this file is requested. And as you can see here we are using equal because we want to focus just on this specific file. We can also add this location block to cache assets forever. Here we are the the assets are located in the b folder. Here the caching strategy is now we are setting it to public. Max age is set to this very big value. Uh it is in seconds. If you convert it to years it's going to give you exactly one year. And here we are using immutable. This is an instruction that tells the browser that this file will never change. Let's save the file. Let's exit. Uh now we need to verify that the syntax is okay. Good. And we can reload. Let's go to the browser. Let's refresh the page. Just ensure that everything is uh working without any issue. Okay, great. Now what I want you to do is to open the network tab. I will zoom in a little bit here. Select all or you can select the static files. Refresh the page and let's see what we have here. I'll select HTML, CSS and JavaScript. If you look at this column transferred, you see that the browser is telling you that most of these static files were cached. That makes sense because we have added the the caching strategy but the root file which is uh the index.html HTML we always uh we are always communicating with the server to get the latest version. And as you can see here in the cache control we uh it is set to no cache which means that the configuration that we added to engineext uh has been loaded uh successfully and in the response we get uh index.thml the HTML and as I mentioned in Vue or other frameworks we inject uh the application inside this div with the id app that's why we need uh it's very important to not cache this file amazing this worked now I want uh I want to mention the pros and cons of using a CDN like Cloudflare or yeah other CDN's uh the benefit here is speed. We saw that before we weren't able I mean for users in China they needed to wait 5 seconds to load the website and my website is not heavy. Imagine if the website was uh very huge or not optimized that 5 seconds uh would have turned to maybe 7 seconds or or more. Um but now thankfully with the help of Cloudflare we were able to bring that down from 5 seconds to 1.5 seconds which is amazing. And as you can see the the numbers have changed because probably the name servers uh have propagated that's why we see this dramatic shift. Uh yeah, as you can see even here uh when trying to run the dick command I no longer see the pork bun name servers instead I am seeing the cloud player name servers amazing this didn't take even an hour and we can see a huge boost in uh performance that this is very amazing another benefit is security even uh even though we are using the free plan we are able to uh get some security. Um, CloudFlare will protect your website from DDoS attacks and other types of attacks. It's going to block AI crawlers from scraping your website uh which is amazing. But the problem is uh this adds complexity. We have introduced another step before reaching uh our application. And if uh let's say that someday a uh a problem happened and your application is not accessible you have to verify everything and um yeah because you don't know if the problem is in cloudflare or is uh in your application and sometimes if cloud if there is an outage in cloudflare your application will not be accessible and this happened many times uh in the recent months uh many people who relied on Cloudflare suddenly couldn't uh or users couldn't re reach their applications. So these are the pros and cons of using this. But I mean I would say the main benefit is that you won't pay a lot of money to host your application in uh many web uh on many web servers scattered around the world. Your website is now globally distributed, passed, and protected by Cloudflare's network. You have successfully finished the performance setup. However, before we move on to automating your deployments, we need to ensure that your server is completely locked down against hackers and automated scanners. As you saw in the engineext logs, we saw that there are many bots who are trying to fetch secure files. So we need to ensure that everything is secure so that we can rest easy and don't worry about something being stolen from your web server. In the next chapter, we are going to talk about vulnerability scanning. I will show you how to test your own defenses. I'm going to show you how to update the engineext configuration to block access to files like envit something like that so that bots automated bots cannot reach them. I will show you how to scan uh your web server for open ports using a tool called end mapap. This will uh with this we can uh verify that only the minimum amount of ports are open. Uh we want ports for HTTP, HTTPS and SSH. These are the only ports that we want to open to the entire internet. But the other ports like uh your applications back end or me search or other services services those needs to be uh closed and if we want to use them we need to use SSH tunneling like we did with the previous tutorials. This is what we are going to focus on uh in the next tutorial. See you next time. Welcome back. In this tutorial we are going to focus on vulnerability scanning. You are going to learn about a concept called soft 404. We are going to fix it because this is present in our application. You are going to configure engine X to block malicious bots that are scanning your website 24/7. And finally, you will use a tool called N MAPAP to scan your firewall from the outside. Let's talk about this trap that we call soft 404s. I have this diagram that explains it. Here we don't have a I mean we could have a regular user but mostly these are bots. They will try to get files that contains um valuable information like your env files. So this request will go through cloudflare and will reach your application. Now, engineext doesn't know about these files because the location blocks that we added are serving just our back end and the built files uh for view. So, env is not known. So, engineext will just fall back to serving index.html and this query or this request will succeed. But this is a false success because the intent of the bot is to get not index.html. Why this is a false success? Because the bot will and will think that it was able to get thev file. So it's going to always come to your web server and it will hammer it day and night. It's going to keep asking it for the same file. But if it gets A44 not found, it's going to skip this. And this is useful for malicious bots and also crawlers like Google's crawlers, for example. If they see that this path doesn't exist on your website, it's going to stop uh accessing it. And this this is b this is good for you and for them because you will uh you will not start wasting bandwidth on requests that are uh going to give 404 and uh in this case bot will not will see a signal that will tell them you are not going to get this file anyway. So they are going to back up. This is the trap that we are uh that we have right now in our application and we can test it here. Uh this is uh the new domain. I can come here. So we can see that we have just slash that will go to index.html but I can add dot /.Nv. As you can see uh the request was successful. I was a I am still on the website. I don't see any error and my application uh I configured my application to load a 404 page because yeah this is not known. there is no route that will there is no page that will uh be loaded after accessing this URL. So I have a custom page but this gives 200 as a uh HTTP status code. Uh but we don't want to do this. Now if I go to my production server and type the same query I am I will get 404 not found because I am blocking these uh most of these files that uh automated boss try to check and now the response is 404 it's not 200. If I go here and click on this request we get 404. If we go back to this website where we have the soft 404 problem, you can see that here we are receiving 200. Okay, let's see how to solve this problem. Again, you can go back to my GitHub repository. You have everything in this file. If you scroll down a little bit, you have the vulnerability scanning section. And here, this is what we need to add. You can see that here we are blocking a lot of files um so that we are blocking mainly anything that starts with dot we have SQL all yeah there are plenty of things that that we don't want to uh serve. I will copy this and I will paste it uh in uh my engineext configuration. Now let's go to my server. I am still logged in. I will use nano and let's add this uh you can add it u here or below it doesn't matter let me explain to you the second location block here we are blocking uh a lot of stuff even though we are not using PHP in this application you if you inspect the logs you will see that they are trying to access some admin panels PHP files stuff like that so I am just I just have this application block that will check all these um file extensions and uh will block them. Here I as you can see I am returning 404 so that I can display that page and access log and log not found are set to off because I don't want to store um yeah these failed requests uh so that I don't consume a lot of disk space because as I mentioned you will be receiving thousands of requests per day just um from bots that are trying to access these files and here we have something called well-known phone. Uh you can go to this page on Wikipedia and read more about them. Uh this is important. We don't need to block this. If you do, search bot will not be able to verify your domain and your SSL certificates. And if that happens, um yeah, uh s will not be able to renew your certificates and that will break after 90 days. we are so here again we are using uh an expression that will capture most files like envit stuff like that but uh we have one exception which is the the wellknown uh directory we don't want uh we don't want to block that we want to keep it for all files we are going to deny that request and again we are going to return 404 and we don't want to add anything or yeah we don't want to add noise to the log files. Okay, now let's save the file. Let's verify that the configuration is correct. Yes, it is. And let's reload. Now, let's go back to the browser. Let's see what we have here. Okay, so remember this is my new domain. This is the response that I re that I got before this update to the configuration. Now, let's refresh the page. And voila, we get 404. Now I cannot get um or what were the other stuff. Uh yeah, let's uh check the logs. So they were getting some PHP stuff. I'm not sure what about uh what they were trying to get. Yeah, you can see that they are trying to go to some admin thing. Uh yeah, I'm not familiar with PHP, but we can uh take this and see if that will get blocked or not. put it here and yeah that worked because we have PHP at the end and um yeah we can also I think we are also capturing some Python stuff let's see mainph that also gets blocked but let's try with Python yeah that works amazing so yeah the list can be can be big uh I just try to you can analyze the the logs to see uh the files that the bots are trying to access or you could search online. There are many blacklists that you can find to add to your engineext configuration. If you want, you can uh use one of these GitHub repositories to uh block uh bad bots. And here uh so yeah, this is a repository. They give you some text files that you can just put on your engineext configuration. Uh so yeah they had a few stuff here that uh that you can just include in your configuration. You can read more about this and you can also uh come here on this other repository and use it. They have plenty of uh files here that you can check. But this is something that you can also configure yourself just uh by reading the yeah by reading the logs. You can also use LLMs. they will be very helpful in this uh step. You can explain to them your stack so that they don't so that they can help you uh block everything but u yeah don't touch some some things that will make your application not load or break. Now we have one more thing that I want you to learn and that will involve this tool N map the network mapper. It's a free utility that we can install uh on Ubuntu and other Linux dros. You just run um sudo app install end mapap and you will get it. Uh I already have it so there is no need to download it. And uh let's verify the version. We have 7.95. That's good. And to use it, we are going to run this command here. I will I'll type my domain name. And let's see what we have. Now end mapap is going to start. The f flag will run a fast scan. And this is going to check just 100 ports. And uh one one thing that I want to mention is that end mapap you should run this command on your local computer uh not uh on the on the virtual machine. Uh you can see that I have opened another terminal tab and let's see what we have here. So we have 97 and here we have another three ports. The the sum gives us 100 which confirms the theory um or what I explained about this F flag. Now let's analyze the the output. You can see that here uh we have not shown 97. These are other ports that are not available or that are not accessible from the out from the basically the public internet. So that's that's a good sign. And after that we have three ports which are this is what I explained uh in the beginning. We want to allow just HTTP, HTTPS and SSH. We don't want to open port 8080 or 7,700. These are ports that we are using uh in that I am using in my applications uh to basically access M search fast API and also uh view but those are running uh but they are p uh they are they're not accessible from uh the outside and uh as I showed you in the previous tutorials if you want to access those you don't open a new hole in the firewall instead Use SSH tunneling to open a temporary tunnel between your local setup and the virtual machine. Do your work and then close that uh tunnel. At this point, you were able to deploy your application manually. You have u learned to how to deal with uh bad actors that are trying to access your website. you learn how to optimize the load time and plenty of other skills that are going to be very um beneficial. We saw you saw that we are using digital ocean but you can take the concept that you have learned here and use those with other cloud providers. But this is the first step. Now we are going to enter another phase where we are going to automate a lot of stuff. We are going to create an automation pipeline that will allow you to make changes to your application locally. Maybe develop a new feature uh fix a security problem and instead of uh taking the source code, copying it from your local computer to the virtual machine, do the build process and um install the dependencies and then serve your application. We you are going to automate this process. You are going to learn about CICD which stands for continuous integration and delivery which will allow you to click on a few buttons and those buttons will run jobs uh on your behalf. Those jobs will check for security problems. They will check if uh dep the dependencies that you are relying on are secure. They will build your front end. They will shut your backend application, reload the new changes if there are any and um other other kinds of job and you will just sit there and watch uh your new changes uh live become live on your website and that will take you a few minutes to do instead of hours of manual labor. In the in that next module, we are going to start slow. In the first sub chapter, I will show you how to put this code on GitHub. I assume that you already have one or you can also use other websites like GitLab or uh Codeberg or yeah any any g compatible client. Uh we'll show you how to protect your main branch so that you don't push code accidentally to it. We need to run that pipeline to ensure that everything is secure, everything is correct before merging it to main. And after merging, I will show you how to do the deployment. But that will come uh in the next sub chapters. So here we just want to focus on the code quality. I'll show you how to use the rough library because here we are using Python to uh standardize to run linting and formatting in order to standardize the code that we write. And on the front end side you will use eslint which is the the default one. Okay we will uh focus on formatting the code and catching linting errors in the next uh tutorial. See you next time. Welcome to module five titled the automation pipeline. In the previous modules you were able to deploy the web application manually. We have started by copying the source code from your local machine to the VM. You have been able to install the dependencies, build the front end and other stuff. This is great. It's important to do things manually, but you should not continue like this because if you add features to your application, you want to do the deployment automatically. you you want to focus on writing the code and let the this automation that we are going to build in this module do the work for you. Here is what we are going to talk about in this sub chapter. First I will show you how to protect the main branch using GitHub rule sets. In this tutorial I am using GitHub but you can do the same thing if you are using GitLab or other uh websites. I will show you how to automate local checks using pre-commit. And for Python, we are going to use the rough project to do linting and formatting. And for JavaScript, we will be using ESLint with prettier. If you use other other programming languages, look for the projects that are there and use them. The concepts are the same, but we are using different technologies. This is the repository that I was talking about a lot during the previous modules. Here you can find everything related to the to the book that I am writing and these the slides everything is inside this repository and I want to show you this is my this is my other GitHub repository where I host my where the source code of my personal website and I want to show you what will what we will achieve by the end of this module. You can see that here is the automation that I was talking about. You have certain jobs that you run here on GitHub. They run whenever you push code and they do a lot of things for you. For example, here this job will check my dependencies. It will look at the Python libraries. If it sees that there is some security vulnerabilities, it's going to stop the code that I pushed and it's not going to be merged to the main branch. This will protect you from deploying code that is not secure. Uh here we are performing linting and formatting. In this video we are going to do this locally but we can also do it here on GitHub actions. You can run tests. We have different kind of tests here. Here you can see that we have unit tests, integration tests and end to end test. Finally, if everything goes well, we will build the front end and the pipeline succeeds. And we we can add other actions that will allow us to build the front end and deploy it. For example, if I look at this one here, as you can see, I have uh plenty of jobs or workflows. And here I have this one, manual deploy. after merging a pull request to the to the main branch. I come here and run the manual deploy because here as you can see we just built the front end because we are 100% sure that there is no problem with the code because it uh it passed all those checks. Now I come here I run the job and here it notifies me sends an email telling me hey do you want to deploy? I just click on a button to approve that. And as you can see it says deploy and I can check the website. This is what we want to do. And we don't need to SSH to the machine, run commands manually. Everything is going to be done for us uh in this interface. And what we do is we just create a pull request. We check the code. And here you have the checks. So for example, here it says all 17 checks have passed. If that's the case, we merge. But if a check fails, it's not going to be it's not going to to let me merge until I fix it. But for now, let's just focus on this branch. It's brand new. I haven't set up anything. We are going to do this uh together. The first thing we want to do is to secure the main branch. I also have my code editor. Here it is. And as you can see, if I click on this icon, it shows me the changes that I have here. You can see I I can come here uh add this and push. Nothing is go no one is going to stop me. But what what if I what if I don't want that? Let's say that you are working with a team of people and you want to do a pull request before merging. In that case, we want to protect this so that you don't push domain accidentally. But to fix this, we come here, we click on settings. Let me zoom in a little bit. Here we click on branches and we add a branch rule set. Here we need to give the rule set a name. Let's call it main branch protection. Here it currently it's disabled. We want to enable it. Let's choose active. Let's scroll to the target branches section. Click on add target. Let's select include default branch. Let's go back to the repository. As you can see, my default branch is main, but in some cases it could be master. That's why here it shows default instead of uh main or master. And here we have some rules that we can apply. You can read through them. And here for example, let's scroll down to restrict deletions. This will prevent anyone from deleting the main branch. That's why we want to restrict it. Here we have another rule that we need to keep. It says block force pushes. This will prevent you from writing from using git push-force because when you use force you rewrite history and that can lead to problems here. These two are uh checks by default. We want to add this third one. Require a pull request before merging. If you are working with a team, required approval should be either one or two. It depends on the size of the team. But if you are working as a solo developer, select keep this at zero. I am I am doing it on my other project. Why? Because it builds the habit of uh creating a branch for each feature you work on. And if you here you have to do zero because you are the only one who is working on this project. And it is very helpful. is going to make you think of always working with making small changes, creating a pull request and merge it to main like I do with my other projects. We will come back to this uh in the upcoming sub chapters because we don't want to do everything in this in this first video, but we just want to get up and running. Now, let's click on create. Here it's going to tell you you need to sign in. I will do this and I'll come back. I am back. You can see it says rule set created and here it is and it it says applies to one target which is the main branch. This is what we want to see. Now if I go back to the repository you can see that this is our main branch. If we click on view all branches and here you can see that we have we can also view the rules from this button. And yeah we have three branch rules. That's correct. But let's go back to VS Code and let's see if we can push something to this codebase from main. Here is the repository. Let's make some change to this file. I will come here and let's just add an empty line. You can see that we we have modified the file. I will stage it and let's create a commit. Get commit- m let's call it test. Hit enter. Okay, that worked. But now let's try to push this get push. As you can see I was not able to do that. Let's see what is the error. You can see that here it says repository rule violations found on the main branch. Here it says changes must be made through a pull request. This is what we want to see. So remote rejected our push operation. Amazing. And to undo this commit because it was just test, you can run git reset add. And here we just want to reset the latest commit. Let's do it. And as you can see, uh, it was removed. And it shows me that this is the last modification I made to this file. Great, it's working. So, let me just undo this because I don't want to make that change. You have successfully protected the main branch. But we still need to check the commits that we are pushing. What do I mean when I say we need to check commits? Sometimes you might be working on your project and you might accidentally commit let's say a secret key. That's that's a problem because that will that will sit on the git history and everyone can see it unless you rotate the key. With something called pre-commit, we are able to catch this problem before pushing the commit to the remote repository. We can do other checks. We can check if the linting is correct, if the code is formatted correctly and other stuff. This concept is called get hooks and we are going to target pre-commit hook specifically. So when you type get commit, this hook will intercept uh your request. Basically, it's going to see what you are what you are committing and it's going to run a series of checks and you can add uh as many checks as you want. If everything passes, great. The request will go through and your commit will be deployed to the cloud or yeah to the cloud repository. But if something if a check fails the the hook will going to block you unless you fix the problem. Okay. So this is what we what we are going to do uh now and we are going to use this amazing project called recommit. Let's go back to VS Code to show you how to use this. Here you can see that I have some changes made to this get ignore. I'll try to stash that because I don't need it. Now I need to add this specific file pre-commit-config.yml. Here we are going to add the checks that we want to perform. Let's now start with the back end. You can see that here we don't have any Python code but we will add one to test this. Let's just add main.py and let's create a a function that will print hello world. We can run this and it should work. So if we do main.py, as you can see, we get hello world. We are doing this just to test this hook. We have two kinds of texts that we can use. You have remote repositories and you have local custom hooks that you can create on your own. For example, here if I click on this link, it takes me to GitHub. You can see that people who are working on rough have created this pre-commit that I can just install by just putting the link of the repository here. So this means when I run install, it's going to go to this repository. It's going to bring this version. How do I know if this is the latest version? It's simple. Just go back to the repository. Scroll down and here click on releases and that will show you the latest release. Currently we uh in here in rough the latest version is 0.15.12 and this is exactly what I have specified here in this field and here you put your hooks what you want to run here I have two kind of checks I have a linting step and a formatting step. Let's focus on the first hook. You can see that the ID is rough and I give it a name and I specify that I want to perform LinkedIn. And here I have I am passing additional arguments. And here I pass d-fix because sometimes there are some some things that we can fix in the code. And here I am targeting everything inside the backend folder. So let's do that. Let's let's create a backend folder. And let's drag this file and put it there. Otherwise, this hook will not find anything. It's going it's going to succeed. But we want to target only u Python files that are inside the backend folder. In the second hook, we are just performing formatting. Uh it's simple. This file is is good. There is no problem in it. Now, if I try to use this hook, it's not going to fail. Let's see how to install this. You have different options of installing it. You can use Anaconda, you can use poetry, uv or any package manager in Python or you can simply use the ven module. Uh let's try let's try that. Then ven as you can see we have our virtual environment and let's activate it. Source van bin /activate. As you can see here I have van and I am using python 3.12 here we need to add dot getit ignore so that we don't push what we have here and let's put van here okay good now we have just three modifications now let's install precomit to do that make sure that the environment is activated and run pip install precomit great that worked and now we need to run precomit install you can see that it says pre-commit installed at this location. Let's controlclick on this file just to see what we have here. You can see that this file was autogenerated by pre-comit and here we have this variable install python that points to the virtual environment. The problem with this approach is that you can see that we have installed precomit on this project. So if I move if I delete this virtual environment, the pre-commit hook will fail. It's not going to find this path and it's not going to work. But if you stay here, it's going to work without any issue. But we have other options that we can try to fix this problem because ideally we want to install pre-commit globally so that we can use it in any Python pro in any projects not just Python and that way we will fix these issues. So using a virtual environment is perfectly fine. The only issue that you will face is when you decide to delete the virtual environment. But if you don't then you can keep using this or we have another option which is to install pre-commit globally with pep x. This is another python library that we can use and we have plenty of options to download it. Here I am using because I am on iuntu I am using apt to install this from the repository. Uh I have already installed it. So, it's not going to add anything, but I just want you to go to GitHub, you will find the pipex there. As you can see from the about section, it says install and run Python applications in isolated environments. And this is what we want to do. I am not using the regular pip because I don't want to install this globally because it's not it's a bad idea. That's why we use virtual environments. But PEX solved this issue for us. And if you scroll down a little bit, you can see here in the image, it shows you that you can install libraries and run them in isolation. And you have plenty of options to install this here on Linux. You can use pip if you want. And uh I decided to use apt. But I just want you to go here to in to look at your current OS to uh get the perfect command for your system. After installing pipex, you need to run pipex insure path. And that's that's the second command here. Let's do it. As you can see, it says that it already found the path. So that's fine. And if you see this warning, no problem. uh the the message that let tells us that if it is working is this otherwise pipex is ready to go. If you see this, you are good to go. Let's clear the page. And now we run pip x install pcomit. As you can see, I have already installed it before. That's why it didn't work. So it tells me that it seems that this library is already installed. So that's fine. And as you can see here is the location of that specific library. Now all we need to do is to go back to VS Code. You can see that here I have activated this virtual environment. Now I need to deactivate. Let's type deactivate. Good. The virtual environment is is no longer there. And we can also delete it because we uh I want to show you that this is going to work without a virtual environment. You can see we have removed that folder. And I can also remove the changes from git ignore. We need to do the same command pre-commit install. I'll hit enter. As you can see, this worked. And in this file, you can see that the path has changed. Now the path is pointing to the pipex environments. This is what we want. Now, as you can see, I don't have a virtual environment. That's fine because this is installed globally. Now, I can open another project, run pre-commit install, and it's going to use the same virtual environment. That's great. And this solves the issue with local virtual environments. Great. Now we can continue. First, we need to commit this file. This is the first operation that we need to do. So, let me commit this. I'll click on the commit button. Great. It works. The code uh I have I have been able to create the commit. Nothing has stopped me. Now, because we have this specific hook that targets Python files, if I try to commit this, it this these hooks are going to check the file. And I'm going to do this in the in the terminal because it's going to show us what is happening. Okay, let's do get commit dash m and let's say test python hooks and let's hit enter. As you can see the first check passed. So linting was good but formatting was not good. So here it says failed and we as you can see it says one file reformatted and the commit is not going to go. The file has been changed and as you can see here is the change that was made and we are using this to make sure that formatting is consistent. If you work with a team, you want the formatting to stay consistent because otherwise it's going to be very hard to review put requests because they are going to make changes with different formatting and and that's not a pleasant uh situation to be in. This is why we use pre-commit hooks. And as you can see, we were not able to push this. And if everyone is using the same set of rules, it ensures that the code uses the same formatting rules and everything stays the same. The only thing I need to do is to add this again to add the modifications. Let's try to commit. And voila, it worked. You can see that uh both checks have passed and I was able to create the commit without any problem. As you can see, I can click on the button to push these two commits. But this is not going to work because I am on the main branch. We can test it again. It's fun to test stuff like this. So, let's do get push. And it was rejected. Great. So, this confirms that it it's working. Awesome. So, this was just an example. But now let's go back to my other repository in order to show you the pre-commit hook, the pre-commit configuration that I have there. It's totally different from this one because we have JavaScript and Python. Here is my other project. And if you remember, this is exactly the code that I told you to download if you didn't have your own projects to work with. And here here is my configuration. We have plenty of things but you can see that the first part is similar to what I have showed you in the other project. We are using rough and we are we have two hooks one for linting and the other one for formatting. Let me collapse this because we have seen it here. I have another repository and this is used for JavaScript. Here we are using eslint because we want to do linting. We want to verify that the syntax in our JavaScript code is correct and that there are no problems. As you can see here, we have just one hook and uh I've called it yes link front end. Here I have files. It's going to target everything inside the front end folder. This is a regular expression that will target only view and JS files inside the front end directory. If I open source you can see that here view files end with dot view that's why I have it here and I also have js here I am not using typescript if I was using it I will change js to ts so the types that uh I'm looking for files and here I also add arguments so that I can fix problems if I find them and I also point the conf to the configuration file because yes lint can be configured and here is a configuration file. So here if you want to disable some rules, you can uh come here and modify the rules that uh you want your hook to check for uh or to skip in this case. And here I have additional dependencies. These are these will change. It depends on the project that you are working on. If you aren't working with Vue, you won't you won't add this. So yeah, these are just additional dependencies that will be installed in order to run this hook. So this is the front end part and we also have some local repositories or local hooks that we create on ourselves. This is what why I mentioned before that you can use either hooks created by the community or use your own. For example, here I have two local ones and here I will you can see that I I call this one prettier because with prettier I can check many files. I can check JS view, CSS and other file extensions and uh same if I detect that some format something is not formatted well I I add the dash dash write to change the file and uh after that I can just come here and add click on the plus button to accept the change and finally I have this other hook that will automatically generate the site map for my website and this will run whenever I commit and here you can see that I just have a simple bash script. I go inside the front end, run the sitemap. So this is a script that I have and it's going to generate this XML file that I will stage. Using pre-commit is really easy and it helps you catch a lot of problems locally in before deploying or publishing the the code changes. And we can also do a simple test here. Let's let's open app doview view and let me add some spaces some new lines here that will trigger prettier and it's going to block this operation. Let me open my terminal and let's check the status just to confirm that everything is all right. Let's add this. Okay, now we can write the commit message. Let's say uh let's hit enter. As you can see that works here. rough is not failing. It is skipped because we haven't changed anything uh in the back end but uh we have changed something in the front end directory. Yes, lint worked. That's not a problem. But prettier didn't like the changes that we have made. And because we have set rights, you can see that it changed the file. So, it removed those new lines for us. That's great because I can just come here verify the new changes and click on add. And yeah, I can come let me also you can see that sitemap was generated for us automatically. That's good. I can come back and uh run it. And as you can see this time it worked because we fixed all the issues. And that's it for this video. Your local environment is now fully secure. You have protected the main branch and you have created pre-commit hooks to check commits before pushing them. In the next sub chapter, we are going to talk about continuous integration. We are going to perform automation using GitHub actions that will allow us to write workflows that we can that we can run after let's say creating a pull request that will allow you to do something similar to what I have here. You can define your jobs and uh put them in a pipeline that will run uh automatically and if everything succeeds you can merge your pull request. This is going to take us a lot of time to develop. But I'll try to go uh step by step. I'll try to go slowly so that you can understand everything. Uh once you understand this, once you make one or two jobs, then the rest is going to be repetitive. You will uh then you will know how to add uh anything. And you might be asking, do I need to follow a specific rule? You can. No, you can design this however you like. Just make sure that you catch the things that you want to focus on. For example, if you want to do security, you need to add some jobs that will test if your app is secure. If you want to check LinkedIn and formatting, you need to add jobs that will check that. So each job is going to check one thing for you. And by building uh the these sets of jobs, you will ensure that uh the code that you push to production is secure. Uh there are and there are no bugs no bugs in it. See you in the next video. Welcome back. In this video we are going to talk about continuous integration. In the previous video you have seen how to perform checks locally to stop bugs or unformatted code from being synced with the remote repository. Today we are going to do the same thing but on GitHub or you can do the same thing on GitLab or other platforms. The same concept applies but there are some uh things that might change from platform to platform. Here is what we are going to talk about in this video. We are going to learn about continuous integration. You are going to build modular and reusable workflows to automate linting formatting and to perform unit tests. And of course, we want to block bad code from being merged to the main branch by using GitHub status checks. In this session, we are going to build a small pipeline that will do the following. You can see that here. This is specific to GitHub but you can find the same concept on GitLab also or other platforms. Here we have five jobs. You can see that we have two jobs that we run to test the back end and we have the same that but for the front end. The first step is to perform linting and formatting and after that we perform unit tests. And you can see by default GitHub will run jobs in parallel but some jobs can depend on each other. For example, before I run the unit tests in the back end, I need to make sure that there is no syntax error and the code is formatted correctly. If this is not the case, this job will be skipped. It's not going to run. The same the same goes for the front end. Finally, we have this pipeline success which is going to be used to allow us to merge the pull request or to uh block it. You can see that in this case everything passed that mean that's why I get a green check mark which means that I can merge this pull request. If let's say the lint failed this is going to turn red and it's going to block the mer the pull request. We will keep adding more jobs to this pipeline in the upcoming videos. But for now, let's just start with these two. This is what we have performed locally in the previous video. But on top of that, we are going to add unit tests. I am now on GitHub and I I switched to my secondary account. Here I have forked the source code that we used to deploy the website on digital ocean because I wanted to experiment here uh without any problem. So I have forked this and now I will create I've already created a pull request but we will start from scratch and I will show you how this works and how to build a GitHub action. First let's start by creating a branch. Here we are using VS code. I will just click on this button that will allow me to create a new branch from a specific branch. And here I am creating it from master. I will call this test number two. I will hit enter and I will select head. I will switch to that branch because remember the master branch is protected. I cannot push directly to that branch. If you are using GitHub, you start by creating agitub folder. You can see I already have it here and we have plenty of things. But to create a pipeline, we put our workflows inside the workflows folder. Let's open it. You can see that I have a lot of things. Don't worry about these. Don't worry about this. Uh what you are seeing here is the final product. Uh we are going to build these step by step. I'll go through these and remove them and I will keep just the ones that we need for this video. I am back. As you can see, I have removed a lot of files and let's start by working on linting and formatting in the back end. If you want to learn more about this, you can search GitHub actions. As you can see, you can search GitHub actions and you'll find you will find them here. Their documentation is great. So if you want to dive deeper, you can use this or you can just take the documentation and uh use that with your preferred large language model and they will help you uh understand the concept. But I'll try to also do my best to explain to you how this works. Our goal is to build a modular and robust pipeline. Which means that we want each job to be contained in its own file. And we call these reusable workflows. For example, here I am hovering over the backend lint and format. This is going to be one file which is this one. The code that we will put here will run at this step. And after that this file ci will be the orchestrator. It's going to call these uh workflows independently one after the other. And we don't want to put everything inside one giant file because if you get an error, it's going to be very hard to debug. But by putting everything inside a its own file, it's going to make life easier and you will see how the the orchestrator is going to be very minimal. It's going to be very easy to read and debugging is also going to be fun. To create a workflow, you start by giving it a name. Here you can put anything and it's going to be easier for you to locate. For example, if we let me go back to GitHub, you can see that here we have this actions tab. If I click on it, uh you can see that we have plenty of workflows and you can see that they're named uh they are named. Let's look at this one. You can see that it's called backend lint and format. Let's go back to GitHub and we we find it here. back end lint and format. You can click on it, you will see the how many times it we run it, etc. Uh, okay. And sometimes you can even run them manually if you want to. Let's look at the workflow or the orchestrator. You can see that I can come here and run it manually if I want to. We will I will show you how to implement that. But this is why we give them names so that it becomes easier for us to uh find them here in the actions tab. After that we add the onb block and here we specify workflow call. This tells GitHub that this file is a reusable template not a standalone script which means we cannot run it on its own. We need to call it from the orchestrator. After that we create a series of jobs that we want to run inside this specific workflow. And here we want to perform just lint and formatting. That's why we you can see that here we have jobs and here you can specify your jobs and here we are going to run one one specific job. We are not going to run more than that. Inside the lint and format job we can also give it a name and runs on is very important. Here you can choose different operating systems. Here I am using iuntu and latest will take the la the the recent version of Ubuntu. Why are we doing this? Because remember we are going to run this on the cloud. It's not going the these checks are going to be executed on GitHub's own machines. That's why we need to specify a an operating system. After that we add defaults run and we specify the working directory. We are doing this to make our life easier because if we don't add this we have to always type cd back end whenever if we want to perform if you want to perform something inside the backend folder we need to always do that but by putting this GitHub will automatically move us to the backend folder and we can run the commands without always repeating cd space back end and finally we put the steps A job is a series of steps and here you can you see the the template is the same. Everything has a name. You can see that here we have name checkout repository. This is going to be very helpful. If you go to your pipeline if you click on this backend lint and format you see here are the steps and let me split the screen and let's look at let's look at this. You can see that here we have checkout repository. This is exactly the name that I have here. Here we have setup Python. We also find this here. And why is this important? Because let's say that this step has failed. You can quickly go back to your YAML file and find that specific step. It's going to make your it's going to make debugging very easy. And as you can see, each step is going to run. And here they're going to run sequentially one after the other. And if a step failed, the other ones are going to be skipped. So let's look at what we have here in this specific job. First we need to check out the repository. Because we are running on a virtual machine, we need to download the source code. And here we have something that we call actions. You can see that here we have two different keywords that we are using. Here we have users and down below we have run. Basically this keyword is going to use scripts that the community has made. Let me show you what I am talking about. If I copy this name and search for it, let's see what we are going to get. You will find that there is a repository that contains the code for that specific action that we want to run. And uh you can see that here here is here it is actions/checkout. If I go back, it's the same name. and let's verify the version. How do we do that? It's simple. You click on the releases page and here you find the versions. So the latest version is 6.0.2 but I am using version 6. And this is important because sometimes these actions change and uh if they introduce breaking changes you can read more about them here on GitHub. But if you use run this means you will provide uh commands that the uh that Ubuntu will run. This is the difference between users and run. Okay. So when I do this, let's look at here on uh this job. Let's open this. You can see that we this action has been executed and now it is syncing the repository. And yeah, you can open this and read more about this. You can see that they are initializing the repository and fetching the repository. This is what we want to see. After this step, the source code is on this Ubuntu machine. And now we can run commands in it. Setup python is going to download Python for us. And uh let's uh let's see here. We install rough. You can see that we use pip to install that library. And it worked. After that, we run the llinter and formatter. We can see that everything is great. Every there is no problem. And here these are jobs that GitHub will run. We can see that we don't have these uh post setup Python. It's not here. But the this these are just steps that are uh run to clean the machine because we are done with it and uh there is no need to keep this there. And you might be wondering how many steps do I need to put here. You can put as many steps as you want. Uh you feel free to customize your jobs however you like and um yeah there is no constraint know the difference between users and run and sometimes it's going it's easier to just use pre-made scripts. Uh so for example here if we wanted to install if we wanted to use rough you can we can search for it and uh use it maybe instead of doing all these steps. Uh let's do this just uh to show you how how to search this. So you type rough GitHub action and you search for it. As you can see there is one. So instead of repeating instead of doing these these steps manually we can reuse this rough action and that will make our life easier. It depends sometimes you might you don't find actions for the specific uh library that you are using. In that case you you use uh this approach and here I wanted to show you both so that you so that you don't get stuck if you don't find a a an action for a specific library that you are using. We have spent a lot of time in this specific file. So now let's go through the other steps quickly and let's see the difference between the back end and front end. Okay. Now let me go back to the CI pipeline. uh let's click on it again and let this time let's focus on uh linting and formatting the front end and here I will also open that file here it is front end lint and format okay let's see what we have here uh we start same as before we define the name and uh here we use workflow call to let GitHub know that this is a reusable template and here we have again just one job here let's see this time the working directory is front end. We are using checkout. And here we use, as you can see, this time we didn't install PMP manually, but we used an an action that we can find on GitHub. And we specify that we want to install PNPM version 10. But same thing with node, we are using an action to install node and we target this specific version. And here I forgot to talk about caching. We are doing that here. And on pi the the python side you can see that here we also cache pip. Why are we using caching? Basically if your dependencies don't change you don't need to download them every single time. So caching will make sure to save uh the dependencies so that uh the next job will just read them instead of downloading them each time. This is going to make your pipeline faster and it's uh yeah it's great uh it's a great thing to add. After that we install the dependencies. We run eslint to lint the frontend files and we use prettier for formatting and uh as you can see we have the these steps are are shown here on GitHub. Here we have check out repository, set up pmppm and nodejs, you install the dependencies and you run the linting and formatting steps. Great. Uh these are the two things that we have done in the previous video. We did them locally um before accepting the commit and now we replicated them here on GitHub using GitHub actions. Now we are going to add one more job. uh we are going to perform unit testing and let me show you how that looks like. Let's open backend unit tests. It's going to be uh similar but uh we will change some steps and I want to emphasize that in this course I am not going to show you how to write unit tests because this is outside our scope. If you want to do this that will that will take a lot of time and it depends on what programming language we are using. So we are going to consider that as a blackbox. And if you are using let's say go or another backend language you can swap the commands that I have here with the ones that are appropriate for the specific stack that you are using. Okay. So let's get started. Here the name is backend unit tests. And here we have again one job which I called tests. Let's go inside. First, we download the source code. We set up Python. We install the dependencies. And here we run the unit tests. Okay. And let's look. You can see that here we are using piest. This is the library that that we are using to test our backend code. And after that, this is not important. Basically, here we are using code coverage. Why? So that we can see the code coverage. You can see that here in the readme I showed that okay uh the tests cover 95% of the back end and 85% of the front end and uh yeah you if you want you can add that and for that we use the code coverage package or yeah you can search for it code cover this is the the service that we are using they have a free plan that uh that you can benefit from okay and so this step is not important but what we have here is is what we need. Yeah, it's it's the same. So, if I open this and look at run unit tests, you can see that here are the tests that we have. The everything went well. So, uh so we have 9 59 tests and they all pass without any problem. But if let's if one of them failed, this step is going to fail which means that this pipeline will not succeed. And we are doing the same thing uh in the front end. Let's open that front end unit tests. Let's go down. So here after installing the dependencies we have this test script and on in the front end we put those in the in your package.json file. So here in this scripts object you let's find test. Here it is. We are using v test for that and we have other commands. So if you want to run end to end tests, we are going to look at this in the upcoming tutorials. But for now, we just run unit tests. And again, we use code coverage to get the code coverage for the front end. Great. So now we created these four individual components. Now let's see how to put them in CI.YAML so that they run so that it orchestrates all of them. Here is CI.YML. I have removed uh some stuff so that um yeah so that you don't get overwhelmed. First the name is CI pipeline and here we have something different. So here we put on push on pull request and on workflow dispatch. Let's start with this one. So on push will run this pipeline whenever we push something to the master branch. We will also run it if we create a pull request that targets the master branch. This is what I have here. If I click on pull requests. Let's look at this one. It is targeting the master branch which means it's going to trigger this workflow to run. If I scroll down, uh you can see that I have the workflow here. Here I have my four steps and you can see I cannot merge because we have some failures. Some jobs have failed. And finally we have workflow dispatch. and workflow dispatch allows us to look at the workflows. Let's look at CI pipeline. And I can come here and run it from this page. This is useful especially if you want to uh if something failed and you want to rerun the job manually, you can do it from this uh page. Let's start with the front end link format check. First we give it a name and here users. This time we already have a workflow. We are going to reuse it here. And as you can see now it looks clean. Imagine taking all all everything inside this file and putting it here. And then do the same thing for these three. This file will be very long. Debugging. If you get a problem here uh in your workflow, it's going to be challenging to read the whole file, find the bug, and solve it. But now it looks clean. I just use this keyword to let GitHub know that the implementation is inside this specific file. And I do the same thing for backend lind, front end unit tests, and backend unit tests. The only thing that that we changed here is that we added the needs keyword. Why did we do that? Let's go back again to the pipeline. Let's collapse these two and let's look at just uh let's look at just these two jobs. By default, GitHub will run your jobs in parallel. Here we are performing linting and formatting in the front end and back end. And because these jobs are independent, we allow GitHub to run them in parallel. But if I want to run unit tests, I need to ensure that the code is clean. There is no syntax error. There is no problem in the code. If that's the case, I can run this. So if you have a dependency, you add the needs keyword. So this job needs front end lint format check to succeed. And this creates this dependency which will force GitHub to run them in sequence. We do the same thing for backend. It's going to wait for backend lint format check. If backend lint format fails, this job will not run. GitHub will skip it. And finally, we have pipeline success. For the pipeline to succeed, we want every job to succeed. Okay, that's why we put everything here. again runs on Abuntu latest and if always is important because if something failed we want to run this anyways we don't want to skip it because this is what's going to give us that status at the end the status is going to be either uh success or failure let's look at steps because this is where we have that logic let me expand this page so that we can see this better and let me also decrease the font size First here we have two steps. The pipeline can either succeed or fail. How do we know if that's the case? We use this function contains and here needs so this variable result is going to give us the results for all these jobs. This condition will check if we don't have any failures and if no job has been cancelled we will show check all checks passed successfully. But if we find a failure or something has been cancelled we will show some checks failed or were cancelled and we exit with number one. When we exit with one, we tell the runner that there was an issue and GitHub will display a uh let me show you. It's going to tell you that yeah that the test has failed. We can compare both of these tests to uh understand what's happening here. So the first test you can see it shows us it is showing us a failure. Why? because that specific uh condition this one contains failure is true. Why? Because we have one failure here. It is and this one was skipped that this will make pipeline success exit with one and the pipeline has failed. So it's which means that I won't be able to merge. But in this other case, you can see that it succeeded because um there is no failure and nothing has been skipped. Great. And now if I save this, you can see that we have we are in this branch and uh push the code, we will we will see that this will run. Okay. Now let me remove the splitting to give us some room to play with. Let's push all of this. Uh let's just type test because we are we are not doing something serious now. And but remember to type uh good com commit messages. I'll commit and push. Let's go back to GitHub. You can see that we have this new branch. And uh let's click on new pull requests. And here I don't want to merge with the upstream repository. I want to merge with my fork. And uh let's see. So I want to merge with the master. But here I need to select test number two. Okay. Okay, it shows me these are the changes that I have made. Click on create pull request. Let's keep everything like that and hit create pull request. Okay, let's scroll down and as you can see here are the checks. But notice that we have a problem. I can this button uh it's it's not grayed out like it is still active that which means I can still click on it. That's a problem and I am going to show you why. Because we are here we are not waiting for the final stage to finish which is pipeline success. But the solution is easy. Let's go to settings and we need to update the rule sets. This is the rule that we created before. Open it. Scroll down and here uh make sure to activate require status checks to pass. here. Let's click on add checks and start typing pipeline success and it should appear. Yeah, here it is. Select it and just like that save the changes. Now let's go back to that pull request and let's see what will happen. Uh as you can see I cannot now click on it. And if I hover the message says merging is blocked due to pending merge requests. And here is the pending uh job. It's pipeline success. And this one is going to wait for the other ones to finish before running. And let's see. It's going to run. Let's click on it just to see what's happening. So, pipeline succeeded. This is the message that we have uh that we wrote here. Echo all checks passed successfully. This is what we see here. Great. So, that worked. And now I can merge because there is no failure and nothing has been skipped. Amazing. But let's uh let's try to break this just to show you that we won't be able to merge broken code. Okay, let's open let's uh let's say main.js. Yeah, let's let's remove import and let's just put Axios from Axios. This is not correct. And here you can see that I'm not running this locally. I am running it here on yeah on GitHub just to make modifications quickly which means this is not going to be catched by the pre-commit hooks that we defined earlier. Normally this will will catch that but this is the limitation that I mentioned in the beginning of this video is that yes it's good to have those pre-commit hooks locally to stop the uh some bugs like these but people can also skip them. Uh this is why we create this pipeline to be the gatekeeper. It's not going to allow anything in unless it's good and yeah let's verify that. So let's just have another test commit commit and push. Go back to here. Okay is blocked. And now um the the linking and formatting step in the front end is going to fail. Let's click on it and let's see what's happening. So now we are set up in NodeJS. Good. And let's read the message. You can see that here it says cache restored successfully, which means it it skipped installing NodeJS and it just took it took it restored it from the cache which is great. It confirmed that the caching is working. But let's scroll down a little bit. Ah, you can see that ESLint is not happy with this. It's saying that error parsing uh this token. So it it couldn't find the import and it uh exited with code one. Now let's go back. You can see that because this job has failed uh we we weren't able to run the unit the tests because this depends on the first job and pipeline success failed two because we have one failed job and one cancelled job and now I cannot merge this. So I have to fix uh the problem and they cannot also push to master because it is protected. You see the beauty of having this kind of pipeline in your repository, it's going to be very helpful to catch problems and whenever you merge something to master, it's it is guaranteed that there are no bugs in it and you can merge without any problem and even deploy to your production server. We have reached the end of this tutorial. I hope that you found it interesting and uh you can find the documentation here on this GitHub repository. You will find the link in the video description. And what is next? So we still have two more sub chapters in module 5 and in this one we are going to talk about automated security scanning. We are going to scan vulnerabilities in the libraries that you use in your stack. This is very important. We don't want to push vulnerable code to a production system because you could lose data. There are many problems that can happen if that if you push vulnerable uh libraries and we will perform also this specific test to yeah ensure that uh there are plenty of tests that we can run and I'm going to show you some of them in the upcoming uh tutorial. Welcome back. So far you have built a pipeline that guarantees your code is formatted correctly and your logic works as expected. Today we will focus on automating security scanning. Here is what we are going to talk about in this sub chapter. I will try to explain to you the shift left security model. You are going to run software composition analysis. We will also add static application security testing and finally we will implement a fail fast strategy. CSA and SAS are the two types of tests that we are going to add because that they look for specific security issues in different areas. And the fail fast strategy basically means if the security scan fails, we stop the pipeline. We don't need to run the subsequent jobs because it's just going to be a waste of time because we know that we have a problem in the application. We should never deploy it. And in terms of the shifting security left concept, here it is. I have made this diagram to explain this to you. We have two ways of deploying an application or working and uh we have to work on security. This is very important. But before or traditionally we used to do it this way. First you can see that this is the development cycle. We plan, we design the application, we start coding, we build to make sure that everything is is correct. we have some tests that should pass and right after that we move to security. The problem with this approach is that if you work on a giant application and at the end you realize that you have a lot of problems that will make this development cycle take a lot of time and you will you will be fighting a lot of problems and that will push the release cycle further. uh this is very problematic especially with bigger applications but the shifting left security model fixes this issue by integrating the security checks during this inear cycle let's say so after designing the application now you start coding but while you are coding you are making sure that you are doing the best practices and so you keep thinking about security if you have for example input fields you think about uh sanitizing the inputs. Try to test the application. Can people do SQL injection or other types of attacks while in this tight loop? And you do it before you build you make sure that the feature that you are working on is secure. And if you do this, you ensure that even if you work on large applications, integrating security is going to be an easy task. It's like translation or adding themes in your application. If you do this at the end, it becomes hard. But if you do it in the beginning of the project, it's going to be very easy to uh to integrate. And finally, once uh once you are done with a specific feature, you release and the cycle repeats. Uh this is my GitHub profile and last time you can see that I showed you how to block merging if some of the tests fails. That's good. Now what I'll try to do I will fix these problems and I will come back because we want to ensure that these steps are fine but uh we want uh to add on top of this the security jobs. I am back and as you can see every job has run successfully without any issue. Now let's start auditing both the front end and back end. This is the software composition analysis step and we use it to basically audit the third party libraries that we are including in our projects. For example, in the front end folder we have package.json. It contains all the dependencies that I am using for this project. You can see that we have axios view router and many more. The problem that might happen is that let's say axio might be attacked. Attackers might find a vulnerability in this package which they can exploit. Your application will run without any problem. because that vulnerability is invisible to the checks that we have here. So by adding another job that goes through all your dependencies, checks if there is a vulnerability. When we do this, we can uh successfully block the pipeline. We will not allow deploying a vulnerability a vulnerable package to a to the production environment. we will wait for the maintainers of that specific project to fix the problem before updating to the newest version. And the same story for the back end. If I go back, if I go to the backend folder, you can see that here I am still using the requirements.txt. People nowadays use uv or other package managers. Uh there is nothing wrong with requirements. We define the dependencies that we are relying on and we need a tool that will go through all of them, check the the database, see if there are vulnerabilities and block the pipeline if it finds any. Okay, let's now go back to VS code. You can see that I am still using the web version because these are changes that are simple. Now let's go to the GitHub folder. open workflows and here I need to add two more jobs one for double check the dependencies in the front end and the other one will do the same but for the back end let's start with the front end I will zoom a little bit you can see that I have added this job front end vulnerability check this is the name that I've used here this is a reusable workflow we have explained this in the previous sub chapter and here what are we doing so We are checking the repository here. We are using PMPM version 11. I have changed this from the previous sub chapter. Why? Because if if you want to learn more about this specific version of PMP, make sure to go to Google and search for PMP1. You will land on this page. You can read more about it here. But basically uh by default PNPM is more secure than npm. Recently there were there were a lot of attacks on very popular npm packages and people sometimes they just download the latest version and that uh gets them hacked. PNPM solved these problems by blocking recent updates. So if an update is less than 7 days, it's not going to be installed automatically. you will you will get a warning or an error and you had to decide if you want to download it or not and it comes with other security features by default. This is why I've moved to version 11 to also help us in um blocking the download of malicious uh code in specific packages. And since we moved to PMPM version 11, that requires node version 22 and up. You cannot, as you can see, this is a requirement. You cannot use node 18, 19, 20 or any version below 22. And after that, we install the dependencies and again we are using pmppm to audit the third party packages. If everything is good, this will not fail. But if we if PMPM finds a vulnerability in a specific package uh then this step will fail. Now let's do the same thing for the back end. Uh the job is called backend vulnerability check. We are doing the same steps checking the repository. We installed Python version 3.13. And here we are using a specific GitHub action that uses the pip audit library. And here as input we give it requirements.txt because here we define the dependencies. If you were using uv or other py uh other package managers those they they generate a file called hi project.toml. If this is the case, you just change the input from requirements.txt to pip projects.totml. And you can read more. You can find more examples in their GitHub repository. Always this is what how how you should reuse these uh specific GitHub actions. Just look for their GitHub repository and read their readme. From the readme, they state that this action uses pip audit which is the default library that we use in Python to audit the dependencies. And uh if you want to learn more about the usage, just go to the usage section. And as you can see here uh for example, here they are pointing to the environment folder. Uh here is where how you can point to requirements.txt. And here you can point to more than one file. For example, if you have if you split the dependencies that are used by your application and those that are used just for development, you can provide both files. And let me show you where is the TOML one. Here it is. So if you read this, they say that to audit a project that uses pi project.toml, you use this specific uh notation. You just point to the directory where this file lives. Now let's go back to VS code and voila. You can see that it's very easy. Now we just need to go back to the orchestrator and add these jobs here. I have changed the jobs list. Now I start with the vulnerability checks. we what we have the front end and back end and we point to those two files that we have created in this session and after that I update the front end LinkedIn and backend LinkedIn jobs to depend on these vulnerability checks. You can see that here we added needs and this front end link job needs the vulnerability check to succeed before running. At the end in the pipeline success job I've also updated the needs list by adding the two security jobs. Now we can commit this. Let's say CI add security jobs and let's commit and push. Now we can go back here and uh you can see the pipeline will run again and this time we can go we can click on view details click on summary and here is our graph. Let me zoom in. You can see that we have the backend audit dependencies job and the front end vulnerability job. Both of them run in parallel and as you can see the backend lint and front end lint depend on these two jobs to succeed. If they fail the pipeline will stop. And as you can see the front end has failed. We will see why in a minute. And the back end also has failed which means that the pipeline success job will fail too. Good. And now if we go back to the food request tab, you can see that I cannot merge. This is blocked. And uh yeah, I need to solve these problems. Now let me let's verify what is happening. I will open this in a new tab and I'll open the backend job here. This is the result that we get from pip audit. And here we have a table. So name is basically the name of the package. Here we have flask cores and pi jwt. We have the current version the ID of the security problems. If you want you can take this ID open a new tab and search for it. You should be able to find the details about that vulnerability. And the severity is a I think it ranges from 0 to 10 if I remember correctly. And if it is closer to 10, that's very problematic. And as you can see, for this one, it's very critical. When you see this, it means that the you should update that package as soon as possible. But sometimes it's just medium or low. That's fine. Uh it's not very problematic. Okay. Now, let's look at the front end job. Ah, okay. So here because we are using PMPM version 11, we need to yeah migrate from uh the previous version. So we need to first in uh try to update the the pmppm lock file. Let's take this command. Let's go back to VS code. I've opened the terminal. Let me let me go through the front end folder and let's run that command. Okay. So we have we have the dependencies but here I think yeah we are using pmppm version 10. So we need to update it. So to update they give us this command to run. Let's run it. Okay. So let's run pmppm setup to fix that issue. Let's run this command. Okay. Good. Now let's try to update it again. Okay. So we need to use pmppm self update. That's the new uh method. And now we can run pmpm- version. Still says 10.32.1. Maybe I need to close and open a new terminal window. So pmppm- version. Okay. Yeah, that now that worked. So we have version 11.1.3. Let's go back to front end. Let's copy this command again. Let's past space here and hit enter. Let's say yes. And now you can see that PMP is verifying the log file for any supply chain problems. Everything went well. But here we have a problem. By default, PMPM is going to ignore ES build. So if we want to fix this, we need to run PMP approve builds or so. We can either do this or come back here because this is going to work locally. But if we move to CI, it's still going to fail and we cannot uh run this in the CI interactively. Now with PMP version 11, we have a new file called PMP workspace that we can use to modify or to update the settings. Here it is. You can read more about it. And if you want to search for uh specific values within it, you you will find you go to this [clears throat] link pm io/ settings and you can see they are talking about this specific file and what we need to do let's go back to this tab. So here they are talking about approved builds. We can search for it. Okay, now it's approved builds. Yeah, it's this one. And as you can see, we need to add this to PMPM workspace and we need to set AS build to true. Let's do that. I will go back to this tab. We are in the front end folder. Let's add PNPM workspacey. Ah, we already have it. Yeah, we already have it. Good. Ah, okay. Okay. So when you run the command, PMP PM will try to help you. Basically, as you can see, it's created the file for us and it's telling us set this to true or false. Let's set it to true. Let's open the terminal again. Yeah, let's try installing this. Copy the command. Paste it. Hit enter. Okay, it worked. Okay, let's commit these two. Let's commit this. Let's go back. to the pipeline. Ah, I need to sync the changes. Let's focus specifically on the front end job because the back end will fail. We haven't fixed that yet. Here we are setting PNPM. That's good. Can see that here it switched to version 11. That's what we want to see. We installed node and here we are installing the dependencies. Okay. So, that failed because we have 17 vulnerabilities. Okay. And now let's go back to PMPM workspace again. We need to let's look at npmrc or sorry package.json. You can see that here before I used to to resolve those problems and to do that I added the overrides object because these are transitive dependencies. I don't depend on these libraries directly. But some of my primary thirdparty libraries are using them. That's why we added overrides. But now with pmpm11, we need to move these to this pmpm workspace. And uh we can search for that here. Let's see. Okay. So we click on overrides. And as you can see here is how we do it. We can copy this format. We can let's put it here. And yeah, I just need to copy all of this and put it here. Okay, let's see. We don't need these commas. We can remove them. Yeah, I think this is the format. Now, let's remove all of that. Let's remove this from package.json. We no longer need that. And let's save. And if you are wondering how do I know all of this, you can read the migrate v uh v12 to v11 empm guide. So they have a guide that will help you migrate to this new version. Okay. Now let's save this. Let's type fix move overrides to pmppm work space commit and sync the changes. Let's open the front end job again. Remember before we got 17 vulnerabilities. Let's see how many we will get or if this change will solve will resolve all of them. Okay, we have the same problem. I think because we have moved those from package.json, we need to uh again update the log file. Let me do it again. pmppm install. Okay, so that changed the log file. Good. So this is how how we work with uh GitHub actions. You start in with something, you test it doesn't work, it gives you the error code, you fix it, and you keep iterating until you get something that works. And in the future, let's say that we we have fixed this. In the future, this job might fail. So you open it, you look at some the the step that failed, you read the error and you go fix it. And this is going this is super useful to ensure that the code that you are deploying is secure and that there are no problems in it. Okay. Uh we are the job has failed once more and now we have 14 vulnerabilities and most of them are coming from Axios. You can see that all of them are coming from Axios. Uh let's see. Yeah, all of them are coming from Axios and you can see that here the vulnerabilities they show you the range where that was found and the patched version. You can also go to the GitHub repository of that specific project, open their releases uh page and here you can read about the release. Sometimes they might have a security section in the release where they talk about the specific vulnerability that was found. So now we can install version 16.1. Uh it's the latest version. uh it was released for more than a week. So that's fine. PMPM will be able to install it. Okay, let's take this version. Let's go back to VS Code. We no longer need this, but we need to search for Axios here. Let's change the version to 16.1. Let's also again run pmppm install. Okay, good. So the the log file has changed. Let's commit and let's push the changes and hopefully this time this job will succeed. Let's go back to the pull request. Open the job in a new tab and uh let's wait. Awesome. We were we were too close. Uh but now we have one. So this this library we are not depending on it. But there is uh some third party library that uses that uses it under the hood. But you can see we went from 17 vulnerabilities to only one. And um yeah, we can add this to the transitive. Let's see. So, copy this. Go back to PNPM workspace where it is here. Here we have that version. And now we can add what was the package name? I think it was WS. Here we put this and and I think the the last version is 8.20.1. So we can fix it to that specific version. You can always verify. You can either type the package name and then add npm or you can add GitHub to the end. As you can see the last version is 820.1 and it was published 9 days ago. That's good. If it was updated just a day ago, the MPM will not allow you to download it because by default it's I think you can change the value. If you go to settings, you can override that. Uh you can set it to one day, a week or more. It depends on the severity. If a package is very popular, uh it's it's good to wait for a while before downloading the last version. Okay, let's now again run pmppm install and let's see if the log file will change. And yes, it did. Commit. Save the changes and let's push. This time it should pass. We are installing the dependencies and yes it worked. It took it took a while but we have solved the problem. Now you know how to iterate uh over this. Now we are using PMP version 11 which is strict. It uh as you saw it blocks a lot of things and we needed to do uh some migration but it's worth it once you do this. You do this once it takes some time but later it's uh you don't think about it. Awesome. Now we can uh focus on the backend job. Let's go back to the audit section and let's see what we have here. Okay. So we have two packages. We have flask cords and pi JWT. The first package is problematic because they don't have a new version. So this is the last version that they have. And pi JWT I'm not sure about that one. So let's search for it. Uh let's click here. Let's look at releases. Is it an active project? Yes, it is still active. And the last version is 2121 which is again we have the same problem with PI JWT. We don't have a new version that addresses this um vulnerability. How do you handle this case? Well, it depends. For example, in my project, I am not using Flask. I am using fast API. And I'm not sure why this is here. But if you dig deeper, you will find that a dependency that I'm using just for development is relying on this package. So I can move that specific package to a to requirements dev and I can scan only requirements. That's that's problematic too. Yeah, you solve the problem with production. So the code that you will put in production is going to be safe because we will not use this library. But if uh you you have if you run this [clears throat] locally, you might also get infected. There is another solution which is to basically remove that package if you don't need it. For example, if you used it only once. Yeah, you can remove it then. It's not it's not necessary. And as we saw in this package, the vulnerability is critical. Maybe if it was low or medium, we could skip it. uh or you could read it. Uh if it requires user input or something like that. Uh if you run it locally, you are safe. There is no going to be any issue. But yeah, this one has a critical problem. And uh so it allows SQL injection. Can see that here they say this vulnerability can lead to unauthorized data manipulation, data exfiltration, DOSs and yeah a lot of problems. But most of them so SQL injection requires an application that users interact with. So that's the problem. And here we we are not we are not we are not using this. We are using fast API for the front end. But it's it's very tricky. Sometimes you have to wait for a specific package to release a fix uh for the that specific problem. What I propose to do is to divide the requirements file to two files. one for production and the other one for local development. Let's go back to GitHub. Let's remove or let me keep that specific workflow because we need to update it. Backend vulnerability check. I'll keep that one here. I'll go to back end here. This is what we currently have. I'll copy this file. Paste it. Okay. So, it's not allowing me to copy the file. Okay, that's not an issue. We will create another one. requirements-dev.txt. Let's copy all of this. I already uh have them here. So, let's put them in this place. How do how do we split the the file into dev and one the other one for production. Basically, these libraries are used for development. For example, for pi test, we use that to run tests. We're not going we don't need this in production. So, that we can drop. We can also drop the other with the other packages by this facing kio and the coverage pre-commit that's another local one we have explained this in the previous sub chapters locust this is we this is new we haven't talked about this uh but we will see it in the upcoming chapter so I will not explain this now but we will see it later uh the other packages are re are uh important python TNV is used to load environment variables. Fast API as our back end. We use Pantic to validate input. UVON is important. Yeah. So everything else is is required by our application. So this is the first thing we will do. Uh now we have two separate requirements files. So let's do this here. Let's commit this. Now let's go back here. Here you can see that we are still pointing to requirements.txt. That's good. Uh let's just sync the changes. Let's see what will happen on GitHub. Uh let's click on backend vulnerability. It should fail but I at least we will have only one vulnerability. Yeah, we have just one which is PI JWT. This one is problematic because if I did uh more research and I found that docker are also flagging this library and here the score is seven. It's not uh like the other site that we saw before. Let me explain to you how do we get these CVE. Basically security researchers they go through packages and they look at the code to see if there is some something that is that should be fixed and in pjwt they say that it has weak encryption but the people that were that work on this project claim that no the the project is fine there is no issue and the problem that those researchers have pointed is that the length of the key uh is not very large so it can be small. So in this case where the maintainers don't want to address the CVE, we can tell this is why I kept this here. We can tell pip audit to ignore that specific CVE because it's just noise and the maintainers they are the ones who understand their project and they have the freedom to either accept the claim the CVE or reject it. because they have rejected it. We will add ignore vulnerabilities and we specify the name of that vulnerability and uh you can find that here uh it's in this in the ID column. Just copy this and paste it in this YAML file. Let's commit this. Let's think and hopefully this time this job should should not fail. Let's click on it again. Okay, we are checking the repository. We install Python. It should be cached. And let's wait for a moment. And as you can see, it says no known vulnerabilities found. And it also tells us that we ignored one vulnerability. And now if we go to summary, everything will run. So now because these security jobs has s succeeded, we now can run LinkedIn. adapt that we run unit tests. But if one step fails, the other one will not run. And here we have a problem with LinkedIn. Why? And we have a problem with unit tests. Interesting. Here we are installing pip install-r requirements.txt. But now we need to also we need to install from both of them. We keep requirements txt and we add requirements there. There is another option where you put - r requirements txt within uh requirementsdev.txt but we will keep it simple like yeah it's explicit it's uh it's not going to hide that uh specific part of the command commit and let's sync the changes. We also have a problem with linting that's uh that's weird. So let's see what is happening there. Well, we can just come let's come here and let's uh run pmppm lint and pmppm format. Okay, so we needed to format workspace. That's the issue. Okay. Uh let's type style apply formatting. I think now everything should pass hopefully. Let's close this tab and this one. Let's go back here. Let's click on view details summary and let's wait. Stage one completed successfully. Let's move to stage two. We have no problem with stage two. Now we can move to stage three. And the unit test has without any problem. Amazing. And the pipeline succeeded. Great. Now we can go back to the pull request and if we are happy we can merge the change the features that we are developing or the bug fix that we that we resolved. Amazing. You can see that uh it took us a while. Uh we had a lot of problems. We fixed an issue here. It generated other problems there. But I wanted to show you the full um the yeah the row experience. That's how you develop this. And uh it's it's it's easy. Yes. But you get a lot of errors along the way. And uh now I I think you you have a better idea of how to work with GitHub actions and how to resolve any problem that you might face. But we are not done yet. We have uh this is this was the CSA software composition analysis. Now we need to do static application security testing. What is the difference? Basically SCA doesn't look at your source code. It checks the third-party dependencies but static application security testing which is which we call SAS for short will go read your Python code and see if there are problems there. If you have password within the source code if you are using weak uh deprecated methods for example if you are using the execute function within your code that's a risky one. So this is going to check for these patterns and it's going to let you know if it found something. Let's go to VS code and let's add another job here. We will call this one backend stat check and we will put this code inside. Let's scroll down. What we have here is this is uh similar to what we have done before. The only thing that changed is bandit. Here we are using a library called Bandit. You can search for it on GitHub. And as you can see, it is a tool designed to find common security issues in Python code. That's the difference between this and uh the other two jobs that we worked on earlier. It is going to read your code. And here we are bandit with these specific arguments. Uh basically dash r means recursive. Here we are. So the working directory is backend. It's going to go in the inside the backend folder and go through all the Python files. Dash LL means so here we have two L's. This means that this is going to flag only medium or high issues. It is going to skip low problems. We will see this in the logs. If you if you don't do this, you will have a lot of issues and they are just false positives like uh what we have done with PI JWT that CVE was blocking us. But if you analyze it well, you see that in the end it's not an issue. That's why we are skipping low problems. We want to focus just on medium or high issues. And if you want to find uh if you want to learn more about this library, you can always go to the source repository and read their documentation. There you will find everything. Okay. Now we need to go back to CI and we need to add that after the backend vulnerability check and I want let me take this. I want backend linting and formatting to run after both of these tests. Okay. And uh I will add that here also. I think that's all we need to do. Now we can we commit this and let's think the changes. Let's go back to GitHub. I think it should pass because uh I am not using any unsecure code in my back end. But let's verify or refresh the page. Sometimes it gets stuck. Uh I have been refreshing the page uh for a few minutes but it is still stuck. And when that happens, sadly, GitHub, they don't show you that there is a syntax error. But here you can see that I have missed a comma. That's the problem. And that's why the pipeline didn't start right away. Okay. And now it's working. Yeah, that they should they should fix this uh somehow because if you don't know that, they should tell you that it failed because of a syntax error. But anyways, now we know if something gets stuck uh that means that you have a syntax error somewhere. And here we are running those jobs in parallel. You can see that backend Linton is uh requires both of these jobs to succeed before running. And as I told you, Bandit will not fail. But let's look at the logs because I want to show you that we have so Banditis found 1,900 lines of code. Here are the metrics. So you can see that we have 159 low issues. If we didn't skip low issues, you can see that it will be a nightmare to look at all of these problems. And most of them are going to be false positive. These aren't real problems in your code. But if we had something in medium or high, now we should look at those seriously. And here you might be conf So don't confuse issues and confidence. So here they have total issues by severity. So these are basically if we have an issue is it severe or not in our case no they are low and confidence is that here it is set to high. This doesn't mean that all of these problems are of a high severity. No it's just the confidence level. So here this means that bandit is confident that all of these are low problems that we that we should skip and don't care about. It seems that this is working. The pipeline has succeeded. Everything has passed and now we can merge without any problem. But before we end this tutorial, I would like to show you how banditis fails. That's important. Um let's open let me open main.py. I have added this because it uses an outdated cryptography module. Um I've just added something just to trigger banditis. So let's see. Sure trigger banditis, but I'm going to remove it right away. I just want to show you what happens when Bandit fails. This will Yeah. Okay. So, let's wait for this. Okay. So, it is it is running. Uh let's let's click on Bandit. That's the one we care about. Did it fail? Yes, it did. So, the exit code is one. And now we have one high problem. And if you look closely, okay, so it is running here. It says issue. Uh so it it found that we used the MDA hash and it's uh yeah it's not uh secure. This is an outdated module that we should not use. So here they show you the where where it found the problem. Here is the location. It's online 18. And you can see that now we have one in the high severity category. That confirms that bandit is working and uh it it is reading the code. Uh it's not like the other the other ones. So now we can remove this safely. Uh we've confirmed that Bandis is reading the code and uh let's wait for this job to finish one more time. I want to ensure that every every job passes and that I can merge without any issue. I am back. The pipeline took a minute and 37 seconds. It succeeded which is a great sign. And uh and yeah, we have reached the end of this tutorial. You know, I've really enjoyed making this video. Uh it was challenging. Uh we have hit a lot of problems and we fixed them uh interactively. I hope that you enjoyed it too and that you have learned how to deal with securing your application. You can see that the pipeline is starting to get bigger and bigger. We are adding we are making sure that we develop a robust pipeline and by the end of the of this module you will get something that uh that is really great and that will help you uh find bugs easily and uh ensure that when you hit deploy that your application will run without any issue. In the next sub chapter which is titled advanced testing and dust. Dust is you can see that here we have sust and dust is dynamic. So we will add that one too. Uh we will still continue working on testing and security. So far we have just unit tests but we will add integration tests and end to end tests. Uh we will we will see we will see that in details but I just wanted you to know what is the next step in our course. Thank you again for watching. See you in the next sub chapter. In this sub chapter we are going to talk about advanced testing. Here we are going to add both integration and end to end tests and I'm going to show you something called dust. Here is the agenda that we will follow. As I said, we will start by writing integration tests. And here we are going to test a live me search instance because this is the search engine that I am using on my personal project. We are going to perform end to end tests using the the playright tool in order to simulate real users. And finally, we are going to look at dynamic application security testing. And we're going to do that with this tool OASP zap. If you want to follow with me, every command will be here. Just go to this GitHub repository, open the book folder and here we are in chapter number five. Just search DOS and you will find the this new sub chapter advanced testing and dust. And here you have all the commands that I will run here. And this is explained uh in detail. I will now close this tab because we no longer need it. And here you can see that I haven't merged this pull request yet. We will keep using it for uh the upcoming videos. And as you can see at the end uh if I click on this icon, you can see that we have all these tests. We can click on details here. Let's click on summary. And currently this is what we have in this video. We are going to add integration tests and end to end tests before the final pipeline success step. Let's start doing this. Open the GitHub folder. Open workflows. And here let's you can see that we have backend integration tests. This is what we want. But before we do that, let's change the branch to what did I call it? Test number two. Okay, let's do it here. Test number two. Okay, now we don't have that file. So, we need to create it. Click on this icon and add this file backend integration tests. I will copy and paste the code. And here, let me explain to you what we have. There are some things that we have seen before like checking out the repository. I will collapse that. Installing Python, installing the dependencies. All of these steps are known and uh we will skip them in this uh sub chapter. We won't we don't want to waste time here. The new stuff is the main search environment variables and the steps to launch that service. And let me explain to you why do we need integration tests. If I go back to GitHub and open the pipeline, as you can see, we have some tests already. We call them unit tests. These are important tests and they test few things in the application in isolation. For example, if I have a button or a function in the back end that converts time into a specific format, I can test that function in isolation. And let's say if I have if I am making requests to m search, I am going to mock that. I am going to uh not talk to M searcharch in unit tests because here we are just testing the individual functions or functionalities. Integration tests are a bit different. They test integrating external services like me search or if you have let's say a postgress SQL database or other services you test them in integration tests. And here when if I want to make a let's say a search query to me search I am going I need an instance of me search running to be able to do that that's why here in this uh workflow we are going to set up m search so that it runs in this job and so that we can make requests to it. Here we have a few environment variables. Uh these are specific to me search. So if you are using other services you need to change this part but we have a master key a URL and a name index name. Here we will store documents. I will explain these two environment variables down below because they are important. Okay let's start. Here we have the download me search binary step. So after installing the dependencies, Python and checking out the repository, we need to install me search and it is a binary file. So we can download it easily using this curl command. And uh this this also needs to change if you use other services. And here we have these two variables max retries and retry delay. Sometimes in the GitHub actions this step will fail and you won't be able to download M search and uh you might say oops the the integration tests have failed but actually the problem was just network and uh we just need to retry this step multiple times until we download mainly search. So we are here uh redefining the variables max retries and retry delay. And here we are using a for loop to uh try downloading me search max retries times and in this case it is set to three but you can change the value to something higher. If this keeps failing and if this step succeeds we will show my search was downloaded and we will break out of the loop. Otherwise we will exit with one and uh tell GitHub that this step has failed. Okay. So here we download me search. After that we need to start it. And in order to start it we just run the binary and we pass the master key. And here notice what I have what I have added at the end. I have this amperand. And this is very important. If you don't add this, this call will block this job or this step and we won't be able to run the next steps. But this by adding an amperand, it will run this as a back as a background job and it will allow the next steps to run without being blocked. And here when we run medarch, we need to ensure that the server has started. And the way we do that is we call the health endpoint. If this endpoint replies it means that M search is ready to accept requests. That's this is what we are doing here. And again we are using a for loop so that we keep pinging this endpoint. If we detect available great we are going to show this in the logs and we will break out. Otherwise we will sleep a little bit to uh retry. We don't want to do this quickly. And here uh at the end we just if this uh doesn't uh work we will show that the me search fails to start after the number of attempts that we have here. If this succeeds we will run the integration tests and uh again we will use piest and in my case I I am storing you can see that I try to group tests by their nature. Here I have all of my unit tests. Here I have integration tests and I also have load testing. We might see this in upcoming uh subchapters. And as you can see here I am using tbhore so that the exceptions are not very verbose and I passed the environment variables because we need them inside the tests. So the only thing that I want you to take from this workflow is that in integration tests you need to start your services. Uh if you have me search this is how you do it. If you have Postgress SQL, you need to do it differently. If you are having services in Docker, you need to run the containers, stuff like that. And after running all of your services, you can run your tests. And the beauty is that here if something broke, it's going to uh be uh very informative because you are testing the live services. Awesome. Now, we can go back to CI.YAML YAML and add this integration test there commit and we can see if that will fail or not. Let's open CI.YAML and here let's add that job. We have backend integration tests. I want to run it after unit tests. And uh here is the file. I need to copy this and also add it here below backend unit tests. And I guess that's it. Let's commit this. I have pushed the code. Now let's go back to GitHub. Let's refresh the page and let's see if this is going to uh fail or not. Let's open the details page and let's see what we have. I want to go back to summary. I want to see how this looks like. And yeah, everything is good. So we have unit tests and after that we will run the integration tests. I will wait for all these steps to finish and I will come back. And as you can see here, we have a problem with audits. Probably something in the front end changed. But this is not a problem. We now know how to solve this. Uh I will just focus on uh the integration tests and then maybe we will we can come back here and solve the problems that we have. Now we are in the unit tests phase. [snorts] Let's wait for it. Okay. Uh let's click on backend integration tests because I want to see the logs. Okay. So far so good. These are the steps that we that we have in every job. Okay, great. Now we are downloading Millie search and as you can see this time it worked. Here we we are showing attempt one of three and yeah we are still trying to download it and that that worked great and you can see that they show us how to run it. Now here in this step we are running it and as you can see uh these are the logs and awesome that worked. So far so good. Uh let's collapse all of these steps. Okay, but I think yeah, we have a problem here. Import error while loading conf test. Okay, so it couldn't import something because it doesn't understand fast API. Interesting. So I guess we need to fix something when we in when we are installing the dependencies. Okay, let's go back here. So we are installing requirements dev and uh what happens what what's the problem? So if we go back to back end we have divided the requirements into two files. Ah I see so I forgot to add - r. Yeah the difference here is that we need to also do this. So we need to do - r requirements txt or we can do it here. So we do r requirements.txt. txt space-ash r requirementsdev.txt txt and u yeah this time I will do it this way pip will go inside requirements dev it's going to find this it's going to open requirements txt and it's going to install fast API okay let's commit this and let's push let's go back to GitHub the pipeline has started let's let's see the details let's open the integration tests and I think this time it should work. Let's collapse all of these steps. We've installed M searcharch successfully. We have started it. Okay. And now we are running integration tests and as you can see we didn't have we didn't get the same error and they succeeded. Great. So that worked. Let's go back here to summary. As you can see, both tests uh run successfully, but the pipeline didn't succeed because we have a problem in the audit dependencies job. And as you can see here, we have a problem with JS cookie vest. So these are the two libraries that we need to fix. Let's do that. While we are here, remember that we have PNPM workspace and here we can add the libraries. uh we need to override them because in my case I am not using JS cookie that's why I need to put it here in the overrides object and what was the the version 306 okay so let's go back here and let's put 306 that's the first one and what what else we have test so for test I am using it so if we open package JSON You can see that I have a V test. Here it is. And the here the version is 4014. Okay, that's why. Yeah, that's why it will it is complaining. So, we need to upgrade that to for the 1.0. Let's go inside the front end folder. And here, let's try to install the newest version. Here is the full command. Let me open package.json JSON again just to verify that we have V test. Yes, it is in the dev dependencies. This is why we are adding dash D and yeah let's install the newest version. Okay, it is installing and we are using PMP version 10 because this is a a fresh virtual machine. So that's fine. You can see that vest uh we were able to update it upgrade it to 4.1 and uh yeah let's see what we have here package.json yeah that worked. I have updated pmppm to version 11 because uh I don't want to see any error in uh this PCI pipeline. Let's run pmp install just to ensure that nothing uh oh sorry I need to go to front end and let's run pmppm install. Uh let's say yes. Okay, we are good to go. Let me close the terminal and let's say sure update dependencies. Let's sync the changes and hopefully this time the pipeline will succeed. We will let that one running and I will come back to it. But now we need to add let's see we need to add end to end tests. Let's open workflows again and let's create this file end to end tests. I will copy the code and I will paste it here. Uh let me collapse this and let's look at this environment variables. This is identical to what we have there. And in end to end test we need to have everything running. We need to have the front end, back end, and all services. So here we have all of these environment variables for me search because we need to start the mele search service. And let's see what we have here. Here we added timeout minutes 30 minutes. Uh we don't want so if end to end test for some reason take more than 30 minutes, we want to time out. This depends on how many tests you have and how and the complexity, but for me 30 minutes is more than enough. Here we are checking the repository. We install Python here. Let's see. So we want to install R requirements and here let me add dev.txt because we want to run uh maybe this is not important here. Yeah, it's not important because we won't run it. We will be right we will be using the playright tool and that one runs in the front end. That's why we just need the production dependencies to run the fast API service or server. Here we download me search. This is identical to what we have in the integration tests. We start search and here we start the backend server. We are using uicorn and again we have the same for loop. We ping the health endpoint and if it returns okay that's great. We know that the backend has started otherwise we exit. And here we have a step that I called see the end to end test data. When we create when we download search and start it, it's going to be empty. We won't have any data to play with. In end to end tests, we need to have data in the browser so that we can interact with uh articles or products or any it depends on the on the project that you are working on. And in my case, I want to put data in my research. So I have created a a specific file that I called test data that will insert documents to many search. You can see that here is the index that I am using for tests. It's called articles in test here and here here are all the documents that I will inject just for end to end tests. If you have Postgress or other databases, you can do the same something similar where you seed your uh where you seed the test data and after that we install here we have we use PNPM version 11 we install node we install the frontend dependencies and now we arrived at the playright tool. Okay. So we install it we check the version the version is coming from again package.json. Uh if we look at playright test here it is we take this specific version and display it in the logs. Okay. After that we cache play right because here we in we need to download browsers and uh this depends on your configuration. If you some if you want to run the test only on chrome this step will download Chrome. If you want to also add Firefox it's going to download Firefox as well. If you want to to do it on Safari, you will install Safari. So, and if the browsers are the same, we need to cache them so that next time when we run end to end tests, they are going to be there and we are just going to use them. And for that, we are using uh an action called cache, which is a very handy uh GitHub action that allows us to cache stuff and uh as you can see the continuation is here. So here we basically define the keys where we want to store um the cache for playright and here we just check. So here if there is no cache we will execute this function that will install here I want to run the tests on Chrome and Firefox I don't want to use Safari otherwise if we if we have downloaded the the browsers previously we will just uh go here and as you can see we have an if block so if we have the cache so we are checking if it is true that means we can just reuse that and we don't need so if we look at the difference between the You can see that here we have with dependencies and this takes more time. Uh but if we have things in the cache that will make things faster. So after all of these steps you can see that this is why I said that end to end tests uh this workflow is going to involve a lot of uh work but we are just reusing what we already have in the other workflows. So after setting up all of these s servers we can run the end to end tests and uh in package.json JSON I have this script that will run end to end tests and here I pass environment CI to true to differentiate between because you can run end to end tests locally on your laptop and or in this case we run it on G using a GitHub runner and finally after the the end of uh the tests we will upload the report we will use the upload artifact action and that will allow us to download the report and reports are amazing. Locally when the tests finish uh playright will open a new tab and show you the report. But here since we are running this on GitHub we can't open a new tab. So we need to download a file that will that contains the report. We can check it later. We can see screenshots. Yeah, it's uh it's really handy. Okay. So this is the end to end tests and this will run as I said on the cloud using GitHub actions. But what if you want to run this locally? For that I mean here on the repository everything is set up already. Uh you can see that we have this playright configuration file that will come when you install playright. And the way you do it is simple. You just you just run pnpm create playrite and this will install playright for you. When you do this the playright configuration JSON will be created with some default values. Maybe there um this command will also create a uh some tests so that you can just uh make sure that playright is running but you can delete all of that. Let's run the command together and let's see what happens. You can see that here it says do you want to use typescript or javascript. In my case this project was using javascript that's why I kept javascript but if you rely on typescript uh change that to typescript and here they tell you where to put your end toend tests. Uh in my case if I look at the front end I always try to group my tests so they are inside the end to end tests. So here the default value is end to end. Okay let's just keep it as end to end test. uh add a GitHub actions workflow. I mean no because we have we did it ourselves. And here this step tells you do you want to install playright browsers? This can be done manually via pmppm execute playright install. And this is the exact command that we have here. As you can see pmppm exe execute playright. Let's install them because in this machine we don't have them. install play operating system dependencies. Why not? And you do this once and later you don't need to uh install the browsers every single time. Okay, so this finished. There was something that was added to uh the logs to the log files, but I I won't change this. It's not important. So I'll just undo this. I just wanted to show you the steps that you need to run. And uh okay, let's see what we have here. Okay. So, we didn't override the file because it found it. But uh in your case, if you don't have this file, this is going to be created for you. So, let's go through it together in order to understand what we have here. First of all, we tell playright that our end to end tests are located inside this folder. We want to run things in parallel so that tests uh finish quickly. Here we set the number of retries. So if we are on CI sometimes tests fail randomly so we want to retry them but if you are locally we won't retry anything here forbid only is sometimes when you run end to end tests you can tell player to run just one but in our CI job we want to run everything so this will fail if you tell player to run one one test only because it's going to skip the other ones and we don't want to allow that. Here we have reporter. This is also important because when you run uh tests locally as I said you can get an HTML page that will show you what failed, what succeeded. Uh but if we are in CI we are running this on GitHub. So we need to to tell play that please show us nice logs on GitHub. Here we define the projects. You can see that I I am using Chromium and Firefox. If you want you can also add Safari down below. Uh you can change this however you like. And finally we have something very interesting which is web server. This is this will allow playright to run the server the front end for you. So if you forget to add a step to run the front end playright will use this command to start it for you and you tell it the URL or and here the use existing server is uh is interesting. So if uh we started the server manually play will see that oh the user has started the server so I don't need to run this command. This is why we use uh reuse existing server and again we have timeout. Awesome. So now we can close this. Let's open uh ci.yml and let's add end to end tests there. Okay we have integration tests. Let's add end to end test underneath it. Copy this and let's paste it here and let's see what did I do in the needs um list. So I am waiting for the integration tests and also unit tests uh in the front from the front end. Okay, let's now commit this add end to end tests commit and push. Okay, uh the tests are running. Let's look at the details page. Let's go back to summary and let's ensure that the end to end test is correctly placed. Yes, it is. You can see that it is waiting for the front end unit tests and the backend integration tests. I will wait for these jobs to finish and I will come back once the end to end tests uh start. I am back and as you can see so far everything is green. Uh and wait yes the end to end tests started because uh front the unit tests in front end and integration tests in back end didn't fail. Let's click here and let's see what is happening. We are now installing the backend dependencies. We are downloading millarch. Good. That worked. Now we are starting the mill search server. That worked also. Let's now start the backend server using uicorn. And as you can see, we were able to ping the health endpoint. We seeded the M search with 15 documents. We've installed PNPM node. And now we are installing the playright browsers. And as you can see, this takes time because now as you can see, we are in the if no cache state in the upcoming commits. Uh this will not run and it's instead it's going to to download to take the browsers from the cache. Okay. Yeah, as I said, this takes some time. Where are we at? Okay, so it took 30 seconds. Okay, and now we are you can see that this the other the the other step was skipped because we don't have any cache and now we are running the end to end tests and as you can see we have 178 tests and here we are using two workers. So they are going to run the tests in parallel. You can see that the tests are uh so here we are running the first one, second one, third one but now they start randomly because we are running them uh not in a sequential order but in parallel. Okay, end to end tests usually take minutes to finish and it depends on how you uh design them. Mine uh mine will take around 3 minutes to finish. So I will stop the video here and I will come back. We are almost done. Uh we have 178 tests. Let's see. So they that should finish now. And uh all of them succeeded. You can see it took 3 minutes. And here, okay, we are trying to upload the report. And awesome. Everything works. Let's go back to summary. Amazing. You can see that the pi the whole pipeline took almost 7 minutes. Okay, let's wait for the last or let's go back. Let's go back here. Uh yeah, everything passed. Amazing. Now what I would like to do is I want to make a small change just to show you that the cache is working. Let's see what should I do here. I will just remove this comma from this file cuz it's not needed. So sure, remove extra comma and let's push this. Let's go back to GitHub. Okay, everything is uh okay. Let's click on summary and let's wait for end to end tests to start and hopefully this time we should see that we will be uh we will uh run the if cache uh step. The end to end tests job has started. Let's wait for Okay, so we are now in the front end area. Okay, we are installing the dependencies. Let's see what we have here. So we are looking at the cache and as you can see that worked. Uh let me collapse this. Before we ran this step if no cache but now we run we are running this other step if cash hit. Before this step took 30 seconds but now this one took almost half of that. It's just 15 seconds but it's it's working. It's better than nothing. And yeah, the end to end test will succeed and uh uh yeah, everything will work. Let's create a new workflow called dast scan. I will copy the code and paste it here and let's see what it does. First of all, you can see that we are we also need my search because here we are going to scan the API. First of all, we are checking the repository. We set up Python. We install the dependencies. We download me search. We run it. Here we see the data because we want to have something in me search. We start the back end. And uh finally this is the new step uh that will use the zap project. And if you are not familiar with zap here it is. You can check their website. Uh it's an amazing project. It's free and open source. And uh we you can use it to scan your applications. And they have good documentation as well. We will use this in this video because by default this scan is going to flag a lot of things that are uh that are not problematic because we are not in production. Well, I will show you I will show you that. So let's look at this step. You can see that we are using a GitHub action provided by the Zap team. And here the target is the open API endpoint. Because we are using fast API by default it gives us all the endpoints in our back end. And this is useful because Zap will use this in order to know which endpoints to target. The format is open API. And here you can see that I'm referring to a file uh called zap rules. Right now I will just delete this one because we don't have it and I will I want to do this so that you understand the reason why we add this specific file. I have deleted that line. Now open ci.l because we need to add this scan here in the main pipeline. I will scroll up. Let's look at backend length and format. I want to put it uh or I want to run it after LinkedIn. So, let me let's go down below. Let's paste it here. And as you can see uh it's going to run after the l after the lint job. Now, copy it and paste it here. Now, let's commit this. Add dust and let's push it to the repository. Let's go back. Let's click on the the pull request. Scroll down and let's see. We should see the pipeline running. Okay, that's good. Let's try to see to look at the details. Click on summary and let's wait for dust to run. As you can see, it should be right after. Yeah, it is after uh backend link and format. You can see that right now we have two kinds of tests. We have sust and dust. The difference is that SAS checks your row source code for security flows. But some security problems only appear while interacting with with your application. This is where Dusk comes in and this is why we started mini search and the back end and we gave it as the target the open API spec so that when it looks at that at those endpoints it's going to attack them like any uh attacker would do uh in order to find vulnerabilities in your application. And as you can see right now it failed. Okay. So why did it fail? It failed because it couldn't download me search. You can see that even though we did three attempts, so the first one failed, the second one failed and the third one also failed. Uh if this happens for you, no problem. You can just go back and rerun the job. Okay, so I can run rerun failed jobs. Uh it gives me all of these. So I'll hit rerun. Okay, let's wait for now. Let's click again on this job. Hopefully this time we will be able to download Mary search. Sadly it failed again for some reason. I'm not sure why. I guess we just need to wait a little bit and retry. They tell us to download this manually from GitHub. But yeah, we cannot do that here in the pipeline. We we will need to change that specific step. Okay, I will just I'll try to rerun it one more time. If it fails again, I will wait for a few minutes and come back. I did some research and I think the problem that we have here is that if we look at their latest version, you can see that it was released 14 minutes ago. But creating the assets take time. You can see that for example this one was just created a minute ago even though the release was pushed 15 minutes ago. So that's why we are getting 404s because um the installer script is trying to download something from GitHub that doesn't exist yet. This is why we are getting uh 404 not found. So we have two options here. We need to wait a little bit to get the new file and then we can run the script again or in the future we need to update this to download a specific version because here when we run the installer script we try to download the latest version or the latest tag from GitHub and in this case it is 146.1 right now I will keep the script like this. But uh right after uh explaining the dust scan, I will go back and I will show you how to update only this specific step to uh download a specific version. And uh yeah, we will remove this line because it downloads all it tries to download the latest version always. Okay, I rerun the script and uh as you can see it worked because now we we have uh the assets were created and uh the installer script was able to get it from GitHub. Awesome. So now let's see as you can see the zap scan step is running and uh okay so here let's see what we have. We see a deprecation warning that's fine. We can uh avoid that. You can see that here it found 15 URLs and it was able to find them because we provided the open API spec and uh here I want to mention that the dust scan that we are using is lightweight because it is targeting just the back end but we can also have a serious one that will check the front end as well. That one takes a lot of time. This is why I didn't include it in the main pipeline. Instead, we will create another file that will uh have the full dust scan and we will run it daily um outside of the the pipeline because we don't want to block everything just to perform that heavy scan and as as you can see this thing has failed and uh I was expecting that. Uh let's see what we have here. You can see that it checks a lot of things and most of them passed. As you can see, everything passed except some problems. And as you can see here, it says X content type options header is missing. Uh let's see what we have. We also have cross origin resource policy missing. And as I told you, we will have these problems because we are running this in a CI environment. It's not running in production. And if you remember in the past we have we have resolved these problems and in our application that we deployed we are using engineext we added the HTTP security headers so everything is set up already and zap is telling us that these are missings that's that's correct because we are running on a CI environment this is why we add that specific file that I was talking about because it allows us to tell zap to skip those specific tests. Here I will just add rules file name and we need to create this one in the GitHub folder. Uh let's create it here. Uh it's already there. Okay. So let's look at it. Here is the zap rules file and this is specific to my codebase. So if you add dust to your codebase, you might have different warnings. So in that case you need to change this list. So how do we do this? If I open let let me show you. So here we have ids and the format is simple. You have the ID of the rule. It's the first column. You have the action and you have a comment. So here we tell zap to ignore all of these rules because here the action is ignore. And this comment is for us so that we can understand what is happening here. How do we get these rule ids? You can get them from their documentation. You can see that here. For example, if we open this active scan rules, you have the ids here. So they they show you the rule, they explain it and they also give you the ID. There is also this website that I will make sure to drop in the video description where you can find uh every every rule. And here if we search for cross origin which is basically this error that we got here. Now you can see the ID is 90,000 and4. Here it is. You can see that we have uh uh a lot of them here. And yeah you can use this website to uh learn more about uh these specific rules. And if you want to read it you can click on it and you will find here a summary solution etc. Okay. And uh we can you can also find the ids here directly from the logs. Uh so for example here I can take this id and uh paste it here in the rules. Uh here it is. You can see 10,21 uh and I have added it here. Once you do this the errors are going to go away. But make sure to not skip everything. Skip only the things that are related to a production environment. And as you can see here, many things are handled by EngineX. That's why I've added them here. And for example, this one is failing because we are running this as HTTP. We are not using HTTPS. And again, because we are not in production. This is fine. There is no problem with that. Here we have a problem with timestamps because M search uses Unix timestamps. It doesn't use other formats. And here uh Zap is thinking that this is a problem. But in fact that's how med search works. Okay. Now what I will try to update this uh so let's say CI add zap rules commit this. Let's sync the changes and let's see what will happen this time. I'll go back to the pull requests. Let's look at the pipeline in detail. Go to summary. Let's click on OASP zap API scan. Uh we need to wait for it first to start. Okay, let's see the now we are auditing the backend dependencies. This should pass without any problem. Great, that worked. Now let's go to LinkedIn and formatting. And now let's look at the dust scan. The scan has started again. It found 15 URLs. And as you can see, it is using our zap rules file. Okay, let's see now what will happen. Hopefully, it's uh it's not going to fail. And yes, it didn't. Let's see what we have here. Okay, you can see that they tested 197 uh rules or URLs. Um so all of them passed and again we have those problems with timestamp, cross origin, etc. because we have added them to zap rules the zap project or the zap tool is not failing it is skipping them and u that's why uh the pipeline has passed something to note is that over time this file will stabilize I'm talking about zap rules because especially if you keep adding more endpoints if you keep expanding your backend application yeah zap is going to find some uh small issues that you can skip. After you do that job once, when you get an error in your CI pipeline, it means that you have a real security issue that needs your immediate attention. Right now, uh you can see that we got a few errors because uh we are just setting up D for the first time and that's normal because uh by default these tools are trying to find anything. So that's why you get false alarms. But once you handle those, you you are good to go. And uh when you get an error, that's a good that's a good signal that something bad is happening. Great. And as you can see, the other steps are are also running without any problem. So we were able to run integration tests. End to end test should also pass. And finally, we will uh the pipeline will succeed. Awesome. We have done everything uh in this sub chapter. But uh let's let me also show you how to update the download step so that you can install a specific version. I'm not going to apply that to the to all files because I like to download the last version and I don't want to go update the the file and then commit to be able to download the last version. But if you are curious about how we can accomplish this, here is how to do this. I will uh remove all of this and I will paste this. Okay. And now what do we need to do? We need to go to GitHub. Click on releases. And here the last one is 146.1. I will take this. I will take this one. So here we need to change this variable to uh target this specific tag. Here we are just echoing this message. And uh here in the curl command we have uh this handy arguments that we can use. Before we uh we had to add these environment variables. Yeah. Because we had an external loop. But here we can do that. We can do it directly with curl. So if uh the command fails we retry three times and between each retry we wait for 5 seconds. And we are we go to the releases page download version and we download this specific binary. After we do that we uh make it executable so that we can start it. And uh you can see it is smaller than the previous solution. But this one requires you to always open the file, update this commit, push and wait for the pipeline to finish. That's the the difference between the two. But if you prefer this, you can keep it. Uh in my case, as I said, I will not keep that one. I I like to always download the last version. We have arrived at the end of this sub chapter. I really hope that you enjoyed it. In my case, I I did. And uh it's always great to struggle to build a pipeline because once you do, uh you you see this uh amazing view where everything is green. And um yeah, it's super super useful. And now we finished the continuation the continuous integration pipeline. In the next uh subchapter we will work on continuous delivery which is also amazing. Uh we will connect this pipeline to our virtual machine on digital ocean. to that if uh this step which I'm I'm referring to continuous integration passes we will click on a button to deploy the built application directly without having to SSH to the VM copy the code run some some commands we will create a a workflow that will do this for us and we will just hit the button wait for a minute or two and see the new version live on the website. We will also update this pipeline because you can see that pipeline success depends on every job before it. So the so the graph looks a little bit ugly. But we just need to look at the last job. We we need to wait for end to end tests because if we wait for this job, we are waiting for this one. This one we are waiting for every previous job. So we don't need to uh make this uh let's go back to CI we can simplify this needs to just uh the end to end tests we know we don't need to have all of them because implicit they are implicitly uh within the end to end tests if we are waiting for that specific job. So we will do some optimizations to make the pipeline uh cleaner and uh yeah that sub chapter will be the the last one in module 5. After that we will move to module six which is the last module in our course. See you in the next sub chapter. We have arrived at continuous delivery. This is the last subchapter in module 5 and here are the things that we are going to talk about. I will show you how to compile and upload the static view front end. You will trigger automated database and search engine backups. Before deploying, we are going to require a human review using GitHub environments. And finally, we will execute an atomic virtual environment roll back strategy. So if something bad happened, we can go back to the previous working version. In the [clears throat] last sub chapter, we have built this pipeline and the last step is end to end tests. What are we going to do before moving to continuous delivery is to also add front end build. So we will try to build the front end to make sure that there is no syntax error that there is no problem in the dependencies that we are using before finishing the continuous integration pipeline. I have I forgot to add this in the previous sub chapter but we are going to add it now. Open the GitHub folder. Open workflows and add this new file content build.yml. But before I do that I need to change to test number two because this is the branch that we were using. Okay, let's now try to create the file. Okay, now we are able to create it. I will copy and paste the code here. It is similar to what we have done before. The name here is build front end. First we check the repository. We use pmppm version 11. We install the dependencies and after that we just run pmppm build. If everything goes all right, that's what we want. But if something bad happens, this job will fail. After that, we are going to upload the artifact so that if we want, we can reuse the build files in a next job or something like that. And the retention days is set to one because because if you add another feature, we are not going to use this uh cache. So there is no need for it. It's going to become old and it's going to be uh removed. Now let's open ci.l and let's add this job there. Here is how we do this. I'm going to come here and I want this to depend on end to end tests because if this succeeds I know that at least there is no problem in the code so we can build and here I will copy the job name and let's put it below dust check and that's all we need to add now commit all of this let's go back to GitHub let's look at the put requests the pipeline has started I will Wait for the pipeline to finish and I will come back. I am back and as you can see the front end build job has started. Let's click on it to make sure that it works. Okay. So now we are building the front end as you can see and it should work. Uh this is just a step that I wanted to add to ensure that building the front end succeeds. Great. And as you can see we get a green check mark. Amazing. Now let's go back to the pull request. Let's click on pull requests here. Let's click on this one. And let's make sure that everything is green. Awesome. So now we have finished working on continuous integration. Now we will work on continuous delivery. Before we start deploying the new built files to digital ocean, I would like to start with uh create working on two things. First of all, a backup management system. This is going to ensure that the servers disk does not fill up because if we keep creating backups for let's say mail search uh and maybe databases if we have them that will eat a lot of storage and because we are using a cheap digital ocean uh virtual machine that's we don't have the luxury to uh waste space. So we are going to create a script that will clean the backups for us and keep only the last five. That means we need to SSH to the server. Let me do that quickly. I am back and as you can see I am logged in. Now what I will try to do is the following. I will create this clean backups script in this location. Now I need to provide my password. Great. Now I will copy and paste the following code and I will explain it to you. First we have this keep count variable. Basically this this is going to uh control how many backups we want to keep. And by default it's it is set to five. I will keep it that like that but if we want we can change it. Here we have a we have an offset variable that will add one to keep count. We are doing this because of this uh huge command that we are running here. So I will explain what we are doing here. Basically this is a function that we are calling here on two folders. In my case in this application that I am deploying I have mainly search and I also have a SQLite database. So for me search the backup is going to be stored in the dumps folder and for SQLite I will store that in the backups folder inside the web application. So what I want is that I want to give this path to this function and keep only the last five files inside each folder and this is how we do it. So the target is basically going to be the path that we are passing to the function. If if the path exists, we are going to show in the screen that we are cleaning it. And um after that here here is what we are doing. First of all, we are using ls-tp so that we can sort the files by time. Here we use grap-v and here we are inverting the search so that we uh skip folders. This command will only keep files. And here we have tail dash n and we pass the offset here and the plus that we are passing here is going to basically give us all the files that are older than uh offsets. So for example if offset is set to six we are going to start from there and it's going to give me the backup that was from day six day seven etc. And we use X arcs to pass each file to the remove command. It's going to be put here so that we can remove it. And this is how this function works. And if you want you can play with this command on your own. You can copy copy paste it and see how it how it works. So if you now I can save the file. I will exit. Let me show you. Let's go back to web app. And as you can see so far I don't have any backups because I haven't deployed from GitHub. But once we start doing that the backup folder will be created and uh in terms of med search we will have the backups in the dumps folder. Now we need to make the script executable using sudo chode plus x. That's done. Great. Now we need to do something interesting. So we need to run sudo visudo. This is going to allow us to run commands that typically require us to enter the password because we need to run them using pseudo. But we are going to skip that. We are going to run them without a password. Why do we need to do that? Because uh in g on GitHub, we are going to have a job that will tell the virtual machine let's say to restart engineext or to restart your uh backend application. And for that we need to run systemctl reload engineext and but this requires us to enter sudo. We are going to run this command. We go at the end of the file and here we need to add the following. I will paste this. I'll go back to the beginning and I will change the username to mine. So for example my username is IAD but for make sure to change this one. So here what do we have? You can see that here we specify that we want to run these commands without a password. And the commands that we that we want to allow are systemctl reload engine x supervisor ctl restart all and the cleanup scripts. Other than that we the other commands are going to require a password. Never never never uh remove password because that's insecure. Let's say that someone was able to steal your SSH keys somehow and they were able to log into the virtual machine. The only thing they can do is either reload EngineX, reload, restart supervisor and uh run the cleanup com backup script. If they want to, let's say remove something from a a directory owned by sudo by the root user, they won't be able to because uh Linux will prompt them for the password. Okay. Now save the file and exit. Now I would like to also explain to you this specific file. Let's go. Let's use cat and let's call that specific file rs sync exclude. Here is what we have. What why do we need to do this? Because we want to copy the source code from GitHub and put it here on digital ocean in this specific virtual machine. And the way uh we are going to accomplish this is with a command called r sync. This is an amazing tool that allows you to copy files. But the problem is that it's going to make sure that what we have here on the virtual machine matches what we have on GitHub. And the this causes a problem because let's say that I added a file that I want to have here on the virtual machine but that I don't want to push on GitHub. The problem is that rsync is going to delete this specific file that I added here on the virtual machine. This is why I have added all of this here. You can see that in rs sync exclude I tell rync to never delete these files from the virtual machine. Keep them and uh keep them on top of what we have on GitHub. Okay. So for example we are keeping backups, we keep the same file, we keep node modules etc. These are things that I want to keep here when I run the rs sync command. I am showing you this because now we are just preparing. We haven't yet wrote wrote the deploy job but we will see these things in that yl file. So don't worry about this. We are just preparing for that step. Great. So now we need to go to go to GitHub because we need to create an environment. I will show you how to do this. Click on settings. Click on environments. Click on new environment. Here let's call it production and click on configure environment. Now click on required reviewers. We are doing this so that as I said when before deploying we will have certain number of people that will receive an email so that if they want they can deploy the application. We don't want the deployment to be automatic. So that we just have one layer of confirmation before confirming. Here I will put my own account but if I have other team members I can add them here and just click on save protection rules. Now we need to create a few secrets. For that we need to go here click on secrets and variables and click on actions. These are secrets that are going to be used in the deploy job. For example, to connect to the virtual machine, we need an SSH key. So that this is why we need to create these secrets inside the repository secrets section. We click on this button. I will add the following keys. So we need to have four of them. We need to have the IP address of the virtual machine, my user, and uh we need the SSH key and finally the mainly search master key. These are all the keys that we need to create. Let's start with adding the SSH key. Now I will open my terminal. I will create this a key using this command. And here I will just add test test to make it obvious. Yeah, let's hit enter. So now we are generating the public and private keys. And here the the command is asking us to add a passphrase. We should not add a passphrase. So just hit enter. Hit enter again and the uh the keys were generated. Why don't why we shouldn't add a passphrase? Because when we run the deploy job on GitHub, the the job should run without being asked for a passphrase. That's why we created a new key just so that we can use it on GitHub to connect to the virtual machine. Uh use the cat command to print the public key to screen and then copy it. I will do that and I will come back. After copying the key, go back to the virtual machine. Run nano SSH authorized keys. After you run this, paste the key in a separate line. I will do that and I will come back. My public key is now inside the virtual machine. Now I need to go back to my to the local terminal or to the local machine and this time copy the private key. Let me do this quickly. So I'll just remove public and I'll run the command. And here I have the WL copy program that allows me to take the output of this command and just uh copy it to my clipboard. Now go back to GitHub and here you need to paste the private key in the secret input field and use digital ocean SSH key as the name. I will do that and and I will come back. Awesome. So we are we were able to create our first repository secret. Later we can either update it or delete it. Now let me add Digital Ocean username. In this case it's a Mac. Let me also add the host IP. And finally, I need to add me search master key. If you don't remember where we stored the me search API key, run this command. sudo nano etc me search environment. Copy it from there. Paste it here and click on add secret. As you can see, I have created the four secrets that I will use in the deploy job. We are now ready to create the deploy job. First, open the workflows folder and create a file called deploy.yaml. Now, let's look at the content of this file. The name is manual deploy because after merging a boot request, we want to have the possibility to click on a button manually to deploy the application. The way we do that is by adding workflow dispatch. We will see this later when we merge this put request on GitHub. We will have a button that will allow us to manually run this particular job. And here under jobs, we have two. We have build and deploy. Build is reusing the work the front- end build workflow that we have created uh in the CI pipeline. Now under deploy we have the name is deploy. This needs to run after the build process. And here under environment, we have production and we point it at the URL. If you have different environments, make sure to change this. But in our case, we have just production. And now here are the steps. We will start with the same steps that we have seen in the previous workflows. First, we check the repository. We download the front end artifacts because now in the in the deploy job we are going to take the files that were built in this job and we are going to put them in digital ocean. This is why we are using this GitHub action to download the front- end build from this specific path. Up to now as I said these are the these are the steps that we have been following before and the new step which is related to the to the deploy job is create backups. What I will try to do is first I will just commit this and then I will add the other step. So let me try to do this. So here because we are u this is something new I will add add deploy job and I will commit this. Now let me publish this branch and I want to publish it to basically my own fork. I don't want to push it to upstream because this is just a test repository to show you how things work. And now on GitHub I can click on this button to create the pull request. And here okay so let's see. No I don't want to I want to merge it with my own branch or yeah and here. So yeah, let's just keep things simple and let's just hit create put request. Now the first thing that will run is basically the CI job and then if everything passes, we can uh yeah, we can merge this and try the deploy job. But while this is running, let's go back to here and let me undo the changes to show you the first step that we are going to perform in this deploy job. As you can see this specific step is called create backups. And here I am going to create backups for both SQL light and mele search. But you can adapt this to create backups for other databases if you are using them. And we are using an new uh GitHub action called SSH action. This is an amazing GitHub action that you can use to connect using SSH. Here it is. If you want to read more about it and they have a few examples here in their GitHub repository, if you scroll down a little bit, you will find there this quick start example that you can just copy and uh paste in your workflow and you can just tweak a few settings there. And as you can see here for example in order to connect they are using a username and a password. But if you look at my own version I am not doing that because we have disabled the ability to connect using a password and instead we are using an SSH key because as we said this is more secure than using a password. Okay. And here we are using version 1.2.4. So if you find that there is a new version just go to GitHub look at the releases tab and see if there is a new release. If that's the case then updates the version. Here under environment we are specifying so we are adding mail search uh and we take that from the secrets and under with we have the host which is basically the IP address of our virtual machine. We have the username key and here we pass the environments. So here we have me search. We need this because we are going to communicate with the me search service or server in order to create the backups and under script. So this is the what I like about this GitHub action. This allows me to run a script inside the virtual machine. So if I collapse this, you can see that's the only thing left in this step. Let's let's uh go through it step by step. First we are creating some variables. We have backend directory, backup directory and how many backups we want to create. So here the maximum amount of backups is going to be five. But if you want you can change this. And here we create a time stamp. We are going to use this to create uh when we are going to create the backup. So that's the file names are going to be different. The the first thing we need to do is basically to create the backup directory because if we go to the virtual machine you will find that we don't have a backups folder inside web app. This is something new and using the make directory command we will create it and here you can see that we are using this option dashp. It's amazing because uh what it does that if the back if the backups folder doesn't exist, it's going to create it. But if it if it finds that we already have a backups folder, it's going to skip this operation. And after creating the backups directory, we are going to start. So the first thing here is uh so this block is going to is related to SQLite. You can see that in my virtual machine or in my project I have this database inside the backend directory. Let me go back to the virtual machine to show you how it looks like. As you can see I am now inside the virtual machine. Let me zoom in a little bit so that we can see this better. Let's type ls-la. Now let's go inside backend and let's see what we have. You can see that we have the visitors database. This is a SQLite database that I am using uh in my application. This is what we want to back up uh when we deploy. Let's go back to VS Code and let's see. So here we have an if block. We are checking if this file exists. If it does, we are going to move we are going to copy that file and we are going to create a uh this new file. And as you can see here we are using the timestamp as a suffix. So we have visitors underscore and we put the timestamp. This is helpful because after that we are going to sort these files and remove the older ones if we have more than five backups. Then we echo basically that we we were able to back up the database. Otherwise, if this file doesn't exist, we are going to show this warning to let us know that we couldn't uh create a backup. That's it for SQLite. It's simple. It's just a file that we copy. What happens if you want to do this with me search? It's a little bit tricky because we need to do more work. As you can see here, first we check if we have the me search key. Normally we should because as you can see we have passed it to the environments uh option here and if we look inside we are using this option to check if this environment variable does exist. If it doesn't we are going to exit and return an error. Now here is how we this is how we are going to basically make a backup. we are going to uh make a post request to this specific endpoint which is called dumps. And with this we we are going to tell me search to create a backup for us. But in order to do that we need to pass the me search key because if you remember we have created this server we have created a a API key so that no one can just make requests to the server. they need to have the API key first. After making the post request, we add a header that will pass that uh me search API key. And here you can see that we are running curl with the s option which is basically silent. And here we what we are doing here is that we are tell we are telling curl to fetch the HTTP code. And if you are not familiar with HTTP codes, you can go and search for HTTPS HTTPS status code and you can learn more about them. You can go here to Mozilla. They have a good documentation that explains how this works. HTTP status codes will tell you if if your request finished successfully or not. In this case, uh you can see here are the ranges. So we have something between 100 to 200 etc. and we are going to use this information to uh basically check if what we have done succeeded or not. Now let let me go back. So after capturing the HTTP code we will have all of that we will put all of that response inside this variable and from that we are going to get the HTTP code. After getting the HTTP code, we will uh compare it to see if it is within this range. So if it is greater than or equal to 200 and less than or equal to 300, this means that we made the request successfully. I can show you this. If if this seems complex, I will copy this and paste it in the virtual machine and let's see what we will get. First, let's try this um help endpoint because it doesn't require us to pass a an API key. You can see that we have uh -x get. This is not necessary because by default when you run a curl command, a curly request, it's going to be get. It's not we add the -x option when we want to perform other actions such as post. And here, let me just run curl directly to see what we get. So for example here we get status available but what happens if I add the -w option after curl let's add dashw and let's see what we get you can see that here let me retry the command you can see that here we have 200 and this is the the status code that we are looking for and let's put this inside a variable like we did uh in the deploy job so you can see that I can wrap everything around this Let's do it like this and let's add the variable name. And I need to close this like so. Now let me hit enter and let's see what we get. You can see that uh now we need to echo the content of this variable to see what it what it holds. Let me copy it and paste it here. And as you can see we are getting the same output. Here we get the response from that endpoint telling us that the M search server is up and running and 200 is the response that we got after making this HTTP request. Now let me copy this second line. Let's take HTTP code. Let's put it here and let's again echo that variable. So let me run echo and let's copy this and paste it below. Let's see what we get. You can see that we are getting 200. Why? Because here we are taking this output. We give it to tail and the tail will take the last line which is in this case is 200. And this is what we are getting here. And the next uh action or command is will get just so let me copy this. It's going to get the status. It's uh because here we are using the set command. It's going to remove the status code. It's going to take just the other part which is the response body. Okay. Uh as you can see the these commands are simple. They look complex but if you run them like what like uh I did uh you will find that um they are easy to understand and what they are getting is uh going to be used here to basically check if the request uh was made successfully or not. So as you can see here we got 200 as the status code. If we compare if we look at this condition it's true because the HTTP code is greater than or equal to 200. So that means that we will show this in the console showing that the request was accepted. But in me search when we call for example the dumps endpoint it is not going to create the endpoints directly because it depends on the size of your database. So med search will give you an ID so that you can track the progress and uh this is what we are getting here in the response body we will try to look for the task UID. Let me try to do this so that so that we can see how this works. Instead of hitting the health endpoint, I'll try to hit the dumps endpoint to see the real output that we will get from uh that uh specific request. Let's run curl. Let's add dash x because as I said here we are going to perform a post request instead of a get request. And let me add this and let's see what we get. You can see that now we weren't able to create to make the post request because we are missing the uh yeah the header. So we need to add that. Here is the the other missing part. So let me copy it and paste it after this. Here is the full command that we can run. Now it should work because we are passing the API key. Let's run this and let's see what we'll get. Okay, here is the output and as you can see I've told you that me search will return the task UID and this is what we will try to get in this nest command. So if we go back here you can see that we have this variable that will take the response body and we'll search for the task UID using this uh expression and uh yeah if we print this we will get the task u ID. So again let's do the same thing. Let's store that information inside dump dump response and let's try to get the response body. Let's put the response here. Let me add this. Okay, that worked. Now let's take the response body and let's now run the task view ID. Let's try to print the content of this variable to see if we are getting the correct view ID. Okay, that didn't work because I forgot to add the other options. So, here I have just d - x. So, let me add the other ones dash s and dash w. Let me add them here. Okay. And now it should work. So, let's go back to response body. And now let's go back to task UID. Let's now try to echo. And as you can see, we got the ID. This is what we want to basically try to communicate with Misarch to keep asking it did the task finish or or not. Let's see how we can do this. So after getting the task ID here we are going to check if it exists. So if it doesn't we are going to throw an an error and uh basically fail the deploy pipeline. Otherwise, if we have the task ID, we are going to tell mainly search uh by making this curl command if the operation finished or not. So, let's see what we will get if we run this. So, let's do that. And okay, so here they say that the authorization header again is missing. So, we need to add that one. Let's add it like this. Let me clear the screen. Let's add the authorization header and let's see what we will get what we get. Okay. So the output is we have a lot of things but here you can see that they confirm that the ID exists and here we have the status which is succeeded. This is what we want to see but in the beginning it might be incued or processed or sometimes it might fail. So we are going to take this status information and we are going to check uh to check it inside the Y loop. Okay. So after making the request we are going to basically grab the status from the output. If it says succeeded like in this example, we are going to basically show that the dump completed successfully and we are going to break out out of the while loop. Otherwise, if it failed, we are going to fail the job. But if it is still uh processing, we are going to increase the attempt variable and sleep for 2 seconds. And here with the max uh amounts of the max retries is set to 30 but you can change this. It shouldn't take that much but this is how we are uh this is the condition that we have in the y loop. So if we reach 30 we are going to uh break. Okay. So now here we are inside this y loop. We can collapse it. And after that if for some reason we have exhausted these attempts we are going to exit. And now we can close this and we can focus on the else block. So if the status code is not within this range, we are going to exit because something bad happened. And finally we are going to run the this specific script that we have created to basically uh ensure that we keep at most five backups for SQL light and mele search. Okay. And uh yeah that's as you can see uh that's all we are doing here and this is this step is going to run whenever we want to deploy our application and this is very important especially if you have sensitive data in your database. Great. So this worked. Let's go back to GitHub. And the CI pipeline also worked without any problem. Amazing. Uh let me commit this. Let's say CI add backup job or step. Uh let's commit it and let's sync the changes. Let's go back again to the virtual machine and let's ensure that we don't have a backups folder. You can see that there is no backups folder and if I go to back end let's see so we have the visitors DB. Great. Now let's I will try to wait for the pipeline to finish and I will come back because I need to wait for this to finish uh in order to merge the p request so that I can go to the actions tab and uh click on the deploy uh job so that I can deploy the application. Okay, I'll wait for this to finish and I will come back. I am back and as you can see now we can merge the pull request because all the tests have passed successfully. I'll hit now confirm merge and okay that's done. Let's also go back here and uh let's go to master to fetch the new changes. Let's do that. Okay, great. So now we have the new deploy file. Let's go back to GitHub. Let's click here. And as you can see, we have merged the third pull request. Now let's now refresh the page. Let's go to actions. Let's click on manual deploy. This is what I explained to you. So now we have merged. As you can see, we have a CI step. It succeeded. Now we have a second step which is continuous delivery. And this is how we do this. So as I said we will do this manually. We click on run workflow and here are the two jobs. First of all we are going to build the front end. Once this finishes we are going to deploy the application. This is not going to run automatically. No GitHub will stop the deploy job and it's going to basically send us a notification to let us know if we should let this continue or not. And this is what we have configured uh earlier. You can set as many people as you want so that they can click on the deploy uh confirmation. And this is what I was talking about. You can see that we built the front end without any issue. That's great. But now we have uh you can see that they requested a review from us. We click on the button, we review. So we want to deploy to production. We confirm that and we hit approve. Let's click on that and let's see what will happen. So the deploy has started and you can see that we have four steps. Let's click on them to see what what is happening. So now we are creating the backups. You can see that everything went well. Nothing bad happened. Uh and we can read through the console. We can see these are the messages that are um that we see because we have uh we are using the echo command. And let's read through this. You can see that SQLite backup created successfully. Here is the task ID. Here is the HTTP status code that we got from media search. And at the end, we say that backup process finished successfully. Amazing. That's what we want to see. But let's confirm. Let's go to the virtual machine to see if that is correct. Uh before running the the deploy job, we didn't have a backup folder. But now, if I go back to uh web app, let's run ls-la. As you can see, we have the backups folder. That's amazing. Now, let's go inside that folder. Backups. Normally, we should have just one file because this is the first time we are running this deploy job. And as you can see, here is the time stamp. Now, what I will try to do is I will copy this file and I will paste it four four times because I want to have five files. when I will when I will deploy the next time and we are not go we are going to create the sixth file but we are going to remove one of the old ones because the script will keep at most five files let's use the copy command let's copy the file and here I'll just change uh 12 to let's say 13 let's change this again to 14 15 and 16 let's see how many backups we have 1 2 3 4 and five. Now let's go back to GitHub and I will try to rerun all jobs. Okay, this will trigger the deploy job again and it should create a new backup but it's going to delete this one because it is the oldest. Let's confirm this. We have the output and uh after uh this deploy job finishes I will run again ls and we will compare um the two lists. Let's wait for the build job to finish. We have the artifact. Great. And now let's hit deploy. Let's click on that specific job. We are checking the repository. We download the artifacts. And here we are creating the backups. And yeah, that finished. Let's see. So here you can see that we have this specific step. Starting cleanup retention policy is to keep the last five files. Well, let's confirm if that's correct. So let's run ls. And as you can see, we don't we no longer we no longer have this specific database. We have 13 up to up to 16. And now we have a new upto-date backup of the database. This is great. And if we want we can also check the dumps folder to see what we have there. Normally we should have multiple uh backups. So if we go there, okay, we don't have the permission, but we can run sudo and pass the password. And as you can see here, we also have a few backups. Awesome. So this is working really, really well. Let's keep going. This is not the end of the deploy job. We still have a few jobs to add. Let's click on here and let's create a new branch. Let's call it R sync. Okay, because this is what we want to do. Let me collapse these jobs. So I will collapse the checkout step, the download front end artifact and also uh the creation of the backups. And now we will add this new job that will sync the files from the from GitHub to the droplet. For that we are using SSH deploy and rs sync is a command that will sync the files. So basically it will take whatever we add in GitHub and make sure that we have that on the virtual machine and uh by default it will try to create a mirror. Basically, whatever we have on GitHub should be on the virtual machine. If we have new files on the virtual machine, the rsync command will delete them. But we don't want that because as you can see, we created a backups folder and this folder doesn't exist on GitHub. So, if we run this, we will risk deleting the backups folder and any other file that doesn't exist on GitHub. For example, thev file, that's another problem. That's why we created that rync exclude file to tell rync yes try to create the mirror but if you find something new in the virtual machine please keep it. You can also search for this new GitHub action that I'm using here. We pass the private key. We pass the host and user. So the host is the IP address. The username that I am using um in my virtual machine. Here source will basically copy everything we have here and the target is web app and here are the arguments that we will pass to the rs sync command. Uh so here by default we will delete and but we exclude these files and folders that are inside the rsync exclude command. Okay, you can see that this job is simple. So let's try to commit it and let's test it. Uh yeah, let's just call it ci add r sync job. Let's commit this and let's publish the branch. We want to publish it in to origin. Uh let's go back to GitHub. Let's click on pull requests. Compare and pull requests. Let's target my master branch. And yeah, let's just uh create the pull request. I will wait for the pipeline to finish and I will come back. And like uh the creation of the backups, I will deploy manually to confirm that the step is working. This is the best way to create complex jobs is to test each step independently to confirm that it works before moving to the next step. If you try to write the full pipeline, it's going to be yeah, it's going to be hard to test because uh multiple jobs will fail at the same time and it's going to be a nightmare. I highly suggest to work on complex pipelines like this. Create a step, make sure that it works and then move to the next one. Same as before, I'll wait for the CI pipeline to finish before hitting the deploy button. I am back. I can merge the pull request. Let's do that. And let's click on uh the yeah the GitHub action to uh trigger the R sync job to verify that it works uh correctly. Okay. Now again click on run workflow. Make sure to choose master. I forgot to mention this before but that's very important because master is protected and whenever we merge a new thing it is ready to be deployed. What we want to confirm in this step is that the backups folder shouldn't be deleted. This is uh very important. We can see that here we have a few backups and if I go back uh the backups folder shouldn't be deleted. Let's verify that this is the case. Uh okay let's confirm the deployment. Click on production and approve and deploy. Let's click on the deploy job. We now should have a new job which is uh going to run the rs sync command. And as you yeah as you can see here it is. Okay. So we can see that the rsync command is going through everything uh in the GitHub repository and it says done. Okay. Uh we we deployed successfully. But the question is do we still have the backups folder? Okay. We still have it. Let's see what we have inside it. Amazing. So, we have everything there. But if we have removed this rync exclude file, things will have changed. Uh but basically, you get the idea. If you want to keep uh files that are inside your virtual machine but don't exist on GitHub, you should try to add a file like this and specify it in the arguments. Now, the next step is to populate the environment variables. Okay. So first of all let me try to add the backups folder again to r sync exclude it was so it was backups uh save the file and exit. Now if we go to back end uh sorry let's go to back end let's run ls-a you can see that we have thev file and I have created this uh manually when I when we were testing the the back end but we can also create this uh directly from the deploy job because we have all the secrets we want and if we uh look at env.example example. You can see that this is the only thing we need. We just need to change the mini search master key because the other ones are not yeah they are not secrets. This is just the endpoint and uh this is the index name and the environments. It can be either development or production but the secrets uh is the master key. This is what we will try to add. Let's try to do that first. Let's go back here. Let's click on the repository. Uh okay. So we have merged. That means we need to go back again to master and let's try to pull the latest changes. Okay. As you can see we have added the new step here. Amazing. Now I will create another branch and here let's call it feature and let's say create environment file. This step is also small. Uh let's add this below uh the rs sync job. You can see that the name is create a the backend env file. Here we are again using the SSH action command because it allows us to run scripts. Here we are using cat and we are redirecting all of this content to this file we are creating. So this is the the endpoint we are this it's local that's why we are using local host. The index name this you can change it based on the name of your index. And here we are using that environment variable that we get from secrets to populate this environment variable inside this file. And finally environment is set to production. And we end the file. That's all we need to do to create a to create thev file. Uh let's uh commit this civil file. Commit this. Publish the branch to origin. Let's go to GitHub and let's create the pull request. Okay, we have a problem. I need to change this to ghost like this. Okay, great. Now, let's create the pull request. I'll wait for the pipeline to finish and then I will come back. Let's merge the pull request because the CI pipeline succeeded and let's test the deploy job. Click on actions. Let's find manual deploy. Let's run the workflow on the on the master branch and let's see if the environment file will be created. This is a simple job so it shouldn't fail but yeah it's always uh a great idea to test. Let's click on review deployments, choose production and deploy. Click on the deploy job and let's wait for the final step to run. As you can see it is there. So that means that we uh yeah we are using the latest version of that deploy file. The RC command has finished and now we and we we were also able to create the backendv file. That's amazing. Yeah, we can verify that we have the file there. Um let's see. So let's run ls-la. And as you can see, today is it's June 28th, um 11:24 a.m. So that means that it's uh correct. Great. Let's go back to VS Code again. Let's switch to the master branch. Let's pull the latest changes. Now, we need to install the Python dependencies. Let's say that we added a new library that the back end depends on. If we don't install it, the back end will fail. So we need to add a step to install the dependencies. But the problem that we need to account for is that let's say that we have 20 dependencies and uh we install all of those inside the virtual environment folder. Uh let me show you. Let's go to um v to the virtual machine. You can see that we have the virtual environment folder. Let's say that something bad happened and we were able to install 10 libraries but the rest failed. If we change this virtual environment folder and um yeah this not all not all dependencies were installed this will cause a problem. So what we need to do is this step should be atomic meaning if it fails we need to roll back and reload uh the virtual environment. This means that we are going to take a copy of this virtual environment folder. We will rename it something else. Uh for example VNV uh backup. Then we will use pip to install the dependencies. That will create a new virtual environment folder for us. If uh everything went well, great. We will delete the backup folder that we created and we will keep the new one. But if something failed, we will roll back and reuse the the backup folder so that um we don't accidentally break the back end. First of all, we need to update the rsync exclude file. we need uh to uh update the backend section. Uh here are the things that we need to add. Remember that I showed you that we need to create the logs folder, the J unicorn start etc. And here we also have the socket. All these things do not exist on GitHub. And if we deploy that was the problem that I had is that these files and folders were deleted. But now I have added them here and uh yeah they shouldn't be deleted anymore. Um so I have gone through the steps that we um followed in the previous sub uh chapters and you can see that I have the socket file I have the scripts and I also have the logs folder and I have added them to rsync so that they won't be deleted anymore in the future and to uh also verify that the back end is working fine you can see that we can use supervisor ctl and here this is the application and it is saying running which is uh Great. This is what we want to see. And if I also open the browser and go to this website, you can see that it is working. If I click on blogs, yeah, they are coming in. And if we click on uh let's go to the conclusion and let's start clapping normally it should work. Yes, you can see that if I refresh the page, the the count uh was updated because the data is stored. Uh and yeah, it means that the back end is running. Maybe search is running. Everything is working perfectly. Okay. Now let's go back. We let's commit this. Uh let's say sure update r sync exclude. Uh let's commit this. Now we need uh we have created the branch. Great. Now we need to add the new job inside this file. Now let me add let me paste the code here. It's long but I'll try to explain it. uh to you. The new job name is called install Python dependencies. And here I have tried to emphasize that we are going to uh atomically swap the virtual environment. We use the SSH action GitHub action to run a script. So let's look at what we have here. First, we define this virtual environment uh so that we can go inside back end. And here is the first step. We want to create a backup of the virtual environment folder. But first, we need to check if that directory exists. If it does, we we use the move command to create a backup. And the new folder name will be virtual environment backup. After that, we will create a brand new virtual environment. We activate it and we use pip install to install the dependencies. And here so yeah if uh this command failed we are going to show the else case which is going to remove the virtual environment. This is the roll back strategy that I was talking about. If it if it if things doesn't work as expected we will fall through the else block. We will show the error to understand what happened and then we roll back. But first we need to deactivate the virtual environment. We remove it and then just to double check we check if the virtual environment backup exists. If it does, we move it back to virtual environment and we exist because the installation failed. But if everything went well, we will do the cleanup. So we have created a new virtual environment. It has all the dependencies. So now we no longer need the backup. We can just delete it. And at the end we echo that the virtual environment setup has completed. And now I can we can commit this um let's say pet and here let's say add uh virtual environment or let's change this to CI because this is what we are working with. We can commit and remember that the rsync exclude file is now looking like this. We can publish the branch. We go to origin and let's now click on pull requests to create another one that we can deploy to pass this new step in the job. Okay. Now let's change this to uh origin. Let's remove all of this. So let's see. So this is the branch we want to merge it with master. Okay. And here let's change the title to CI add virtual environment atomic setup. Click on create pull request. Now we just need to wait again because uh yeah we need to ensure that the CI passes before testing the new deploy job. I am back. As you can see we can merge the pull requests. Let's do that quickly. Now let's click on actions. Let's scroll down to manual deploy. Click on run workflow and hit run workflow. And at the same time I'll try to uh looking at the virtual machine to see if um when the backup folder will be uh created. But first let's wait for the build the job to finish and then we can run the deployment job in order to test the atomic virtual environment uh creation. Let's hit review deployment. Click on production and approve. Okay, let's click on the deploy job and here. Okay, let's do clear and ls-la. We are syncing the files. Let's hope that the logs folder and the other ones do not get deleted now. Okay. And now we are installing the dependencies. And as you can see, we have the virtual environment backup. And now we created a new virtual environment where we are going to install the dependencies. And we can confirm this by the date. You can see that this virtual environment was created uh a week ago. But now this one was created today. And we can also see that the logs file that the logs folder these files were not deleted because we have updated the content of the rync uh exclude file. Amazing. This is working really well. And you can see that the deploy job um finished successfully. Which means that now if I run ls-la, this folder will be removed. Let's confirm that. And yep, that's it. That's what we what happened. And you can see that the date on this folder is um June 28th. This is working really well. Now, let's go back to VS Code here. I need to go back to the master branch cuz we need to pull the new changes. Let's hit okay. Um let's wait for a while. Okay, we have a new job here. Great. Now the next step is to uh reload the back end as well as the engineext configuration because if you have made changes to the front end or back end, we need to restart the application so that the changes take effect and for that we will let's go back to the website. You can see that here in the navbar I have so this is my name. I will try to change that and we will hit deploy and then come back here to the website to see if um yeah the new modifications uh are going to be live. Let's try to search for the navigation bar component and here. Okay. So, let me search for my name. Here it is. And I will add uh let's say 2026. Okay. So, I have changed the uh changed that file. Let me create a new branch. Commit that. Close the file. We will close the rsync excl exclude because we no longer need it. And now I will try to add this job. Uh let me indent this correctly. So it says restart the backend service. Here again we are using the SSH action GitHub action and inside this job. Here is what we are doing inside script. We check we try we run the restart all command using supervisor ctl. If this succeeds it means the back end restarted successfully. Otherwise we fail the job. Uh after running this we will wait for let's say 5 seconds so that uh we can run again the status command to check that the output will say running. This is another layer of confirmation that will tell us if the back end is is working. Okay. So we will try to push this and then I will add another job after this that will reload X. Let's commit ci restart back end and below this I will add uh let me go back here let's go down below and let's paste the new job uh I will collapse this because uh we have uh seen it and uh now we just want to focus on the reload engine x job here inside the script first we need to check that the configuration is correct if the configuration is not correct we are not going to reload EngineX because if we do then we are going to break the yeah we are going to break the website and we don't want to do that. If the syntax is okay we move to the next if block we run again here we run the reload engineext command. If this succeeds great otherwise we are not going to run it because the yeah the configuration is not correct. And at the end we show deployment complete. This is the last job in this deploy if workflow and if this succeeds we have um created uh a good CD workflow that when I hit deploy after changing my applications code it's going to be live in a few in a minute or two. Uh so here let's add reload enginex. Let's commit this. I'll publish the branch and create a pull request. Then I will wait for a few minutes for the pipeline to finish and I will come back. I am back. Let's merge the merge requests or the pull request. Let's now go to actions. Click on manual deploy. Let's uh where is it? Yeah. And here let's click on run workflow on the master branch. Let me split the page on two so that we can see the changes uh quickly. Okay. Now let's click on review deployments and let's approve and deploy. Let's click on deploy. We'll follow the steps. We are checking the repository. We download the front end artifacts. Here are the backups. Okay, we are are syncing the files. We have installed the dependencies without an issue. And now we are trying to restart the backend service. You can see that the message is saying restarting the fast fast API back end. Let's hope that this works. We have seen that we were able to stop and start the the backend service but we have a problem. So here it says sudo a terminal is required to read the password meaning that the v sudo uh change that we did didn't work. Let's try to investigate why. So here what was what was the last message we have seen pass API backend that restart is started successfully. Okay now let's go back here and let's search for that message. Okay so supervisor ctl start all worked well but I think what happens is that maybe the status uh is not allowed because here we need to run sudo. So let's verify that. Let's open the terminal again. Let's run sudo v sudo and let's see what we have inside this file. Let's scroll down. Okay, you can see that here we have uh yeah that's the problem. So here we are allowing just restart all. We don't have status. That's the problem. So what we can do is copy the command. I'll try to copy this. Let's go back to the terminal. And here let's try to add it after this one. Let's type user bin and I will paste this command. Supervisor ctl status all. Save the file. Exit. Now go back to uh let's go back to GitHub and let's restart the failed jobs. So here uh let me try to make the screen bigger to so that I can see what's happening here. I will click on rerun failed jobs. Let's uh rerun that. Let's now click on review deployments and let's hope that this time the we won't have an error when trying to reload the back end. Okay, now we have reached the other the step that failed before. But now as you can see it uh it didn't fail. That's great. And um okay, so now we have the same problem while trying to reload EngineX. And I think I know the um uh how to solve it. I think yeah we have the same issue if we look at here let's see so we have reload engine x but we forgot to add uh engineext-t because we are checking the syntax if we go back to vs code let's collapse this because it succeeded it didn't fail now we need to basically uh try to work on this one you can see that systemct ctl reload engine x is already here it systemctl reload engineext. But when we try to uh check the configuration, we run engineext-t. We need to also add this one. To fix this, we need to find where the binary of is located. But I think normally it is located at user sbin /enx. But we can verify that. I have saved the file and now I can run which engine x. And as you can see this is the path that I have added in b sudo and um okay let me just verify it one more time user/ spbin/engineext and then I added the comma. Okay great. So now with this we won't um yeah we won't have an error when trying to run that specific step. Okay let's go back again to GitHub and let's try to rerun this failed step. Uh let's use rerun all failed jobs and uh again let's uh rerun this. I will refresh the page to get the confirmation is production and approve. Let's click on the deploy job. Hopefully this time it should work and then I will go back to this tab refresh the page and hopefully we should see that 2026 uh will appear here. Let's collapse this rync step the create uh backups etc. Now we are installing the Python the Python dependencies. Uh we arrived at um the last steps. This one will not fail because we have confirmed that. Uh let's just hope that reloading engine X will also work and yes it did. Amazing. Uh let's uh look at this. So as you can see everything is green now. And as you can see here, GitHub will also give you the link so that you can click on it to see if everything uh is looking great. Okay, this is the moment of truth. Let's refresh the page. And as you can see, we have 2026. And this will work when um it doesn't matter if you change your front end or back end. Both will both uh will work. If you change your back end, we restart supervisor. We restart yes supervisor using the supervisor ctl command. And if your front end changes, we also reload the engine X. Awesome. So, as you can see, we have built the full pipeline. And this is how uh how I want you to build it. Don't try to write it fully. No, go step by step. Try to add one step at a time. Verify it because as you can see right now, we try to add these two steps. They fail. It's not a problem. We can go ahead and fix them before moving to the next steps. And once you do that you you learn how to handle errors and it's uh also amazing when you try when you fix the problem and see uh the green check mark uh it makes you happy and developing it like this is also fun. Uh so yeah we have finished the deploy job and this is how it works. So now in the future you have seen now that I was creating put requests and then I merged them. That's the first step. create your uh create a new feature branch. Develop your uh the yeah your feature or bug fix or whatever. Mer make sure that the CI pipeline passes. Then merge that request with your master branch. Then after that move to the second uh step which is to deploy. So go to actions and then hit the button on the master branch. Don't forget that. And uh after that your steps are going to go one by one and then your application will uh update accordingly with the new code that you pushed to master. And just like that we have finished chapter 5. It was a long one and uh this this was the I I would say the most interesting chapter because we have merged everything we have learned before and we try to automate as many things as possible. We have one more chapter left. In that one we are going I will it's a it's not a long one. uh I will just show you tools that you can install uh on your server so that you can monitor let's say uh HTTP requests that are hitting your server etc. So these are just tools that are nice to have and that are easy to install and configure. We have arrived at module 6. We are going to focus on observability. You will learn how to parse native engineext traffic using go access. I will show you how to deploy a dashboard where you can see this engineext data using HTTP basic authentication. We are going to learn about rotating logs and we are going to use a chrome job for that. We are going to visualize the CPU and memory usage as well as process trees using BTOP. And finally, I will show you how to find people that are or bots that are trying to get data from your virtual machine and we are going to block those IPs. Now let's go to the terminal and let's SSH to the virtual machine. Here is my terminal and as you can see I am inside the machine. I am going to start by installing go access. Here we are using ibuntu. We can install it very easily using this command sudo apt install go access. I already have it on my system. So that's why it didn't take too much time to install it. And how do we use this? Okay, let me show you. First, we need to try to look at the this path that stores the logs. You can see that here we have a lot of logs because we have just one website on this server. It is called my website_com. We are seeing this. But if you are if you have a virtual machine that is that is hosting multiple websites then pick one or multiples. So here we are going to pick my website. We want to look at the access logs and you can see that some of them are regular files but some of them were compressed and this is what I was talking about in the beginning. So, Linux is trying to rotate the the logs, meaning it it tries to compress them to save disk space. But first, we are going to look at just this one because it's a regular file that we can read. And later, I will show you to in how to include the zipped files also. Now, we know the logs are stored here and we know that we need to pick this specific uh website. Here is the command that we need to use. I will paste it and then I will explain what it does. Here we are using pseudo because we need to read files from this specific directory. And here I need to copy this and uh replace this placeholder with this specific file name. And here the log format is set to combined. This is the for the default format that enginex uses. So if you have if you are using a different format, make sure to change this option. That's all we need to do. Now if I hit enter, let's see what happens. You can see that we get this amazing in real time dashboard. This web server received 122 requests today. And here we have 54 unique visitors. Go access is showing you the data that is stored in the engineext logs. And here it is also shows you where it where it found the data. And this is the normal file that I was talking about. And here it shows you so the uh the unique visitors we I we have just one person that was that was hitting the the web server. We can keep scrolling or you can hit tab to move to the second uh tab or to the second um section. Here we have requested files. Some were requesting the site map. Some were just visiting the root directory. Here we have the static requests. So you can see that some were checking the robot cxt file. We don't have 404s. Okay. Here we get the IP addresses of people or bots that are trying to reach the web server. Here it shows you the operating systems. You can see that we have a lot of useful data that we can read and try to understand what is happening. And here it also shows you where the traffic is coming from. You can see that some were able to find the website from Google. Some were uh some traffic was coming from our website itself. And here we got the HTTP status codes per request. So most requests here succeed but some of them have client errors. Great. And as you can see all of this data is coming from one single file. And later I will show you how to increase this range by incorporating the files that were compressed. But before we do that, I will show you if you don't if you prefer to see a dashboard instead of sshing to the machine having to run this command to get this terminal dashboard. We can tell go access to generate a an HTML page that we can access from our browser. And this is what we are going to do now. But since this data contains sensitive information like IP addresses and other stuff we don't want everyone to see that page. So we are going to use HTTP basic authentication to block access to that specific page. So you need in order to see the data you need to provide a username and a password. And in order to do that here I will hit Ctrl C to exit. We need to install a tool called Apache2 utils because it has that functionality that allows us to create to generate an encrypted password file. Great. We have installed Apache to utils. Now let me clear the screen and let's use it. Here is how we use that utility. So here we tell it that we want to generate a as I said an encrypted password file and we will put that here in this location and here the user is going to be uh admin. I hit enter and now we need to provide a password for that specific admin user. Now I need to retype the new password and it says add in password for user admin. Now we can use this username and password to lock that specific page. But before we do that, we need to generate the go access html page. And in order to do that, let me go back to this specific command where we generate where we get the terminal dashboard. And we need to adjust a few things. The first part is going to stay the same. So we are pointing at a specific log file but we need to add the following option before log format. So here I will add this and as you can see here we add dash O which basically will try to generate this specific HTML page from the data. Once I hit enter, you can see that it is going to put it inside front end this. And this allows me to be able to use this file and to check it from my browser. And if I want, I can check the engineext configuration. It says okay. And now I can try to reload it so that engineext can serve that specific file. So let's go to Firefox. And here let's go to uh the website. And now because that analytics html file is in the this folder, I can access it. But you can see it didn't work here because I need to create I need to update the engineext configuration in order to serve this file. First let's u make sure that when we go to web app front end and this we need to ensure that we have the file there. As you as you can see we we do. So we have analytics.html HTML. Let me make sure that I have typed it correctly. Yes, I did. Now that's great. We need to open the enginex configuration and let me try to do that. We use the nano. So here I am using nano but you can use other text editors. And here we are pointing at my my website com inside the sites available folder. And here you can see that we have we need to come here. So here we are serving uh index html, we are serving assets and we are serving other stuff. So what are we going to do is we are going to add another location block but we are going to do that before the spa fallback routes. So we can do it maybe before assets or before index html. It it doesn't matter. Okay. Uh I will just copy the following code and I will paste it here. And now I need to try to indent this. And here you might see that I have changed something here. Root instead of specifying this path I have this v this variable that I'm using here. I did this because uh before I didn't have this coming soon page. So when I was uh because here I had I was serving other stuff. So I just uh changed this variable to point to this coming soon but basically it's just a variable that is holding a path and here so here you can see that root is pointing at web app front end this and this is why this was not working now I can save the file exit let's check engineext-t it says that the configuration is okay and now we can reload the page sorry I made a mistake Let's try to run this again and let's hit enter. Okay, now let's go back to the browser and let's refresh the page. And as you can see, I can't access that page because I need to provide my password. And this is what we are using in the configuration. I forgot to explain it. So let's go back to this file and let's see. You can see that root is pointing at the folder because there we have the analytics.html file. And here we add this o basic to tell engineext that this endpoint or this file is restricted. We cannot access it. And here is what we are using. We are using also and here is the encrypted file that we have created. So if the user or if the person that is trying to get this resource if they provide the correct user and password then serve this file for them otherwise don't serve it and show them this challenge which is what we are seeing here. Now I type admin because that's the user that I have created and here I need to provide the password. If I make a mistake let's say I remove the last character it's not going to let me in. So I need to provide the correct credentials in order to see that dashboard. And as you can see now I am in. So we need to wait uh for a few seconds. Um I'm not sure what is happening. Do we have an error here? Okay. Okay. Okay. So I see the problem. Remember that we are using content security policy which is restricting this project from loading some some data. That's why it is not working. If we go, so we need to fix this and this is really cool because as I told you when you apply the CSP policy, you have to keep attention to it and if you change your front end, you need to always test it to see if there is some new dependency that you added or something that is not loaded correctly. And this is what what happened here. But we can solve this easily by going back to the engineext configuration. And uh let's go up to see what we have. You can see that here we have the content security policy. And this is what we have done. And what we could do here is we can uh [clears throat] h have the header here just for analytics. HTML so that we can override this. Uh okay. So we can come here and I will I will try to add that. I will paste that here. Let me save the file. Exit. Let me check that the configuration is okay. And now I can reload. I will I will just make sure that uh the website is going to load correctly and then I'll try to explain to you what is happening there. Okay. So, we still have a problem here. It says that the page settings blocked an inline script uh because it violates something. Okay. Let's go back and let's see what we have here in the configuration. I think what I will do here since this is a page that only admins can access. I will add unsafe inline just so that we can see how the dashboard looks like. So here we have unsafe eval and after that I will add unsafe inline. But please never do this on your main production site because this is a page that is protected. it's not it's not going to be an issue. That's why I'm adding this and to just make sure that it is working. So here now I refresh the page and as you can see we get the dashboard. It's the same information that we will see in the terminal. But if you prefer to say that you want to see this from your phone or you don't have access to the virtual machine, you can use the dashboard to look at the data. And here you have you can for example open the settings. You can change the theme if you like. If you want a bright theme, you can do that here. And here you can see all the panels that we talked about. Here it shows you the unique visitors per day. It shows you the files that were requested, etc. And if you want to learn more about Go Access, you can go to their GitHub repository. They have a ton of resources and it's an amazing project. You can also if you want you can uh as I said go to GitHub and here they have their own example the so on their website they show how this might look like. So if you want to to come here to read the documentation uh that that would be great because they have a lot of features and if you click on this image on GitHub you see that they also have their own dashboard and you can see that they have a ton of information and you can also add maps. It's amazing. It's an amazing project. Great. Now let's go back to this to my website. You can see that we were able to add this and uh as I mentioned here we have we just did a shortcut just to be able to solve the problem with the CSP but as I mentioned and never ever use unsafe inline because that goes against security. Now let me show you how to do this correctly. First we need to add nons. Uh so yeah we need to add this. Let me show you. We need to add nons dash requests ID to the CSP only in the script source. Okay, because this is related to loading inline scripts. And after that, this was the missing piece. I forgot to add it. This is what we need to add to uh this location block. Here we are passing sub filter once off because we want engineext to go through the content of this file and we don't want it to stop uh when it finds the first occurrence of the things that we want it to search. And here here are the filters we are telling it to search for script tags that don't have attributes. We want to have to search for script tags that have attributes. So for example, this one is not closed but the first one is closed. And for these two we want engineext to include the nons value. And this is a dynamic ID or yeah request ID that is generated whenever we request this file. And because of security we want yeah we want to make sure to uh include this stuff. We are telling EngineX to replace these strings with these values and this should resolve that problem. So now I can save the file. Exit. Let's first make sure that the configuration is correct. Good. And now we can reload the page. Awesome. Now let's go back to the page and I will do I will do a hard refresh. We can do that by using control shiftr. And as you can see it is working now. and here. So yeah, we have just warnings. We can remove them. So we can see that we don't have any errors and we can confirm. This is great to play to try to play with this because once you add a CSP, you will have problems like these. And in order to uh understand them, you need to read the error that you are getting to understand what needs to be changed. And here for example, I can remove this and see if it is not needed. And now I can reload the reload the configuration, come back, refresh the page. As you can see, we were able to load the dashboard but not fully. And this is how you do it. Now we see that okay, we have a problem. So now we can zoom in and read a little bit. It says that here uh okay so the pages settings blocks an inline script and here from being executed we can also click on this specific file and as you can see here it shows us that we have this specific script and what do you see? We have we have the type attribute. So it's not a a closed script like the one we have here. Let's go back to the configuration. You can see that here the scripts that we captured is closed. Here it is closed but this one was not we weren't able to load it because now it has an attribute and that was the second thing that I've added in order to capture that. So let me go back and paste the file and here let's indent it. And uh we have seen how to work with CSP. But I just wanted to remind you how we do it. Uh you can use LLM to help you with this. Just copy the console error, give it your current configuration and tell it to give you a secure CSP. Sometimes it might tell you to uh do the what we did before to get rid of the errors quickly, but that's not secure. So try to tell it to give you a secure CSP and that's also going to be very helpful. Okay. Now we can save this file after adding this sub filter. Let's reload it. I know that the configuration is correct. And let's refresh. And as you can see everything is working perfectly. Amazing. Now let's go back to the terminal because now I want to talk about log rotation. We have seen that when we did ls and looked at the var directory, we found that we have a lot of zipped files here. And if we want to use them, it's not possible. So that's why here in the dashboard, we see just what is happening today. But let's say that I want to see what happened during the this month or uh during the past year. I need to change the command a little bit. So let's see how we can do this. For that, we have a utility called zeb uh grap. You can search for it. Normally it is installed on Linux. So uh you can use it and this is how it works and it allows you to search inside the zipped file. So you don't need to unzip them and then search within them. This utility allows you to uh search for patterns within the file directly and it's super useful. We can try we can try to do that uh in order to show you how that works. I will copy the full command and then I will show you what it what it does. Let me go back to the terminal. I will paste the command here. I need to change that placeholder using this website. Let's do that quickly. Okay. Let's see what we have here. Here you can see that we are using a wild card. This means we are going to capture every file that starts with my website_-access.log. And this will capture the normal file and the other ones that are compressed. Now let's look at this Z grab-h dot. If we go back to Google, here we see the this uh AI generated content. It says that here we pass the options and the term that we want to search for. And here we put dot because we want to capture the whole file because that's what uh go access is going to use in the dashboard. And here we pass the dash h option in order to basically tell it to hide the file names. And here if you search online you'll see that's the this confirms it. So dash h means d- no file name. And here yeah it's it's not going to show the uh the file names in the output. But that's how it works. So you you give it the the files. Here we use the wild card to capture all the the files and here we pass that to go access and here the dash is telling go access to read the unccom log data from the standard input because here we are using pipe it's going to take the output from this and give it uh to go access uh from the standard input again so we here we have - o web app and we put that inside the and we use the combined log format that and here we get a problem. So it tells us permission denied. That's uh understandable. So we need to add sudo and uh here is what it is saying we get permission denied because when we do sudo like this it is going to be applied to just z grab and uh after piping that we are going to use that permission. So we need to change that command to uh to look something like this. We need to run it inside this shell execution script. We are using p sudo and we use bash and we pass it the full command that we want to that we want to run. And this will uh use the pseudo privileges. And this normally should work. And as you can see we don't get the permission denied error like we did before. And now since this is inside the this folder I don't need to re to reload the engineext configuration. I can just update yeah refresh the page. And as you can see the the duration changed. So now it is saying 28th of June to 12th of July which is amazing. Uh normally it will be 2 weeks max. But we will solve this issue because Linux is going to first compress the files but after two weeks it is going to download uh to remove or to delete old files. And uh yeah now you can see we get more data. Now you can see the number of visitors per day and the number of hits. A visitor can hit the server multiple times. That's why the number of hits is usually higher than the number of visitors. Amazing. But what do we want is that we want to automate this generation process. We don't want to log to the virtual machine and every single day generates this uh updated uh static HTML file. We want to automate this and we know how to do how to do that. So we are going to use chron tab and we are going to add uh this uh file or this command to the end of that file. Okay, let me copy the full command and I'll paste it here. Here we are going to run it uh hourly. So that's why we have zero here. It's going to make sure to run zap every hour for us. And now the only thing I need to change is this. Let me save the file and I'll just copy that from here. Let's go back to Chrome tab and let me change this and paste it here. Great. Now we can save the file. Exit. And now this is going to automatically generate the dashboard. And let's say tomorrow if I want to see um the stats. I can just go back and uh refresh the page. But we still have a problem where Linux is deleting old files. But we are going to come back to that issue and resolve it. For now, we will uh stop a little bit and we will go to GitHub. I want to show you an amazing project that you will find very useful. It's called Btop. So, if you go to Btop GitHub, it's a way to monitor resources and it's super super amazing. The application that they have developed looks very nice. What I want you to do is open the releases page and here the last version is 1.4.7 4.7 and here they have the pre-ompile binaries uh that we can download but since we are here in the terminal we need to use the wget get command to download that so I'll just download this one and I will use um first let's use ls okay we have the file there I will use tar to unzip that okay now we have btop so we need to go inside there sorry let's go inside btop and this is the source code we can just use pseudo make install to compile this project. Okay, you can see that this is the installer and it installed the binaries uh in their respective folder so that we can just type btop and uh the program uh will run without a problem. So now if we type btop it says that okay we need to resize the window and uh voila as you can see we have everything here but sometimes Linux will not allow btop to get uh sensor data. So for that we can you can run this command. So if you get that problem you run pseudo set cup and that will allow btop to read the sensor data without an issue. After doing this, we can go back one directory and remove the the the files that we have inst uh downloaded because now btop is running without an issue. And as you can see, btop is a replacement for htop or top or any uh one of these commands because here you can see yes, we get the same information but it's hard to read visually. Uh it's hard on the eye. But Btop is uh is an is an amazing uh terminal user interface. As you can see, it looks really clean. Let me zoom in a little bit just to see what is happening here. You can see on top we have the CPU. Since this is a lowend virtual machine, we have just one core. Uh here is that CPU. We have uh 512 MGB of RAM. You can see is here. We see that currently we are using 48% of memory and uh 50% 50 52% is available. We also have swap. This is what we have created in the beginning. So we are allocating 2 GB from disk as a swap memory to help us because we don't have a lot of uh RAM and uh here so we have disk we have 8 GB and here we have network. So it show it shows us download and upload speeds and here we have the processes and here you see you can you can filter you can uh switch between tree view so if you hit E it's going to show you individual processes uh but if you want to see the tree you can hit E to yeah yeah group the processes uh like so if you want to sort you can use the arrow keys currently we are sorting by memory but you can sort with other stuff. I usually keep it to memory because here as you can see I don't have a lot of it. So if I see that my server is struggling I would like to see what is the process that is using a ton of resources so that I can go here maybe hit K to kill that specific process if it is using too much memory and uh is making my application work u poorly. Okay so that's how we do it. You can also filter. So for example here I you know that we are using J unicorn. So we can look at that. And as you can see here we have all of this and we can hit E. And here you can see that our application is using these three J unicorn workers because that's what we have specified. But here you can see that we have some orphaned uh processes that are left here maybe when we were reloading the the back end but we forgot to delete them and these are consuming memory. So what we can do here we can just hit K and kill them one uh one by one because you can see that some of them are using 5 MB of memory and they are doing nothing and we know that here when we set up the J unicorn file we have specified that we want to have three Unicorn workers that's why we have three of them there and you can see that they are grouped by the master J unicorn process that's how you know if those are orphaned or not. And we can also verify. So if I go back to my website, it should I yeah it should work without any problem. So if I go back here, you can see that here I have the coming soon page. But that's just something that I have added recently. So if I open another terminal window and update. So let me show you what I'm talking about. Here I have this maintenance file just to allow me to change between this page that you are seeing right now and uh this page that contains my uh website. So let me reload EngineX. Let's go back refresh the page and you can see I get the website. And remember last time we did the change where we added 2026 here. And if I go back to blogs you can see that the back end is still running. And uh we can also verify something because now if I try to kill one of these workers what will happen? Let's see. It is going to be killed. But as soon as that happens J the manager so this process manager is going to see that hey we have two workers now but uh the the configuration says that I need to have three workers. So it is going to spin a new one to uh fulfill that. If you don't remember where we have that, you can always go back to web app. Let's go there. We open backend and let's go to back end. Let's use cd instead of ls. And let's see here we have you can see that we have j unicorn start. Let's look at that file. And as you as you can see here we have specified that the number of workers should be always three. And here what when we are on J unicorn it is going to uh ensure that we have three UVORN workers. That's why when I tried to kill uh one of these workers I I don't see two. So one gets deleted and a new one comes in. And how do we know that? Just look at the process ID. So here is the current process ID. Let me take a screenshot quickly just to verify that this is the case. Um here is the screenshot. I will try to kill it again. And here is the number. So you see before it was uh 827 but now it changed to 990. And that's amazing. This is the what we have in the configuration. And now if I go back and refresh the page, you can see this is still working because and uh these processes are what's handling the the requests that are coming from the browser. And you can see this is why Btop is amazing because it allows you to interact with it uh using your mouse and keyboard simply instead of top and other programs that are hard to use. Okay, I have now showed you how to use Btop. Now, let's talk about identifying bots and uh people that are trying to do malicious stuff with your server. Okay? And let me also show you how to understand what I'm talking about. So, if I go back to analytics.html, you can see it is still working even though we changed to my website. You can see that if we focus on requested files. Let me see if I can zoom in. Yes, we can. But I think what we can do, we can also hide some panels. So we can see that here we have unique visitors. Can we hide that? Yes, we can. Okay. And now we have requested URLs. Here you can see that this is what we currently have. Some people are trying to get the site map. Okay, that's fine. But look at this. Someone is trying to do some fast traversal inside the virtual machine. This is malicious. This is not something that a normal user will do. We can keep looking. This one is this person is trying to login somehow. Uh let's see. This person is trying to get Yeah, you can see there are a lot of malicious things. This guy is trying to get password and u some configuration environment files etc. These are not normal users. Basically, there is a person or a group of people that are building programs that are going to the through the internet and trying to find web servers. Once they they find your web server, they try to find if there are vulnerabilities. Basically, if you are not good at security, you might leak some files. And if they find that you are leaking some passwords or some common files that we see here in the requested files, they will try to uh get as many things from you as possible. You can see that here they are also trying to get some AWS credentials or stuff like that. And these bots they might hit your server a lot. So if you if you see here here so for example this is a new web server. I am not getting a lot of hits but this will increase from time to time but we want to solve this. We want to block these people because they are just wasting bandwidth. Now let's see how to do this. We are not going to do it manually. Meaning I am not going to come here. Okay look at this request come down see which IP uh because here we have IP addresses. You can see that here we have the IP addresses. So, we need to correlate them together and see, okay, this IP address is hitting my websites a lot. Like, look at this one for example. It says hitting the website mo almost 2,000 times. That's inhuman. A human person will not go through your websites and try to refresh the page 2,000 times in uh the span of two weeks. So, that's how you know that you have some outliers, some uh automated programs that are trying to get something from your website if it if they found it. So here this our security is great. So and we don't here we are not using PHP. So the resources that they are trying to get are not in on the website. So that's fine but just so that uh we make their life hard. We can't block them. So let's see how to do this. Now we understood the problem. Let's see how to solve it. Let's exit now from Btop. We no longer need it. You can do two things. You can either look at the dashboard here and see the the IP addresses that are that have the high amount of hits or we can ex uh tell go access to give us a JSON file. So if we do sudo go access let's look at this command we can change it a little bit here we are looking just uh at the the today's log and here we do dash o to specify the output but we can here in this case it is an html file but we can also generate a JSON file instead of html let me do that I'll go here and I will remove this path and replace it with this one. You can see that I have parsed the log file and I have generated a JSON inside uh the web app folder. Let's ensure that we have the file there. And yes, we do. So now we can write a simple Python file to analyze this data. Here is the content of that script. And let's see what we are trying to do. First we we load the file. So if we find this great we will have this data dictionary and otherwise we exit if we don't find it we we load the hosts and from that we get data and we will print the IP addresses. I will save the file and I will run it just to see what we will get. Okay so this is what we are getting. This is the traffic that we get today. Okay. So these are all the IP addresses that uh reached the web server. And here you can see for example this IP address sent 76 uh hits today and these these uh these ones send just three four five. Okay. So these are low. That's not a problem. Even 76 it's not that big of a problem. But if I go to my own personal website which which is um an old [clears throat] one uh it has been deployed for more than a year. In that case I have some cases where one IP addresses one IP address will send in 6,000 requests 10,000 requests. Those are clearly automated bots that I usually block without thinking about that. But let's say that this IP address uh is a bot. It's not a normal user. What we will do? We analyze this output and we see okay so these are not doing bad things or they are not wasting bandwidth. We can keep them now but if in the future they change their behavior we can spot that. So now we copy the IP address and we use the uncomplicated firewall to plug them. So we use sudo UFW. We type deny from this IP address to any and this will block that specific IP address. And you can see UFW says that we have a new rule. Let's confirm that. I will clear the screen and I will run sudo UFW status. And as you can see, we denied this IP address from accessing anything from the web server. So now when they try to make a request is going to be dropped. it's not going to reach Ajax at all and that's what we want to see and this is how we block nefarious actors that are trying to do us harm. This was the first sub chapter we have talked about observability. Uh but in the next sub chapter we are going to focus on reliability. That's one is also important. We are going to try to put your server under stress. Meaning that you need to know this virtual machine how how much traffic can it support. So if I have 100 people who are trying to access the website at the same time, will my application be able to handle that? I cannot just say it like this. I need to test that. I need to put my server under stress to confirm that yes, we can support that. Otherwise, no, we need more resources. So we need to use a tool that will help us answer this question. We will also see solve the problem with engineext logs. So we we are going to tell index hey please don't delete the files that are older than 14 days. Instead delete files that are older than a year or something like that. And I'll also show you a tool that will that will allow you to analyze the disc the disk space to see the uh what is what are the things that are consuming a lot of storage so that you can delete them if you want to. and uh more things. So that's what we are going to talk about in the next subchapter. In this sub chapter, we are going to talk about reliability. Let me show you what I mean by that. I've mentioned that we need to simulate concurrent user traffic locally using a any tool. And in the Python world, we have locust, which is an amazing tool that will allow us to simulate a lot of people trying to access our website. to answer the question, can my website handle x amount of traffic? So with locos, we have a class called fast http user that will allow us to boost execution capacity by five times. So that's something that I want you to use to benefit from it. And we are going to use this tool called NCDU to analyze the disk space to see what are the things on our virtual machine that are consuming space to determine if we can delete it or not. If we are running out of storage of course we are going to use systemd journal D. So we are going to enforce memory constraints and build a monthly automated cache and package cleanup system. So we want the system to remove things that are normally not used by our application monthly. So this is just really uh something that we can add and it's going to run automatically uh for us to save us some space. Amazing. That's what this these are all the things that we are going to look at. Now let's go to the terminal to apply this. So last time we saw that uh the last thing we did is to block an IP address that is trying to do bad things to our virtual machine. Okay. Now we can continue from this. We need to install a tool called locust. But we need to be careful. We should not install that here on the virtual machine. Instead we need to install it on our laptop. Because if you install it here on the virtual machine and you point it at your server, the data that you are going to get is going to be incorrect because Locos is going to consume some memory and your web server is trying to fulfill all those requests that are sent by locos and it's going to fail or it's not going to fail but it's not going to handle all of them because locos is there is consumer uh CPU and memory. So we want to put that on our laptop and then perform the the test. And in order to install it, you use pip install locust. That's how we do it. And um after that, you create a Python file uh locally to perform the test. Okay. I will open the terminal, a new terminal locally, go to the correct directory and I will come back. I am back and as you can see, I have opened a new terminal. I am now inside the project. I can go to back end and here I have created that file inside tests load because this is a uh load test and this is the file that I was talking about. It is called locust file. You can put it wherever you want but in my case I decided to put it here because it is some type of testing. Now let me open VS code to show you uh the contents of that file. So let me zoom in and let's see what we have here. Obviously you need to change this based on your application and you want to test a few endpoints. So here I have a few variables and but these are uh the things that you can that you can tell locus to test. So you create specific tasks. Here I have a few of them and each task receives a weight. So a task with higher weight is going to be preferred over a simple task. So here I am giving a higher weight to the uh to the latest and uh recommendation endpoints as well as the search endpoints because these are tasks that are heavy that are going to do a lot of work and I want locus to uh focus on them so that uh because if we have a lot of requests that are doing search that will use more resources and here in your test let's just look at one uh just as an example so we tell block us to uh go to this endpoint here. We uh we take random inputs. So we take a random query. Here are the search terms that I have here. So I can search for Inkscape, Python, M search and um I want to uh randomly take the article type, sort field and sort order. And because this is a post endpoint, I need to have a payload. And after doing that I can uh send uh a post request using the client. And here the so you can see that this is the the client that we are that we have here. And here is that fast HTTP user class that I was talking about that will boost the execution capacity by around five to six times. And uh you can see that it's simple and uh you can do the same thing for your other endpoint. But again this is specific to my application. These are the endpoints that I have there. But if you have other things, you can change um the endpoints to suit your application or your back end. Okay. So this is how we write that locus file. But now what do we need to do? Let me show you. So after coming here first I need to activate the cond environment because uh locally I I like to use Anaconda and here inside that virtual environment I already have locus. So what I need to do just type locus and hit enter. Okay. So there is a problem with loading imports. That's not an issue. I think I need to go back to back end and then run it like this. We use locos and we tell it here is the file. Okay, now it is working. And as you can see by default locos will pick the this port 8,89. But if you want to specify a port, you can do it like so. You can come at the end and add the host option uh to add the endpoint. But let's just keep things simple and let's uh go to this website. Okay. So this is how locus looks like. Let me use light mode so that you can see things clearly. Now what do we need to do? Okay. So let's change the number of concurrent users. At peak we will have 50 people trying to access the website. Here the the spawn rate or the ramp up rate is going to be five. This means uh locos will try to add five new users every second. So that's uh what it means and host here we need to put the production website and make sure to do this testing only when uh your website is not used by people. So for example before launch you will come and do the testing or if you have a staging area before production you can test that but never test production because if you increase the number of users that might create problems and if you want you can also open the advanced options but let's keep things simple. Once we do this, we will have two things here in this terminal. We will see some uh some data after stopping and because here we also are using the dashboard, we can see it here. So that's fine. But we can also I can also use go access to see that interactive terminal dashboard uh to get the data. So let's try to do that. So we we have sudo go access. So this is what we have used right. Let's run that again and let's see. So currently we have 214 requests. Okay, that's fine. Uh I'll try to put this uh below like this. Let me also put uh this on top like that. Uh but let me decrease uh the font size a little bit so that we can see this better. Okay, are you ready? So this page is fine but since it is going to be generated every hour we are not going to see realtime data that's why I am going to close it and close these tabs because we don't need them. Okay let's hit start and let's see what will happen. So okay so now we are get we are creating the we are generating the requests and look here you can see that we are getting some traffic and here we are also uh so this is the amount of data that we are consuming right now and uh okay so it is running and make sure to run this uh for 5 to 10 minutes don't let it running for an hour or more because that might create a problem maybe your ISP or if you are using cloudflare or uh other stuff they might detect that you are under a DOS attack and they might block the IP. So just run this for 5 minutes and then get the data and see uh if your server was able to handle all that traffic or not. So I will wait for 5 minutes and then I will come back and let me try to make this bigger so that we can see things better. Okay. So here you see the number of requests. You can also stop for example if you reach 8,000 or something like that. So currently we have we we served 1,000 requests. Here you get the the data that we are going to look at after that. So you here we have the average response time, minimum, maximum and the current requests per second. So for example, this endpoint is receiving three requests. Again, as I said, I will wait for 5 minutes and then I will come back. I am back. We have reached 5,000 requests. I will stop now because I have the data. Okay. So we can read this table or we can also check the other tabs. If we click on charts that gives us uh that will plot the data. Uh so we can see that we started at 123 and we finished at 1211 and uh so here what do we have? So we have the total requests per second. Uh on average we were getting 16 requests per second and it also shows us the requests that failed. So that's interesting. We also has 16 requests that were failing per second. We have here the response time. So normally it's small um but sometimes we get these spikes. That's interesting. This is the ramp up. So this is the number of users. So here we started small but then we reached the maximum value set to 50. Okay that's good. But if you are curious why sometimes we get spikes in the response time that's normal. This is what we call tail latency. And if you are curious, there is this amazing article that you can read and uh so by Andrew you can see that here on this image it it shows you that most requests are going to have low response time but some of them are going to have long response times and this is what we call tail latency. It has nothing to do with your application so don't worry about it. Uh but if you want to uh read more about it to see why that happens even though the request is going to take the same amount of time to process, you can read this article. I'll make sure to uh it is already on the documentation. So you can find it there uh if you go to the repository. And again just a reminder if you don't if you don't know what I am talking about, this is the repo that uh that I'm that you can go to. So it's called fullstack deployment handbook and inside the book folder you can find the content for this specific module and here if you search for tail latency you'll find that article here we can also go to uh the terminal let's see what we have here and we can stop this and this will also show us some data. So if you it's the same data that we have in the dashboard. So you can see that here we have 5,000 requests in total and these are the error. So you can see that we have 429. Ah so now I understand why we have this error because in my API endpoint I have rate limiting. So that's why we get 429 because once we reach I think 20 requests per uh per minute uh I block the request. So that's why we were getting this. So I was confused why we have a lot of errors, but that's fine. So again, that's just another layer of security or it's a layer to block people that are that want to scrape your data or something like that. So that's why we have this. So that's completely understandable. That's amazing. I told you that the rate limiting is done by minutes. So this is why we have this specific recurring shape because here if you look at 12 six okay so the this specific user was below the limit but once they reach that the number of errors starts to increase because they made a lot of requests but after the end of that minute we reset so that's why they are able to make successful requests but then it increases. So now let's go back to the statistic to the statistics page to see what we have here. So on aggregate the average response time is 100 milliseconds. That's amazing. So if we look at the search uh endpoint it's done is in less than 100 milliseconds. That's that's really great. But if we go to max which is the SP the tail latency we uh we have to wait for 2 seconds. But that happens very rarely. So what you want to look at is basically the 50th percentile. So that's what we have here in this chart. this green. Yeah. So, if I remove this, if we look at this green graph, you can see that this is what we want to care about because most requests are going to be here. And you can see that the response time is slow is small. It's less than 100 milliseconds. So, that's that's really great. Uh that's all I wanted to show you here in locus. You also can look at failures, exceptions. We don't have those. And if you want, you can download the data. So now we can close this because we have stopped the server here and I can also close that. And if you look at Btop you can see that the server was doing a lot of work uh here. But after that yeah now uh it is sitting idle because there are no requests. I can also close that. And here if you look at this dashboard you can see that here are the yeah that's 5,000 requests that we received today. And here are the end points that were uh that that received a lot of work. And you can see this is based on that those weights that we specified in the tasks. So track search were the ones that had the highest weight. That's why the we have a lot of uh hits. Amazing. Now we can close this and we can look at the NCDU utility. You know if you want to look at this disk this space you can use du-h and that gives you this information you can aggregate it stuff like that but again it's not uh a tool like btop where you can interact with it so that's why there is a tool called ncdu that allows us to inspect the disk util us usage uh with a terminal user interface and that tool is called ncdu and on ibuntu you can use a to install it and once that's done you can use sudo incdu and here we are going to do a full scan starting right at the root directory so that will take some time but as you can see already we have a nice in application running in the terminal yeah we can use to analyze the disk usage and as you can see I can use the arrows to go through all of these and here let me show you what we have here so currently the total disk usage is 6.2 GB. Here you can see that under the user folder we are use we have we are using 2 GB and here my web application is using 300 mgabytes. If I want to understand what is inside user I can hit enter. Now I am inside can go to library. Okay. So here we have things related to Linux. I should not touch that. Here we have Python. I should not touch that because I am using it. Um so that's how you use it. If I go want to go back, I use I go up and uh uh so that will take me uh to the user folder and now I can go to maybe home. Let's see what we have there. Okay, so we have my user there and this these are all the things. So here we have NVM because we are using node, we have cache. So maybe if you want to remove the pip cache, we can come here and hit D. That will delete it. Okay. And if you want to learn more about the commands, you can hit you can press the question mark and that will give you this help panel that shows you how to move. So if you want to move your cursor, you can use the up and down keys. If you want to open a directory, you hit enter. If you want to delete, you use the D key, etc. And you can uh use the up and down arrow to see the other commands. So let's hit Q to close. And let's close out of incu. And to do that we use Q in order to close. And that's how we use incu to analyze disk space and to delete uh any folder that is no longer needed. Okay. So now let's try to fix the issue with uh rotating logs. In order to do that we will open the journal D configuration. And here I want you to search for this. So in order to search, you type, you hit Ctrl W and you paste the term that you want to search for and just hit enter and there it is. So what we need to do, we need to uncomment this in order to tell Linux to change the amount of of logs that we want to use because by default on Ubuntu, we allow uh systemd journal D. So this damon is going to consume up to 10% of your total disk volume just to store logs which is a lot. So if you want to change that default you come here you unccommend this option and you set it to 100 megabyte or any other value. So now after we save this we need to restart the service. So how do we do that? We use systemctl and we restart it and we now we can run status to see if it is running and yes it is. That will uh reduce the amount of space that we allocate to logs. But in order to fix the engineext issue we go here uh etc log rotate and we go to engineext. And as you can see here under rotate we have 14 days. So after for everything older than 14 days will be deleted. I will change that to 365. And this value depends on the disk space that you have left. So if you don't have a lot, this is going to cause you a problem because storing 365 files, it's a lot. Verify that you have enough space before doing this. Okay. Yeah, I'll just do it because in my case, it's uh it's not an issue. And I will exit. And it also depends if your website receives a lot of traffic because let's say that I only received let's say a thousand requests a day that's not a lot. So the log file is going to be small but if I receive 100,000 requests that's a lot. So one file is going to be very huge. Multiply that by 365 and uh imagine the amount of disk space that you will be consuming. So yeah now we won't we won't delete older files and in the dashboard we will be able to see a large uh duration so that we can see what how what is happening across months or weeks. Finally we are going to create an automated script. I will put it inside user local bin because if you remember when when you put a a bash script there you can run it in any location. So that's the benefit of putting it right there. And this specific file it's small it's not that big and this is what it is going to do. So here set e is awesome. So if we have an error is any step we are going to terminate and we are going to exit because we want to run all of these steps without an issue. First we clean um anything from apt and we use auto remove with dash y to accept the the things that we are going to delete. The journal ctl vacuum time is a command that will delete logs that are older than 2 days. Here we are removing pip cache and pmppm cache. And we print echo system cleanup completed. And I want to do this let's say once a month. Okay, so I will save the file, exit. I will use C cho to make it executable. Let's go back to chron tab. Let's go to the end of the file and let's add the following line. And this is going to run this um script at 3:00 a.m. in the morning on the first day of every month. This is what we have why we have this. And the logs are going to be stored inside this file. But we can see if we are getting the correct echo message. Okay. Now I can save and exit and that will be run that that specific script will run uh once a month. But you can also test it because as I mentioned once we put that thing there you can see that once I hit tab it detects it. And now I can hit enter and it says that I need to use pseudo. Okay that's understandable. And because we have put it inside p sudo that's fine. So it's it is going to run without an issue. So now it is uh using apt get to clean the cache. This is the vacuum. So you can see that we deleted logs that are older than 2 days and you can see they are using a lot of space. So here we have maybe seven uh so 82 megabytes and we have the correct echo message and this is what we should see in that log file. Amazing. We have finished this reliability subchapter. Now we have one more subchapter that I want to add before ending this book or this course and that one will be will be about continuous security. If you remember before uh we have created a pipeline that runs uh let's see so we have SUS and we also have a light security scan using OASP zap but that one runs in 2 minutes so that's why we have included it in the pipeline because it is not going to block us but there is a a heavy where it is so this one is lightweight but there is a heavy version of this that takes a lot of time that's why we should not put it in the pipeline because otherwise instead of waiting for 8 minutes we might wait for 30 minutes or more that's why I didn't include it in the pipeline but in the next sub chapter I will show you how to create a GitHub action that will run daily and that action is going to run that heavy security dynamic security scan. So that's what we are going to see in the next sub chapter. It's uh it's a bonus that I wanted to add to show you um how that happens. And the beauty about that one is that it's going to run automatically. We don't need to trigger it with a pull request or something like that. It is going to run let's say every day at 3:00 a.m. or uh something like that. We can specify maybe even hourly if you want. And uh once when we see an error there, it means hey we have a problem that we need to solve. But never include that in your pipeline because that will make it very very slow. We arrived at the last sub chapter in module 6. It is titled continuous security. Let's see what we are going to talk about. We have mentioned in the end of the previous sub chapter that in the pipeline we are performing a light dynamic testing. So we want to move from that state into performing a complete synchronized stack. And that's what I mean by a heavy security scanning because we need to run the front end, we need to run the back end and other services. In my case, that other service is M search. So we want to have all of these things interacting together while running the dynamic testing. We are going to also use OAS, but we are going to configure it a little bit to perform that heavy scan. And as I said, we are going to use a an automated GitHub action that will use the chrome syntax like we did in the chrome tab uh recently to run this scan following a specific uh frequency or interval. And like before we are going to use that uh zap rule set that specific TSV file to uh avoid uh false alarms. So things related to engineext security headers stuff like that. We are here in my second account. This is my fork. I need to uh click on the repository. Uh let me remove these uh tabs. We don't need them. And here on GitHub, you can hit the dot key to open uh VS Code directly. So if you want to do things quickly, uh you can do them that way. So just hit uh the dot key and that that will open VS Code. Uh so here it says the extension yes allow login. Let's continue. The only thing we need to add is a new uh workflow and here we are in the master branch. So we cannot push directly to it because it is protected. So let's create a new branch. Let's call this feed daily bas scan and let's create it from the head. Let's switch to that branch. Okay. Now open the GitHub folder and add this specific file inside the workflows folder daily deep security scan. I will copy the full content and I will paste it here. You will see that this is similar to the other file where it is this dust scan. Uh I'll put them next to each other so that we can see we can see the difference. The only thing that we have here, you can see that here we have on workflow call. But here we also add this option which is to schedule this. So this means that this GitHub action will run daily at 6 UTC. We don't need to. Yes, we have the option to run it manually, but on top of that, GitHub will read this chrome syntax and it is going to understand that we wanted to run this for us every day at 6 uh UTC. And that's the the only the the first difference, but we have another one. So here uh so because this is a deep scan, we want to time it out. So if something bad happened maybe uh we didn't install dependencies correctly or something was not working uh well we don't want this job to get stuck. So we are going to fail if it takes more than 30 minutes and this value can be adapted based on the scale of your application. So if you have a lot of endpoints and a lot of services the scan will take a lot of time. So you want to run it maybe two, three or five times to get the average time and then put it here. But if we see if we look at the other stuff, let me uh decrease the font size. First of all, we define the environment variables for me search. But then the next steps are similar. We check the repository. We install Python. We install the backend dependencies. Uh we are here. We after that we install me search. Uh okay then we start it we use that script to see the so let's see so here we are starting mill search and uh here you can see that we have that seed script here it is here is that same step and we also start the back end so here maybe I could uh put this here to have the same order let's put it there you can see that here we have se test data we start the backend server and after that so here we stop because we were testing just the back end. We weren't testing the other things in the stack. So we can see that here after arriving at this we just pointed at the open API JSON because we have a fast API back end that will tell Zap all the end points. But here we continue. We install we set up PNPM. We install NodeJS. We install the front end dependencies. We build the front end. And then we start it. Here we are using pmppm preview because we want to simulate a production environment. We are not using pmppm dev because that is a dev server. We want to use pmppm preview. And here we uh like before we are using a for loop just to ensure that the front end started and and it is ready to uh be used. And finally we perform the full scan this time. And if you look at the image that we are using it's different. So here we have action API scan because this is uh used for API endpoints but here we are using the full scan and here you can verify the version to see if there are new ones. If there are make sure to update and here the target also changes. So we are pointing at the front end. It's like a user that will go there and try to do malicious stuff. uh we are we don't we don't just point at the back end and we are reusing that TSV file and uh yeah the this the other options are are the same but after doing this you can see that we are going to test everything in our stack mainly search the front end and the back end as you can see both of them are similar we just uh added running the front end and uh we changed the image that we are using here to action full scan amazing now let's commit this file. So let's come here and let's say that field add a new workflow for full dust scan commit this and push it to this new branch that we have created. And now we can create a new merge request and point it at my master branch. And I hope that we don't have a problem with the dependencies. I think we will because it's been a few weeks since I've worked on this. But if we yeah if I get some problem while auditing the dependencies I'll try to resolve them and I will come back and uh Okay. So let's wait for this to finish and then we can go to the actions tab and look for that new file and then we can run it manually from here. Let's wait for this to yes thankfully the auditing step passed. That's amazing. Uh so that means that this will will not fail hopefully because we haven't changed anything in the source code. I am back and as you can see we can merge the pull requests because we don't have any error. Let's confirm the merge and I can also close this tab because I no longer need it. Let's go back to master and yeah as you can see it merged now. Now we can click on actions and we should see this new scan. Yes, here it is. Daily deep security scan. Let's click on it. And as you can see, normally if we wait, it should run at 6 in the morning every day. But now it is 12:00 a.m. or 12:00 p.m. So I cannot wait for that. So let's just hit run workflow. Make sure that it is on master and hit run. And uh yeah, it's just one simple job. If we click on it, we will see the the steps. We installed Python and now we install all the dependencies that we need for the back end. We downloaded M search. Now we start it and we need to seed it with data so that the end points will work and won't return errors. We have restarted the started the back end and as you can see we sent a request to help to ensure that server has started. Now we are installing NodeJS the front end dependencies. We are building the front end. That shouldn't take a lot of time. Okay, that's done. Now we start the front end using PMP view. And we see the front end is ready. Great. And now we are starting the full scan. Hopefully that I haven't forgot anything to add to that TSV file. Normally this should pass because uh I haven't changed anything in the back end and I have tested this before on my personal project or on my websites that I have deployed to production. So it shouldn't fail. But since this will take some time, I will wait for it and then I will come back. I am back and as you can see the scan didn't fail. That's a good sign. And in my case it took 6 minutes. You can see that this is already uh taking three times more time than the light one. The light one took 2 minutes and this one took six. So, and it runs successfully and this is going to run uh every single day. But as I said, if you have a a large application, my personal work is not that big. So, I have a few endpoints that's why it took 6 minutes. But if you have a lot of applications, a lot of services in your app, this time will increase drastically. And that's all I wanted to show you in this subchapter. Thank you for watching. The next video will be about we will look at all the things that we have learned together across this long journey that we took from the first sub chapter where we were just talking about what we are going to do. And now as you can see we are able to perform scans, block IP addresses and do a lot a lot of things. And I hope that you have learned a ton from this course. The the next video will be simple. It's not going to be very long, but we will try to at least give you something to that that you can use to basically tell you um maybe remind you of all the things that you have learned. Congratulations on reaching the end of the course. You have successfully journeyied from a bare Linux server to a fully hardened self-sustaining production application. These are all the things you have built. In the first two chapters, I have showed you how to harden SSH access by removing the ability to authenticate with password, removing the ability to connect as root user. You also learned how to play with the uncomplicated firewall. We have learned how to use engineext socket proxying to talk to our J unicorn/ Uvic application. On chapter three and four we have focused on performance and data. Here you have learned how to isolate the the me search engine by creating a new user that we called me search and that user was managing the this service and uh we also made sure that the environment variables that med search requires are stored in a way that cannot be read by other users. We have played with go access which allows us to read the enginex logs to create really beautiful dashboards that shows us what is happening on our web server. We have also learned how to use cloudflare CDN to serve the static files quickly across the world across the globe and we have we have used SSL certificates to make your website secure. And finally in the last two chapters we talked about automation and security. Here you have learned about pre-commit hooks so that you don't push code that is broken. So you need to pass a few tests in order to be able to commit and push. You have learned about CI/CD testing pipelines, atomic rollbacks, automated dynamic application security testing and also uh testing the libraries that we are using in our application. After doing everything, now our application is ready for production. Now your system is observable. You have go access which allows you to read the logs and get realtime metrics. It's automated meaning we will have zero downtime. We have seen that even if we kill a UVON worker the J Unicorn process manager will create one for us. So even if you have a lot of traffic and maybe one of the workers dies everything is automated. So you will have zero downtime when we deploy our our application. If something happened while installing the dependencies, the application is not going to crash. We are going to roll back and use the all dependencies that we had before the deployment. And finally which is this is the most important point security. We have made sure that people cannot log in using SSH as root or by using a password. We you have SSL certificates that renew automatically so that people who connect to your website are connected securely and we have done a lot of testing jobs in our CI/CD pipeline. I really hope that you enjoyed going through this course with me and now we are completely ready to build, launch and scale your features with absolute confidence. Thank you for following along and happy building.
Generated algorithmically for Search Engine
Indexing.