Deploy a Flask App with Custom VPC
Build a custom VPC from scratch and deploy a Flask app on EC2.
Introduction
30 Second Summary
Every time you visit a website, your request passes through multiple networking layers before it ever reaches the server running the code. Most developers deploy apps without understanding what happens between the browser and their application.
In this project, you will build and deploy a Python Flask web application on AWS, manually configuring every networking layer so you understand exactly how internet traffic reaches your server.
What You'll Build
A publicly accessible Flask web application running on an EC2 instance inside a custom VPC you design from the ground up.
By the end of this project, you'll have:
- A custom VPC with a public subnet, internet gateway, and route table you configured from scratch.
- A Flask application running as a production-ready systemd service on an EC2 instance, accessible from the internet.
- Hands-on understanding of how internet traffic flows from a browser through cloud networking layers to reach your application.
- Secret Mission: Add a private subnet with an RDS PostgreSQL database connected to your Flask app.
Are there any prerequisites?
You should be comfortable with Python and basic command line usage. Some familiarity with HTML/CSS is helpful but not required.
You will need a Linux environment (or WSL on Windows) and VS Code installed. You will create a new AWS account during the project, which comes with $100 in free credits valid for 6 months.
Set Up AWS Account and CLI
Before you can build anything on AWS, you need an account and a way to interact with AWS services from your terminal.
In this step, you will create an AWS account, set up a dedicated user for programmatic access, and install the AWS CLI so you can manage cloud resources without leaving the command line.
In this step, get ready to:
- Create an AWS account with free credits.
- Create an IAM user with an access key for CLI use.
- Install and configure AWS CLI v2 on Linux.
Create an AWS account
AWS gives new accounts $100 in free credits, valid for 6 months. This covers everything you will build in this project at no cost.
- Open your browser and navigate to aws.amazon.com.
- Click Create an AWS Account in the top right corner.
- Enter your email address and choose an account name.
- Complete the sign-up process including email verification, payment method, and identity verification.
- Select the Basic support - Free plan when prompted.
- Once sign-up is complete, sign in to the AWS Management Console.
What is IAM?
IAM stands for Identity and Access Management. It lets you create separate users with specific permissions instead of using your root (main) account for everything.
Your root account has unrestricted access to everything. Creating an IAM user with only the permissions you need is a security best practice.
Create an IAM user with an access key
You need a dedicated IAM user to authenticate the AWS CLI. This user will have administrator permissions so you can create all the resources in this project.
- In the AWS Console, search for IAM in the top search bar and open the IAM service.
- In the left navigation pane, click Users.
- Click Create user.
- Enter a user name (for example, flask-cli-user) and click Next.
- On the Set permissions page, select Attach policies directly.
- Search for AdministratorAccess and select the checkbox next to it.
- Click Next, then click Create user.
Now generate an access key for this user.
- Click on your new user's name to open the user details page.
- Select the Security credentials tab.
- In the Access keys section, click Create access key.
- Select Command Line Interface (CLI) as the use case.
- Check the confirmation box at the bottom, then click Next.
- Click Create access key.
- Click Download .csv file to save your access key ID and secret access key. Store this file in a safe location.
- Click Done.
Save your credentials now
This is your only opportunity to view or download the secret access key. If you lose it, you will need to delete the access key and create a new one.
Install and configure AWS CLI v2
The AWS CLI lets you manage AWS resources directly from your terminal. You will use it throughout this project to verify your infrastructure.
- Check if AWS CLI is already installed by running this command:
aws --version
✔️ I see aws-cli/2.x.x
Your output starts with aws-cli/2. followed by a version number. AWS CLI v2 is already installed and you can skip to the configuration section below.
ⓧ I see aws-cli/1.x.x
You have an older version of the AWS CLI installed. Install version 2 alongside it by running these commands. The v2 installer places the binary at /usr/local/bin/aws which takes precedence over the v1 path.
- Download and install AWS CLI v2 by running these commands:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
- Close and reopen your terminal, then verify the version:
aws --version
You should now see a version starting with aws-cli/2. in the output.
ⓧ Command not found
AWS CLI is not installed yet. Install it now.
- Download and install AWS CLI v2 by running these commands:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
- Close and reopen your terminal, then verify the installation:
aws --version
You should see output starting with aws-cli/2. followed by a version number.
Still seeing command not found?
If unzip is not found, install it first with your package manager (for example, sudo apt install unzip on Ubuntu or sudo dnf install unzip on Fedora).
If aws is still not found after installing, make sure /usr/local/bin is in your PATH. Try opening a completely new terminal window.
help me fix my AWS CLI installation on Linux
Now configure the CLI with your IAM credentials. The aws configure command will prompt you for four values one at a time.
- Start the configuration wizard by running:
aws configure
What does aws configure ask for?
The command prompts you for four pieces of information, stored in ~/.aws/credentials and ~/.aws/config:
- Access Key ID: paste the access key ID from your downloaded CSV.
- Secret Access Key: paste the secret access key from your CSV.
- Default region name: enter us-east-1.
- Default output format: enter json.
- Enter your access key ID when prompted (from the CSV you downloaded).
- Enter your secret access key when prompted.
- Enter us-east-1 for the default region.
- Enter json for the default output format.
Verify your configuration is working by running this command:
- Test your credentials by running:
aws sts get-caller-identity
You should see JSON output containing your Account ID, User ID, and ARN. This confirms your CLI is properly authenticated.
Seeing an access denied or credentials error?
Double-check that you pasted the correct access key ID and secret key from your CSV. They are case-sensitive and must not include extra spaces.
If you accidentally closed the access key page without saving, go back to IAM, select your user, open the Security credentials tab, delete the old key, and create a new one.
help me fix my AWS CLI credentials configuration
Your AWS account is live and your CLI can talk to it. Next up, you will build a custom VPC from scratch and configure every networking layer yourself.
Build a Custom VPC with Networking
Your AWS account is configured and your CLI is ready to go. Now it's time to build the network that your server will live inside.
Every EC2 instance lives inside a VPC (Virtual Private Cloud). AWS gives you a default VPC, but building one from scratch teaches you exactly how internet traffic reaches your server.
In this step, get ready to:
- Create a VPC and public subnet with your own IP address range.
- Set up an internet gateway and route table so traffic can flow in and out.
- Configure a security group to control which ports are accessible.
Create a VPC and public subnet
A VPC is your own isolated section of the AWS cloud. You define the IP address range, and then carve out subnets within it for your resources.
- In the AWS Management Console, type VPC in the top search bar and select VPC from the results.
- On the VPC dashboard, click Create VPC.
- For Resources to create, choose VPC only.
- For Name tag, enter flask-vpc.
- For IPv4 CIDR block, select IPv4 CIDR manual input and enter 10.0.0.0/16.
- Leave all other settings as default and click Create VPC.
What is a VPC?
A VPC is a logically isolated virtual network you define in AWS. The 10.0.0.0/16 CIDR block gives you up to 65,536 private IP addresses to assign to resources inside this network.
Think of it as reserving an entire office building. The CIDR block is the building's address range, and subnets are individual floors you'll carve out next.
You should see a success banner confirming your VPC was created. Now create a subnet inside it.
- In the left navigation pane, click Subnets.
- Click Create subnet.
- For VPC ID, select flask-vpc from the dropdown.
- For Subnet name, enter flask-public-subnet.
- For Availability Zone, select us-east-1a.
- For IPv4 subnet CIDR block, enter 10.0.1.0/24.
- Click Create subnet.
One more setting is needed. By default, instances launched in a custom subnet do not receive a public IP address. You need to enable this so your server will be reachable from the internet.
- Select the flask-public-subnet you just created.
- Click Actions, then Edit subnet settings.
- Check the box labeled Enable auto-assign public IPv4 address.
- Click Save.
Why build from scratch instead of using the default VPC?
AWS gives every account a default VPC with everything pre-configured. Building your own teaches you what each component does and how they connect.
This is the mental model employers expect from cloud engineers. Understanding each layer means you can debug networking issues and design secure architectures from the ground up.
Don't see flask-vpc in the subnet dropdown?
Make sure you are in the us-east-1 region. Check the region selector in the top-right corner of the console. If you created the VPC in a different region, it will not appear here.
help me find my VPC in the AWS console
Set up an internet gateway and route table
Your VPC is isolated from the internet by default. An internet gateway is the door that allows traffic to flow between your VPC and the public internet.
- In the left navigation pane, click Internet gateways.
- Click Create internet gateway.
- For Name tag, enter flask-igw.
- Click Create internet gateway.
The gateway is created but not yet connected to your VPC. You need to attach it.
- On the internet gateway detail page, click Actions, then Attach to VPC.
- Select flask-vpc from the dropdown.
- Click Attach internet gateway.
What is an internet gateway?
An internet gateway is a horizontally scaled, redundant, and highly available VPC component that allows communication between your VPC and the internet. Without it, nothing inside your VPC can send or receive traffic from the outside world.
Now you need a route table that tells your subnet to send internet-bound traffic through the gateway.
- In the left navigation pane, click Route tables.
- Click Create route table.
- For Name, enter flask-public-rt.
- For VPC, select flask-vpc.
- Click Create route table.
The route table exists but has no route to the internet yet. Add one now.
- On the route table detail page, click the Routes tab.
- Click Edit routes.
- Click Add route.
- For Destination, enter 0.0.0.0/0.
- For Target, select Internet Gateway, then select flask-igw.
- Click Save changes.
Finally, associate this route table with your public subnet.
- Click the Subnet associations tab.
- Click Edit subnet associations.
- Check the box next to flask-public-subnet.
- Click Save associations.
How does the route table work?
The route 0.0.0.0/0 means "all traffic not destined for the local VPC." By pointing this route at the internet gateway, you are telling your subnet to send all internet-bound traffic through the gateway.
This is what makes a subnet "public." A subnet associated with a route table that has a route to an internet gateway is a public subnet.
Don't see flask-igw in the target dropdown?
Make sure you attached the internet gateway to flask-vpc in the previous step. An unattached gateway will not appear as a target option. Go back to Internet gateways and verify the State column shows Attached.
help me attach my internet gateway to my VPC
Create a security group for SSH and HTTP
A security group acts as a virtual firewall for your instance. It controls which traffic is allowed to reach your server and which is blocked.
- In the left navigation pane, click Security groups.
- Click Create security group.
- For Security group name, enter flask-sg.
- For Description, enter Allow SSH and HTTP for Flask app.
- For VPC, remove the default VPC and select flask-vpc.
Now add two inbound rules. The first allows you to connect via SSH. The second allows web browsers to reach your app via HTTP.
- In the Inbound rules section, click Add rule.
- For Type, select SSH.
- For Source, select My IP. This restricts SSH access to your current IP address only.
- Click Add rule again to add a second rule.
- For Type, select HTTP.
- For Source, select Anywhere-IPv4 (0.0.0.0/0). This allows anyone on the internet to view your web app.
- Leave the Outbound rules section as default (all traffic allowed).
- Click Create security group.
Why restrict SSH to My IP?
SSH gives full command-line access to your server. Leaving port 22 open to 0.0.0.0/0 means anyone on the internet can attempt to connect.
Restricting it to your IP address is a security best practice. If your IP changes later (like switching Wi-Fi networks), you can update the rule in the security group.
What is a security group?
Security groups are stateful. This means if you allow inbound traffic on a port, the response traffic is automatically allowed outbound, regardless of outbound rules.
You can think of each rule as a bouncer checking a list. Port 22 (SSH) lets you in to manage the server. Port 80 (HTTP) lets web visitors in to see your app.
Can't find flask-vpc when creating the security group?
The VPC dropdown defaults to your account's default VPC. You need to clear the default selection first, then search for or select flask-vpc. If it still does not appear, confirm you are in the us-east-1 region.
help me select the correct VPC for my security group
Your network is fully wired up. Next, you will launch an EC2 instance inside this VPC and connect to it over SSH.
Launch and Configure the EC2 Instance
Your custom VPC is fully wired up with an internet gateway, route table, and security group. You have a private network in the cloud, but nothing is running inside it yet.
In this step, you'll launch an EC2 instance inside your VPC, connect to it over SSH, and prepare it to run a Python web application.
In this step, get ready to:
- Launch a t3.micro EC2 instance in your custom VPC.
- SSH into the instance and install system packages.
- Set up a Python virtual environment with Flask and Gunicorn.
Launch a t3.micro instance in your VPC
An EC2 instance is a virtual server running in AWS. You will launch one inside the VPC you built so that traffic flows through the networking layers you configured.
- In the AWS Console, make sure your region is set to US East (N. Virginia) us-east-1 in the top navigation bar.
- Navigate to the EC2 service using the search bar at the top.
- Click Launch instance from the EC2 dashboard.
What is an EC2 instance?
EC2 (Elastic Compute Cloud) is AWS's virtual server service. Each instance is a machine you rent by the hour. A t3.micro is a small instance type covered by the AWS Free Tier credits.
- Under Name and tags, enter flask-server in the Name field.
- Under Application and OS Images (Amazon Machine Image), select Quick Start.
- Choose Amazon Linux and confirm the AMI is set to Amazon Linux 2023 AMI.
- Under Instance type, select t3.micro from the dropdown.
What is an AMI?
An AMI (Amazon Machine Image) is a template that contains the operating system and pre-installed software for your instance. Amazon Linux 2023 is AWS's own Linux distribution, optimized for cloud workloads.
- Under Key pair (login), click Create new key pair.
- In the Name field, enter flask-key.
- For Key pair type, leave it as RSA.
- For Private key file format, select .pem.
- Click Create key pair.
Your browser automatically downloads a file called flask-key.pem. This is your private key for SSH access.
Save your key pair safely
AWS only lets you download this file once. If you lose it, you cannot retrieve it. Save it somewhere you can find it later.
- Under Network settings, click Edit.
- In the VPC dropdown, select flask-vpc.
- In the Subnet dropdown, select the public subnet you created (in us-east-1a).
- Under Firewall (security groups), select Select existing security group.
- In the Common security groups dropdown, choose flask-sg.
- Leave all other settings as default.
- In the Summary panel on the right, click Launch instance.
A success banner confirms the launch. Click the instance ID link to open the Instances page.
- Wait until the Instance state column shows Running.
- Note the Public IPv4 address shown in the instance details. You will need this for SSH.
Instance stuck in Pending?
It can take 1-2 minutes for an instance to finish launching. If the state does not change to Running after a few minutes, check that you selected the correct subnet and security group.
Why isn't my EC2 instance starting?
Connect to your instance and install system packages
SSH (Secure Shell) is a protocol that lets you securely connect to a remote server's command line. You will use your key pair to authenticate.
- Open a terminal on your local machine.
- Navigate to the directory where flask-key.pem was downloaded by running this command:
cd ~/Downloads
- Set the correct file permissions on your key by running this command:
chmod 400 flask-key.pem
Why set permissions to 400?
SSH requires that your private key file is only readable by you. The chmod 400 command sets the file to read-only for the owner and no access for anyone else. Without this, SSH refuses to connect.
- Connect to your EC2 instance by running this command:
ssh -i flask-key.pem ec2-user@[[EC2_PUBLIC_IP="your EC2 instance public IPv4 address"]]
What does this command do?
- ssh starts an SSH connection to the remote server.
- -i flask-key.pem tells SSH to use your private key file for authentication.
- ec2-user is the default username on Amazon Linux instances.
- The IP address is the public IPv4 you noted from the EC2 console.
When prompted to confirm the host fingerprint, type yes and press Enter.
You should see a command prompt starting with [ec2-user@, confirming you are connected to the remote instance.
Connection timed out or refused?
- Double-check the public IP address matches what the EC2 console shows.
- Confirm your security group flask-sg has an inbound rule allowing SSH (port 22) from your IP address.
- Make sure you ran chmod 400 flask-key.pem before connecting. Without it, SSH shows a "permissions too open" error.
Help me troubleshoot my SSH connection to EC2.
- Update the system packages by running this command:
sudo dnf update -y
This pulls the latest security patches and package lists. It may take a minute to complete.
- Install Python 3 and pip by running this command:
sudo dnf install python3-pip -y
Why dnf instead of apt?
Amazon Linux 2023 uses dnf as its package manager (similar to Fedora and RHEL). Ubuntu-based systems use apt. The package manager depends on the Linux distribution, not on AWS.
Set up a Python virtual environment with Flask and Gunicorn
A virtual environment isolates your project's Python packages from the system Python. This prevents version conflicts and keeps your server clean.
- Create your project directory and virtual environment by running these commands:
mkdir /home/ec2-user/flask-app
python3 -m venv /home/ec2-user/flask-app/venv
What does python3 -m venv do?
This creates a self-contained Python environment at /home/ec2-user/flask-app/venv. When activated, any packages you install go into this folder instead of the global system Python.
- Activate the virtual environment by running this command:
source /home/ec2-user/flask-app/venv/bin/activate
Your terminal prompt now starts with (venv), confirming the virtual environment is active.
- Install Flask and Gunicorn by running this command:
pip install flask==3.1.3 gunicorn==23.0.0
What are Flask and Gunicorn?
Flask is a lightweight Python web framework for building web applications. Gunicorn is a production-grade WSGI server that handles multiple requests concurrently. Together, they let you serve a Flask app reliably to real users.
You are pinning specific versions (3.1.3 and 23.0.0) to ensure your deployment is reproducible.
- Verify Flask installed correctly by running this command:
python3 -c "import flask; print(flask.__version__)"
You should see 3.1.3 printed in your terminal.
- Verify Gunicorn installed correctly by running this command:
gunicorn --version
You should see gunicorn (version 23.0.0) in the output.
Seeing "command not found" for gunicorn?
- Make sure your virtual environment is active. Your prompt should start with (venv).
- If you accidentally closed the SSH session, reconnect and reactivate with source /home/ec2-user/flask-app/venv/bin/activate.
Help me fix my Flask or Gunicorn installation.
Your EC2 instance is running a fully configured Python environment. Next up, you'll write your Flask application and deploy it as a production-ready service.
Deploy Flask as a Production systemd Service
Your EC2 instance is configured and ready to serve traffic. The networking layers (VPC, subnet, internet gateway, route table, security group) are all in place.
Now you need a running application behind all that infrastructure. In this step, you will create a Flask app, then configure Linux to run it as a persistent systemd service using Gunicorn as the production WSGI server.
In this step, get ready to:
- Create a Flask application that returns an HTML page with the server hostname.
- Write a systemd service file to run Gunicorn as a background service.
- Start the service and verify your app is live on the internet.
Create the Flask application
Your Flask app is a single Python file that defines a web route. When someone visits your server's public IP, Flask returns an HTML page showing the hostname and a success message.
- In your SSH session, make sure you are in the project directory by running:
cd /home/ec2-user/flask-app
- Create the application file by opening it in nano:
nano app.py
- Paste the following code into the nano editor:
import socket
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
hostname = socket.gethostname()
return f"""<!DOCTYPE html>
<html>
<head>
<title>Flask on AWS</title>
<style>
body {{
font-family: sans-serif;
max-width: 600px;
margin: 80px auto;
text-align: center;
}}
.success {{
color: #16a34a;
font-size: 1.5rem;
margin-bottom: 1rem;
}}
.info {{
color: #6b7280;
font-size: 0.9rem;
}}
</style>
</head>
<body>
<h1>Hello from AWS!</h1>
<p class="success">Your Flask app is live.</p>
<p class="info">Hostname: {hostname}</p>
<p class="info">Served by Gunicorn behind a custom VPC.</p>
</body>
</html>"""
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
- Save the file by pressing Ctrl+O, then Enter, then Ctrl+X to exit nano.
What does this code do?
The socket.gethostname() call grabs the server's internal hostname so you can confirm which machine is responding. The @app.route("/") decorator tells Flask to run the home() function whenever someone visits the root URL.
The double curly braces {{ and }} are Python f-string syntax for literal curly braces in the CSS. Without doubling them, Python would try to interpret the CSS braces as variable placeholders.
- Confirm the file was created by running:
ls app.py
You should see app.py printed in the output.
Don't see app.py?
Make sure you ran cd /home/ec2-user/flask-app first. If you saved the file in the wrong directory, run find / -name app.py 2>/dev/null to locate it and move it to the correct path.
Help me find my app.py file
Create the systemd service file
A systemd service file tells Linux how to run your application as a background process. It defines what command to execute, which user to run as, and what to do if the process crashes.
- Create the service file by running:
sudo nano /etc/systemd/system/flask-app.service
- Paste the following configuration into the editor:
[Unit]
Description=Flask App served by Gunicorn
After=network.target
[Service]
User=ec2-user
Group=ec2-user
WorkingDirectory=/home/ec2-user/flask-app
ExecStart=/home/ec2-user/flask-app/venv/bin/gunicorn app:app --bind 0.0.0.0:80 --workers 2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
- Save the file by pressing Ctrl+O, then Enter, then Ctrl+X to exit nano.
What does this service file do?
The [Unit] section names the service and tells systemd to start it only after the network is available. The [Service] section is where the action happens.
- ExecStart runs Gunicorn from the virtual environment, binding to port 80 on all network interfaces with 2 worker processes.
- app:app tells Gunicorn to import the app object from the app.py module.
- Restart=on-failure means systemd will automatically restart the process if it crashes.
- The [Install] section ensures the service starts automatically on boot.
Why Gunicorn instead of Flask's built-in server?
Flask's built-in development server handles one request at a time and is not designed for production traffic. Gunicorn is a WSGI server that spawns multiple worker processes to handle concurrent requests reliably.
- Confirm the service file was created by running:
ls /etc/systemd/system/flask-app.service
You should see the file path printed in the output.
Permission denied when saving?
The /etc/systemd/system/ directory requires root access. Make sure you included sudo before the nano command.
Help me fix the permission error
Enable and start the service
With both files in place, you can now tell systemd to pick up the new service, enable it to start on boot, and launch it immediately.
- Reload systemd to register the new service file by running:
sudo systemctl daemon-reload
- Enable the service so it starts automatically on boot by running:
sudo systemctl enable flask-app
- Start the service now by running:
sudo systemctl start flask-app
- Check that the service is running by running:
sudo systemctl status flask-app
You should see active (running) in green text in the output. Press q to exit the status view.
What do these commands do?
- daemon-reload tells systemd to re-scan its service files and pick up any new or changed ones.
- enable creates a symlink so the service starts automatically on every boot.
- start launches the service right now without waiting for a reboot.
- status shows whether the service is running, how long it has been up, and any recent log output.
- Now test your app from the internet. Open a browser and navigate to:
http://[[EC2_PUBLIC_IP="your EC2 instance public IPv4 address"]]
You should see a page that says "Hello from AWS!" with a green "Your Flask app is live." message below it.
- You can also verify from your local terminal (not the SSH session) by running:
curl http://[[EC2_PUBLIC_IP="your EC2 instance public IPv4 address"]]
You should see the raw HTML of your Flask application printed in the terminal.
Service not starting or page not loading?
If systemctl status shows failed, check the logs for details by running sudo journalctl -u flask-app -n 20 in your SSH session.
If you see an error related to binding port 80, this is because ports below 1024 require special privileges. Add AmbientCapabilities=CAP_NET_BIND_SERVICE on a new line under the Group=ec2-user line in your service file. Then re-run sudo systemctl daemon-reload and sudo systemctl restart flask-app.
If the page does not load in your browser, confirm your security group flask-sg has an inbound rule allowing HTTP (port 80) from 0.0.0.0/0.
Help me troubleshoot my Flask service
✔️ Awesome, I've got everything!
Great. Double check you have saved both files and that your service shows active (running).
ⓧ I'd like to double check the full code
Here is the complete app.py file:
import socket
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
hostname = socket.gethostname()
return f"""<!DOCTYPE html>
<html>
<head>
<title>Flask on AWS</title>
<style>
body {{
font-family: sans-serif;
max-width: 600px;
margin: 80px auto;
text-align: center;
}}
.success {{
color: #16a34a;
font-size: 1.5rem;
margin-bottom: 1rem;
}}
.info {{
color: #6b7280;
font-size: 0.9rem;
}}
</style>
</head>
<body>
<h1>Hello from AWS!</h1>
<p class="success">Your Flask app is live.</p>
<p class="info">Hostname: {hostname}</p>
<p class="info">Served by Gunicorn behind a custom VPC.</p>
</body>
</html>"""
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
Here is the complete flask-app.service file:
[Unit]
Description=Flask App served by Gunicorn
After=network.target
[Service]
User=ec2-user
Group=ec2-user
WorkingDirectory=/home/ec2-user/flask-app
ExecStart=/home/ec2-user/flask-app/venv/bin/gunicorn app:app --bind 0.0.0.0:80 --workers 2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Your Flask app is live and serving traffic from the internet. In the Secret Mission, you will extend this setup with a private subnet and a PostgreSQL database.
Secret mission
Add a Private Subnet with RDS PostgreSQL
Your Flask app is live, but it has no database. In production, databases live in private subnets where they cannot be reached directly from the internet. Can you extend your VPC with private subnets, launch an RDS PostgreSQL instance, and wire it into your Flask app?
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. Your AWS resources are covered by the $100 sign-up credits for up to 6 months, but leaving them running consumes those credits faster.
Cost warning
While all resources in this project are covered by your AWS Free Tier credits, the EC2 instance and RDS instance (if you completed the Secret Mission) consume credits continuously while running. If you are not actively using them, stop or delete them to preserve credits for other projects.
Resources you used:
- 1 VPC (flask-vpc) with 1 public subnet and 2 private subnets.
- 1 internet gateway (flask-igw), 1 route table (flask-public-rt), 2 security groups (flask-sg, rds-sg).
- 1 EC2 instance (t3.micro) running Amazon Linux 2023.
- 1 RDS PostgreSQL instance (db.t3.micro) in private subnets (Secret Mission).
- 1 DB subnet group containing both private subnets.
- 1 key pair file (flask-key.pem) stored locally.
Keep everything running
No action needed. Choose this if you are still actively building or plan to continue experimenting with your VPC and Flask deployment.
- Your Flask app stays accessible at the EC2 public IP.
- Monitor your credit usage in the AWS Billing dashboard to ensure you stay within the $100 credit allowance.
- Your local flask-key.pem file remains on your machine for future SSH access.
Pause - I'll come back to this later
Stop the EC2 instance to reduce credit usage while keeping all configuration intact. Note that the public IP address will change when you restart.
- Open the EC2 console at https://console.aws.amazon.com/ec2/.
- In the navigation pane, choose Instances.
- Select your EC2 instance, then choose Instance state, then Stop instance.
- If you completed the Secret Mission, you can also stop the RDS instance. Open the RDS console, select your database, choose Actions, then Stop temporarily.
- Keep your flask-key.pem file safe. You will need it to SSH back in after restarting.
Delete - I don't want to use this again
Remove all project resources to ensure zero ongoing credit usage. Follow these steps in order because some resources depend on others.
- Delete the RDS instance (if you completed the Secret Mission):
- Open the Amazon RDS console at https://console.aws.amazon.com/rds/.
- In the navigation pane, choose Databases.
- Select your PostgreSQL DB instance, then choose Actions, then Delete.
- Uncheck Create final snapshot? (not needed for a learning project).
- Enter delete me in the confirmation box, then choose Delete.
- Wait for the RDS instance to finish deleting (this can take several minutes).
- Delete the DB subnet group (after RDS is deleted):
- In the RDS console navigation pane, choose Subnet groups.
- Select the DB subnet group you created, then choose Delete.
- Choose Delete in the confirmation window.
- Terminate the EC2 instance:
- Open the Amazon EC2 console at https://console.aws.amazon.com/ec2/.
- In the navigation pane, choose Instances.
- Select your instance, then choose Instance state, then Terminate instance.
- Choose Terminate when prompted for confirmation.
- Delete the VPC (this also removes subnets, route tables, internet gateway, and security groups):
- Open the Amazon VPC console at https://console.aws.amazon.com/vpc/.
- In the navigation pane, choose Your VPCs.
- Select flask-vpc, then choose Actions, then Delete VPC.
- Review the list of associated resources that will also be deleted (subnets, route tables, internet gateways, security groups).
- Enter delete in the confirmation box, then choose Delete.
- Delete your local key pair file:
- Remove the flask-key.pem file from your local machine by running:
rm flask-key.pem
- Optionally delete the EC2 key pair from AWS. In the EC2 console, choose Key Pairs in the navigation pane, select flask-key, then choose Actions, then Delete.
That's a Wrap!
That's a Wrap!
Nice work! You just deployed a Python Flask application on infrastructure you built from the ground up. Every networking layer between the browser and your app is something you configured yourself.
You've learned how to:
- Build a custom VPC from scratch with a public subnet, internet gateway, route table, and security groups.
- Launch an EC2 instance, SSH into it, and configure a Linux server from the command line.
- Deploy a Flask app as a production-ready systemd service behind Gunicorn, accessible from the internet.
- Secret Mission: Added a private subnet with an RDS PostgreSQL database connected to your Flask app, implementing proper multi-tier architecture.
Ready to quiz yourself?