AWS IaaS Tutorial

This tutorial will walk you through deploying a web app to Amazon Web Services (AWS) Infrastructure as a Service (IaaS). Specifically, we’ll:

  1. Create an EC2 instance, which is a rented Linux virtual machine running in an Amazon data center
  2. Install and configure a server-side dynamic pages server (Gunicorn)
  3. Install and configure a static pages server (Nginx)

In EECS 485, each team will deploy with one team member’s account. In other words, it’s not necessary for each team member create an AWS account.

You’ll create and configure one instance, and then reuse the same instance for each project. That means you’ll follow the create, launch and configure instructions once, and the deploy instructions multiple times.


If you’re here looking for advice about how to deploy a real, scalable web app, then STOP! This tutorial describes how to launch a single IaaS virtual machine and run a production server.

For a scalable production deployment using many machines, you’re probably better off with AWS’s PaaS products instead, tutorials linked below. If you arrived here from an EECS 485 project spec, skip these PaaS tutorials.

  1. Scaling the Database with PaaS
  2. Scaling the Static File Server and Uploads with PaaS
  3. Scaling Server-Side Dynamic Pages with PaaS (Part 1)
  4. Scaling Server-Side Dynamic Pages with PaaS (Part 2)

Create AWS account

Create an AWS account at https://aws.amazon.com. You should be free tier eligible, which means that you will be able to run an instance for free for the duration of the course.

You will need a credit card on the account, even if you only use free tier resources. This is how Amazon charges, in case you request more resources than provided in the free tier. Do not launch any additional instances other than the one we specify in order to avoid additional charges.

Optionally, you may redeem extra benefits as a student, including $100 in AWS credits. https://aws.amazon.com/education/awseducate/.

Start EC2 instance

EC2 AWS’s infrastructure as a service product. An EC2 instance is a rented virtual machine running in an AWS data center.

Navigate to the AWS Management Console. Select the “Services” dropdown menu, then “EC2”. An EC2 “instance” is a virtual machine running on Amazon AWS hardware.

Click launch an instance.

Select the same Ubuntu server version as specified in the P1 tutorial. Your version might be different from this screenshot.

Select the “t2.micro” instance type. You should see “free tier eligible”. Click “Next”.

Click “Next”

Click “Next”

Click “Next”

Add a rule to allow both SSH and HTTP traffic in and out of your instance. Then, click “Review and Launch”.

Click “Launch”. Ignore a warning about “Improve your instances’ security”.

Create a key pair and download it. You’ll use this later to SSH into in the instance. Finally, click “Launch Instances”.

Click “View Instances”.

EC2 instance status

Navigate to the AWS Management Console. Select the “Services” dropdown menu, then “EC2”.

Click “Instances”.

Select one of the instances and view its status and Public DNS.

SSH to EC2 instance

In this section, we’ll configure SSH access using the eecs485deploy.pem file generated in the Start instance section.

Copy or move your instance’s SSH key to your project directory and set the permissions to read-only. Notice the single r when showing the permissions with ls -l.

$ pwd
$ mv ~/Downloads/eecs485deploy.pem .
$ chmod 400 eecs485deploy.pem
$ ls -l eecs485deploy.pem 
-r-------- 1 awdeorio staff 1.7K May 12 15:55 eecs485deploy.pem

SSH into your instance. Replace ec2-54-86-86-246.compute-1.amazonaws.com with your Public DNS from the Instance status section.

$ ssh -i eecs485deploy.pem ubuntu@ec2-54-86-86-246.compute-1.amazonaws.com

Pitfall Make sure your instance is running. See Instance status.

Install Nginx

We’ll use Nginx to listen for HTTP requests and then proxy them (“pass them on”) to your Flask app Python code, which will run in a gunicorn server.

First, SSH into your instance.

Install Nginx.

$ sudo apt-get update
$ sudo apt-get install nginx

Verify that it’s working by navigating to your Public DNS in a web brower.

Configure Nginx

Optionally install your favorite editor make it the default for this login session by setting the $EDITOR environment variable. Vim should be installed already.

$ sudo apt-get install emacs-nox
$ export EDITOR=emacs

If you do not have a favorite text editor, there are a few options.

  1. Nano is a beginner friendly, command line text editor with a GUI. It is installed by default on most Linux distributions. This is the recommended option and its usage is described below.
  2. Check out the Emacs Tutorial. Emacs is a highly configurable and advanced text editor with a steep learning curve. Once learned, however, it can be very powerful.

For the rest of this tutorial, we’ll assume you’re using Nano to edit files. First, check if nano is already installed.

$ which nano

If no installation path is displayed, you do not have nano installed. Install nano as follows.

$ sudo apt update
$ sudo apt install nano
$ which nano

