I have been wanting to write a post about this for a while. Combining some of my favorite technologies (Node.js and Amazon Web Services) into a post seemed like a fun idea and something I was excited about writing about.
Although there are a lot of tutorials about deploying projects to Amazon Web Services (AWS) I thought I could take a different approach than the articles I have seen and add some value to the current tutorials out there.
I have written a high level lightning talk about EC2 and AWS in the past, which you can find here. I hope to build off that and give a much deeper look into how to set this up for your project.
I hope in this guide to really break things into small pieces and explain a few of the alternatives of each step we do. On a high level here are the steps I hope to cover in this tutorial:
- How to create an EC2 instance to run the server
- How to install Node.js on the instance
- How to get your source code onto the instance
- How to ensure that your server remains running on the server even after connection has been broken with your local machine
- How to map your server port to port 80 to run on the default HTTP port
- How to link your server to a domain name
- How to install MySQL on your instance
- How to install MongoDB on your instance
This tutorial is written in a general order. But certain steps such as the database stuff is at the bottom due to the fact that not all applications require a database. So depending on your application pick the parts you need, and jump around this tutorial to setup your system depending on what your application requires.
If you have any questions throughout this tutorial I would encourage you to Google, ask on Stack Overflow, or contact me.
Quick disclaimer before we get started: I'm not liable for any costs that Amazon Web Services or any other service I discuss might charge you. I encourage you to look into the pricing of each of the services we discuss, before using. I'm also not liable for any data loss or problems with deployment. There are a lot more security things, and other items that you have to consider before deploying your service/site. This guide is meant to get your feet wet and give a high level overview of deployment but this is a topic that could be discussed for days on end, and still only scratch the surface. Continue to do your own research and realize this guide is meant as a high level overview. I'm not liable for any costs, misinformation, data loss, security problems, related to this post.
In order to start this process we need to create an EC2 instance that will run our code that we are going to deploy to AWS. In this step I'm assuming you already have an AWS account created and are ready to get started.
An AWS EC2 instance is basically like a virtual server/computer that runs an operating system such as Linux or Windows or some other operating system. One physical AWS server in their data centers might have many EC2 instances on it. But each EC2 instance is setup in a dedicated environment, meaning you won't be able to access other EC2 instances, unless of course they have internet access and you access it through normal means. This EC2 instance is what we will install Node.js on and setup our entire application on.
Once you are at the AWS Console Homepage click on EC2 to open the EC2 console. It should look something similar to the following.
Once you are at this page click on the blue
Launch Instance button to start the process of creating an instance.
For this example I'm going to choose
Ubuntu Server 16.04 LTS (HVM), SSD Volume Type. I'm doing this because Ubuntu is a very common Linux OS that I have found to be pretty reliable for these type of projects. I would encourage you to look into what every OS is and the benefits and drawbacks to each, but Ubuntu is an overall pretty safe choose to start out with.
The next page has us choose our instance type. This is where the EC2 pricing comes into play. The larger instance you choose the more it is going to cost you, but you will get more resources (memory, and CPUs).
For this example I will be choosing the
t2.nano instance type, because it is the cheapest for me. I would encourage you to consider how big your application is and what kind of resources your application will require, along with your budget for your deployment. Each instance has a different amount of processing (CPU) power, and memory (RAM) that your instance has access to. As of writing this post you can find the details about the different instances here, you can also find the pricing information here (note: I will not always provide the pricing information during the steps, as I mentioned at the beginning of this post please be aware that most of what we talk about costs money so please be aware of the pricing related to what we are discussing, as discussing the pricing is not really covered in this post).
This next option has us choose the details of our instance we are going to launch. We can choose how many instances we wish to launch. It asks us if we want to use Spot Instances (which tend to be less expensive, but AWS can shut it down at any point, meaning it's not as good for 24/7 web servers) or not. It also asks us if we would like to assign an IAM role to the server, this would allow us to access other AWS resources without putting API keys in our source code, which tends to be a good idea. There are a lot of other options here I would encourage you to look into.
For now I will be leaving everything at the default settings and going onto the next step.
This step asks us how much storage we would like to attach to our instance. It also asks us the type of storage (SSD or Magnetic). 8GB is fine for our application so I will move on to the next step. Although if your application requires a lot of storage I would encourage you to consider the workflow of your application and consider increasing this. For most applications 8GB is fine, but if you are going to need to store more than 8GB on your instance at a time I would encourage you to think about increasing this or better yet using a service like S3, or other storage options AWS provides.
This step asks us what tags we would like to assign to our instance. This can be helpful to identify our instances in the AWS Console, billing reports, or load balancers as you scale. At a minimum I would encourage you to add a name to your instance. If you have multiple products or applications and many instances per application, it might be a good idea to tag them by product or other meaningful tag.
You can find more details about AWS tags here.
We are almost done creating our instance. This step asks us to choose our security group to assign to the instance. We can either choose to create a new security group or use an existing one.
This is an important step as not defining this correctly can cause your instance to be inaccessible. So we have to make sure we give it enough access, but giving too much access makes it easier for attackers to target your instance. You can always change this later as your needs of your instance change.
For today I will be creating a security group like the following. Enabling SSH for my IP address (so that we can use SSH to access our instance later), enabling HTTP for all traffic, and enabling port 3000 for all traffic. You can see this in the screenshot below.
The security group basically dictates what ports and IP addresses have access to your instance. Any ports or IP addresses not designated in the security group will not have access to your instance. This is a very good security method if you only want your web server running on port 3000 to be accessible from your IP address. Or if you want the MySQL database only accessible from your IP address.
This is basically like a firewall that helps limit internet access to and from your instance. Mainly it's used to limit access to your instance because it is assumed that traffic going out of your instance is approved since it will be normally traffic that your application is requesting and that you are aware of (since you wrote the code).
We could have set a name and description for this security group, and I would highly encourage you to do so, just to be able to know what each security group does later on. The next step has us review everything that we set to create our instance then launch our instance.
Once we click create it will ask us which key pair we would like to use for this instance. If this is your first time creating an EC2 instance you will need to select
Create a new key pair and enter a name. After that click
Download Key Pair.
The key pairs basically let us login to our instance using SSH to run terminal commands on our instance without needing a password. The key pair that we generated acts as our password so it's important to keep it secure on your computer as it is like a password to your entire instance.
The next step that is not required but I highly recommend is to add this to your SSH config file on your computer. Sadly because I don't have a Windows PC I'm only able to give details about how to do this on a macOS computer.
The first step is to open Terminal. Then type
mv (remember the space, and don't hit enter), then drag the file that was just downloaded right into your Terminal window. This will add the path to the end of the command, after that ensure there is a space after the path and type
CHOOSEANAMEHERE with any name you choose (probably would be a good idea to put in the same name as you put into AWS). At the end your command should look something similar to
mv ~/Downloads/Tutorial1.pem.txt ~/.ssh/Tutorial1.pem. If it does hit enter. This will move the file that was downloaded into your
.ssh folder. From there run
nano ~/.ssh/config in your terminal to open
~/.ssh/config in a terminal text editor. You can also use any normal text editor to open this file and edit it. Add the line
IdentityFile ~/.ssh/CHOOSEANAMEHERE.pem to the bottom of that file (replacing
CHOOSEANAMEHERE with the name you set the file to be). Then save this file by hitting
Control + X then the
y key then the
Enter key, or save it normally if using a different text editor. Finally run
chmod 400 ~/.ssh/CHOOSEANAMEHERE.pem (replacing
CHOOSEANAMEHERE with the name you set the file to be) in your terminal.
Next step is to go back to the AWS page and click
Then after it finishes launching click
Make sure your instance is selected and click
Connect at the top of the page.
Copy the URL below step 4. This will look something like
Back in your Terminal run
ssh [email protected] (replacing the URL with the one you just copied). This command will look very similar to the example command AWS provides right below that. The only difference is that I removed the
-i "Tutorial1.pem" part. I did that because I added it to the
~/.ssh/config file, so that I don't have to type it in every time.
Once you get to that step just type
yes and hit enter.
If you see that you have successfully connected to your instance. Any terminal commands you run now will be run on the remote EC2 instance. To exit this and go back to your local terminal and run commands on your computer again just run
logout and hit enter. This will log you out of your EC2 instance SSH.
One final note about this. There are many alternatives to using AWS EC2 instances to deploy your project. You could setup your own Ubuntu Server, or use another host such as DigitalOcean, or OVH. There are so many different options for hosting your site. The most important thing you have to remember is that it needs to support Node.js. Most shared hosting services such as GoDaddy or cPanel based solutions don't provide support for Node.js. You need an actual instance that you can SSH into and access. Using an official VPS is normally a good way to go.
Next we are ready to start installing things on our instance.
The next step is to install Node.js on our EC2 instance. I suggest doing this using NVM. But you can also just install Node.js manually on Ubuntu. For this example I will be using NVM so that it's easier to manage and upgrade Node.js versions.
To start, SSH into your instance by opening your Terminal and running
ssh [email protected] replacing the URL with the URL you got from your AWS EC2 instance.
Then run the following commands in your terminal once you are connected to your instance via SSH.
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm nvm install 8 # This will install Node.js version 8
Then after you run all of that you should be able to run
1+1 and hit enter. This should print
2 in your terminal. You can hit
Control + C two times to exit out of this interface.
The next step is getting our source code onto our EC2 instance. There are a couple ways to do this. You could use SCP to copy your code onto your instance from your local computer, you could clone a Git repository onto the instance. You could also use something like AWS CodeDeploy to automate this process even further.
I personally prefer AWS CodeDeploy because it is SO easy to deploy updates once you get it setup, especially for more complicated applications that require a lot of setup for each deployment. I can deploy updates without having to SSH into my instance at all. It's all very automated and easy to work with.
For this tutorial we'll be working with SCP, because it is the easiest to get up and go for super easy applications.
Since we are using SCP, sometimes I have found it's very helpful to delete all of your
node_modules folders before running the SCP command below. This tends to make things go a lot faster and I highly recommend it.
From your local computer (not SSH) you can use the following example command to copy your code onto your instance.
scp -r LOCALPATHTOFOLDER [email protected]:~/app
This will copy the
LOCALPATHTOFOLDER to the
~/app folder on your instance. Of course you will need to replace
ec2-18-232-175-210.compute-1.amazonaws.com with your settings and what you want.
Once you get your code onto your instance you should be able to SSH into your instance and change directories (
cd app) to get into your code.
If you did remove your
node_modules folders locally before running the SCP command, be sure to run
npm install on the server after you get into that directory to reinstall all the packages again before trying to start your server. If you decided not to delete it you should be all good to go.
Then you can run
npm start or
node server.js or whatever command you run to get your code up and running locally.
If you then go back to your local computer and open a browser and go to
http://ec2-18-232-175-210.compute-1.amazonaws.com:PORT, replacing the url with your instance's URL and the port with the port your application is running on, it should load up your page.
Now there are a few problems with this approach. Once you quit out of your terminal, your server will die. And you can't expect your customers to enter a port number to get to your site. Both of which we will be covering how to handle in the next steps. If neither of those is a concern to you, you can probably stop here and be totally fine.
The next step is to ensure that even if we quit out of our SSH that our Node.js application continues running in the background. There are several ways to do this. screen, forever, or pm2 just to name a couple solutions. For this tutorial I'll be going over how to setup your Node.js application with pm2. I don't really have a huge reason why I decide to use pm2 over the others, I just find it has pretty good support, documentation, and has a lot of cool features built in. There are situations where I feel like it goes slightly overboard, but it's pretty easy to get it up and running.
The first step is to SSH into your instance and run the following command to install pm2.
npm install pm2 -g
You then need to
cd (change directories) into your code folder before starting the application.
Then from there you can just run the following command to start your Node.js application in the background.
pm2 start npm -- start
pm2 start server.js
Depending if you use
npm start or
node server.js to start your Node.js application (
npm start being the first option, and
node server.js being the second option).
From there your application should still be running even if you cut the SSH connection. There are a lot of commands you can run with pm2 such as
pm2 status, etc. I would encourage you to look at the pm2 documentation for more details on everything that pm2 can do.
The next step is to map your Node.js port to port 80 so your users won't have to type in a port every time they want to access your site. There are so many different ways you can set this up.
Now you would think you can just set your application to listen on port 80. But the system won't allow you to listen on port 80, unless you run your application as a root/admin. Which for many reasons is a pretty bad idea. The security risks of running your application as a root/admin are pretty massive. So we need to figure out how we can map the existing port (3000) to port 80.
Systems such as nginx provide this as one of their many features you can use if you setup nginx. You can also use something like AWS Elastic Load Balancing to map to port 3000. There are a lot of different methods to achieve this and reasons why you would want to use one option over another. Systems such as nginx or AWS Elastic Load Balancing for sure provide their benefits and can be very useful for certain things. So I would encourage you to take a look at all the options and decide what works best for your application.
For this tutorial we are going to be taking a very simple approach that doesn't require installing any additional software. Simply running the following command after you SSH into your instance will map port 3000 to port 80.
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000
Now remember to change
3000 to your port if your Node.js application is listening on a different port.
Now you should be able to test going to your site without the
:PORT part in the URL and your site should display as expected.
Next we will discuss how to link this all up to a domain name.
The next step will be to link your system to a domain name. There are lots of services that you can use to purchase a domain name (name.com, GoDaddy, AWS Route 53, Google Domains, etc). Almost all of these registers have a DNS service that will let you link your domain name to your IP address for you.
You can also choose to not use your registers DNS system and use something like Cloudflare, which provides extra benefits such as DDOS protection, caching, easy SSL, and many more features. I personally like Cloudflare because the time it take to change your DNS settings and have it update throughout their systems is SUPER fast. I have found other registers can sometimes take up to a few hours to actually apply the IP address changes you make to your domain name.
If you choose to use Cloudflare they have a super simple setup process to walk through the setups to set it up. After you set that up (or if you decide not to) either Cloudflare (if you set that up) or your register will have the DNS settings. You can copy the IP address for your instance from the AWS EC2 dashboard and paste it into the IP address field.
Sadly due to every system being vastly different in terms of design and exactly how you access it, I'm not able to give specific details about this step. Most registers or Cloudflare have amazing documentation about how to change the DNS record to point to a different IP address. Googling this or looking through your services support will likely yield good results for this.
If your Node.js application requires or uses MySQL you will need to set that up for your application. There are many different methods to do this. You could use a service such as AWS RDS, or create another dedicated AWS EC2 instance that just runs your database. There are advantages to both of these, but these solutions will probably cost more and be a little bit more dedicated than what we want for this tutorial.
We will just be installed MySQL on our existing EC2 instance. So our instance will now be running MySQL and our Node.js application.
The first step to install MySQL on your instance is to SSH into your EC2 instance. Once you have done that run the following command on your instance.
sudo apt-get update
It might ask you if you sure you want to do this, if it does you can just type
Y and hit enter to confirm the changes it will make. It should look something similar to the following after you complete that.
After you finished that you can run the following command to install MySQL.
sudo apt-get install mysql-server
After you run that it might bring you to a screen that looks something similar to this.
This is asking you to create a password for the root MySQL user. You can either leave this blank or create a password. Because the root user has full access for your entire MySQL server I would encourage you to really think about security here. Creating a very secure password and protecting it is very important. Considering how many password and data leaks there have been in the past please think about the security here and do your own research beyond this tutorial about how to secure your MySQL server.
Because this is just a test server I'm setting my root password to be
root. I would highly discourage this but because this is just a tutorial it is fine. For anything else please put a higher level of security to your MySQL server.
You can navigate down to the Ok button using your arrow keys then hit enter to continue to the next step.
It might then ask you to confirm the password you just set.
After that is all working run the following command to setup the MySQL you just installed.
It will then ask you a lot of questions. The first of which is asking what the root password you just set was.
After you enter that it will then ask you if you would like to check the security of user passwords on your MySQL server and require passwords to have a certain level of security.
For this example I'm going to set this to no, but this is for sure something you might want to consider for your databases security.
It will then ask you if you would like to change the root password, but because we just set it normally you won't need to do this, unless you want to change it to something different from what you just set it as a few steps ago.
It will then ask you if you would like to remove anonymous users from your server. It gives a good amount of detail about what this means. Probably would be a good security idea to remove those anonymous users.
The next step asks if we would like to disable remote access to our root user. Choosing yes here increases the security because only your instance will be able to login as root if you choose to disable this. But choosing no will allow you to access your MySQL root user from your own computer or from any other computer, which might be useful for debugging or testing things.
Keep in mind you can also change this later, and you can also create more MySQL users that have less permissions that you can give remote access to. So that might be a smart idea to create a remote user that has less permissions so that if a hacker were to get a hold of that information they couldn't do as much damage.
The next step asks if we would like to remove the test database. It suggests removing this before going into production so probably a good idea to remove it.
Then it asks us if we would like to apply the changes we just made. Choosing yes for this is a good idea, unless you wish to do it manually later.
You are all setup with your database now. To confirm it's running run the following command.
systemctl status mysql.service
It should output something similar to the following.
You can access your MySQL install by running the following command.
mysql -u root -p
It will then ask you for your root password that you created. From there you will be able to run standard MySQL commands such as
SELECT * FROM users; and such. To exit out of this you can just type
exit followed by the enter key.
Almost all of the settings and such we set here can be changed later. For example searching on Google for how to change the root password in MySQL will probably yield some good results. Or how to create another MySQL user. Or how to enable remote access for MySQL server. All of which will likely yield good results for how to customize and maintain your MySQL database.
There is a lot more you can do to improve this work-flow and add extra features, security, etc to your server. So I would encourage you to do some research about MySQL configuration options and figure out how to best fit it to your needs.
You can now use
localhost as the host,
root as the username,
3306 as the port, and the password you set when creating the root user in your Node.js application settings to get it to work. Remember you might have to create your database on the MySQL server first before your Node.js can use it.
Remember also if you are trying to access your MySQL server from outside of AWS (like your local computer) you will need to change your security group to give access to port 3306, to either only your IP address or to the entire public (probably a bad idea).
One final note. I have found DigitalOcean's tutorials on how to install MySQL on Ubuntu to be very helpful. DigitalOcean has a lot of tutorials about how to install different things on Ubuntu and get your server setup and configured the way you want, so I would encourage you to check those out for more details and other things you can do to configure your instance.
To install MongoDB on your instance first SSH into your instance and run the following command.
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
It should look something like the following once you are complete.
Next run the following command.
echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list
Then we can update the available package list with the following command and we will be ready to go to install MongoDB.
sudo apt-get update
It might ask you if you sure you want to do this, if it does you can just type
Y and hit enter to confirm the changes it will make. It should look something similar to the following after you complete that.
Next run the following command to install MongoDB.
sudo apt-get install -y mongodb-org
After that is complete run the following command to start up your MongoDB server.
sudo systemctl start mongod
You can then check the status of the server by running the following command.
sudo systemctl status mongod
Finally to have the MongoDB server startup whenever the instance starts up run the following command.
sudo systemctl enable mongod
You are now all setup to change your MongoDB settings in your Node.js server to port 27017 and host localhost.
Deployment is a very difficult topic. There are a lot of moving parts to it. As with any programming task try to break it into small pieces. Figure out what small task you need to complete then do the research to figure it out. Deploying an entire application is difficult since every application is different and there are a million ways to do anything. So realizing what you need to get things up and running is important. Do you need MongoDB or MySQL or neither? Does your application rely on some other service that you will need to install?
I hope this tutorial has helped with the basics of deploying a Node.js application to AWS. There is a lot more that we could discuss and go into. I hope this is a good starting point if you are wanting to deploy a simple application.
There is a chance I will be updating this tutorial as things change. I can't make any promises that it will remain up to date but if there is something that is unclear or that has changed or you would like to see added please contact me.
As I mentioned at the beginning if you have any questions I would encourage you to Google the question, ask on Stack Overflow, or contact me.
Good luck in your future deployments!!