Before editing any files, it may be helpful to view the available keyboard shortcuts in nano. To do so, press Ctrl + G. Here are some particularly relevant keystrokes:

Open /etc/nginx/sites-available/default to edit the file.

$ sudo nano /etc/nginx/sites-available/default

Modify /etc/nginx/sites-available/default. Replace the entire default server settings with the settings specified below. Save and exit.

server {
  listen 80;
  server_name <Public DNS (IPv4)>;

  location / {
    proxy_pass http://localhost:8000;

Open /etc/nginx/nginx.conf to edit the file.

$ sudo nano /etc/nginx/nginx.conf

Modify one line in /etc/nginx/nginx.conf, uncomment the line saying, server_names_hash_bucket_size and change its value to 128. Save and exit.

server_names_hash_bucket_size 128;

Restart the nginx server.

$ sudo systemctl restart nginx

Install Flask app

Pitfall If you are just getting started on your project, stop here and return when you are ready to run your flask app.

Use these install instructions for every project.

First, SSH into your instance.

Make sure you have Python virtual environment tools installed.

$ sudo apt-get install python3 python3-venv sqlite3

Clone your web app source code.

@ip-172-31-81-21:~$ pwd
$ git clone https://gitlab.eecs.umich.edu/your/project/repo
$ cd p2-insta485-serverside/

Note: if your GitHub/Gitlab account has two-factor authentication enabled, you may need to create an SSH key on the AWS instance.

Install back end

Create a virtual environment. Your Python version may vary, but Python 3.8+ is required.

$ pwd
$ python3 --version
Python 3.8.2
$ python3 -m venv env

Activate the virtual environment and install python package dependencies, including your project. Also, install gunicorn, which will be used for server deployment.

$ source env/bin/activate
$ pip install --upgrade pip setuptools wheel
$ pip install -r requirements.txt
$ pip install -e .
$ pip install gunicorn

Initialize database.

$ ./bin/insta485db create

Install front end

Skip this subsection if you’re not using JavaScript.

Install node and npm. Your Node version may vary, but Node v8+ is required.

$ sudo apt-get install nodejs npm
$ node --version

Install JavaScript packages, including obfuscator.

$ npm install .
$ npx webpack
$ npm install javascript-obfuscator

Compile and obfuscate JavaScript.

$ npx webpack
$ npx javascript-obfuscator insta485/static/js/bundle.js --reserved-strings '\s*'
$ mv insta485/static/js/bundle-obfuscated.js insta485/static/js/bundle.js

Note: The –reserved-strings flag sets the config for the obfuscator to “reserve” the space character in your JS code. This will prevent space character from being replaced with \x20.

Run server

Make sure that no old gunicorn processes are running.

$ pkill -f gunicorn
$ pgrep -af gunicorn
# no process should appear here!

Start gunicorn in the background (-D for daemon mode).

$ gunicorn -b localhost:8000 -w 2 -D insta485:app
$ pgrep -af gunicorn
17498 /home/ubuntu/p2-insta485-serverside/env/bin/python3 /home/ubuntu/p2-insta485-serverside/env/bin/gunicorn -b localhost:8000 -w 2 -D insta485:app
17500 /home/ubuntu/p2-insta485-serverside/env/bin/python3 /home/ubuntu/p2-insta485-serverside/env/bin/gunicorn -b localhost:8000 -w 2 -D insta485:app
17501 /home/ubuntu/p2-insta485-serverside/env/bin/python3 /home/ubuntu/p2-insta485-serverside/env/bin/gunicorn -b localhost:8000 -w 2 -D insta485:app

Pitfall If you’re having trouble, trying running Gunicorn in the foreground with debug messages enabled.

$ pkill -f gunicorn
$ gunicorn -b localhost:8000 -w 2 insta485:app --log-level debug

Browse to your Public DNS name (or refresh) and you should see your web app.

After successfully deploying, go back to the project spec and follow the deployment submission instructions. After submitting, be sure to kill your gunicorn process using pkill.

You may exit the ssh session with the exit command.

$ exit

Test app

Your web app should now work from outside AWS. Using a browser on your development machine, navigate to your app. For example, the instructor solution URL was http://ec2-54-86-86-246.compute-1.amazonaws.com. Replace ec2-54-86-86-246.compute-1.amazonaws.com with your Public DNS from the Instance status section.

Stop EC2 instance

To avoid using your AWS credits, shut down your instance when you’re done with it.

Check the status of your instance.

Right click on your instance -> “Instance State” -> “Stop”.

You should now see that your instance is stopped.


This document is licensed under a Creative Commons Attribution-NonCommercial 4.0 License. You’re free to copy and share this document, but not to sell it. You may not share source code provided with this document.