How to deploy a Python app to AWS EC2 via SSH

Henry Coder
hellocode
Published in
4 min readNov 13, 2023

After you built up a Python app locally, it is time for you to upload to cloud, which is called deploy. Here are the steps to deploy your python code on AWS EC2.

Usually, there is a venv in your local python file. You don’t need to upload it to cloud. Instead, you can extract the venv to requirments.txt, upload it to cloud and then rebuild the venv in the cloud by this file.

by the way, you can exclude any files that you don’t want to upload in your python directory:

rsync -av --exclude='.git/' --exclude='.DS_Store' --exclude='venv_winjob' --exclude='__pycache__' --exclude='.gitattributes' --exclude='.gitignore' -e "ssh -i /Users/henrywu/MyDrive/98_Products/P01-Project-Alpha/03-keys/AWS_EC2_key_pair_helloworld.pem" /Users/henrywu/MyDrive/99_Coding/01-Github/winjob/ ec2-user@ec2-44-239-28-119.us-west-2.compute.amazonaws.com:/home/ec2-user/firstweb/
rsync -av \
--exclude='.git/' \
--exclude='.DS_Store' \
--exclude='venv_winjob' \
--exclude='__pycache__' \
--exclude='.gitattributes' \
--exclude='.gitignore' \
-e "ssh -i /Users/henrywu/MyDrive/98_Products/config/AWS_EC2_key_pair_winjob.pem" \
/Users/henrywu/MyDrive/99_Coding/01-Github/winjob/ \
ec2-user@ec2-54-185-22-17.us-west-2.compute.amazonaws.com:/home/ec2-user/winjob/

Note: please pay attention to the prompt of the terminal. when upload code from local to AWS, the prompt should be local computer. When set up the environment in AWS, we should login SSH, and the prompt should be user in AWS.

You can use ls command to check if the upload is successful:

After login SSH, we can set up the environment on EC2.

sudo nano /etc/nginx/conf.d/firstweb.conf
# HTTP server block
server {
listen 80;
listen [::]:80;
server_name winjob.ai www.winjob.ai;

location ^~ /.well-known/acme-challenge/ {
root /home/ec2-user/firstweb; # This directory must be the root of your web content for the ACME challenge
allow all; # No restrictions for accessing this location
try_files $uri =404; # Serve files if present, or error
}

location / {
# Ensure that requests to the application root URL are proxied to Gunicorn
proxy_pass http://localhost:5000;
# Include proxy headers for forwarding the original host, IP, and protocol used
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Define the max body size for client requests, if needed
client_max_body_size 10M; # Adjust the size as necessary
}

# ... rest of your server block ...
}
[Unit]
Description=Gunicorn instance to serve firstweb
After=network.target

[Service]
User=ec2-user
Group=nginx
WorkingDirectory=/home/ec2-user/firstweb
Environment="PATH=/home/ec2-user/firstweb/venv_winjob/bin"
ExecStart=/home/ec2-user/firstweb/venv_winjob/bin/gunicorn --workers 3 --bind 0.0.0.0:5000 app:app

[Install]
WantedBy=multi-user.target

Restart=on-failure
RestartSec=5s
sudo systemctl reload nginx
sudo systemctl restart nginx
sudo systemctl daemon-reload
sudo systemctl restart firstweb.service

run the code

first, activate the venv, then run the code:

gunicorn --workers 3 --bind 0.0.0.0:5000 app:app

Basically, there are two config files the most important:

nginx

sudo nano /etc/nginx/conf.d/firstweb.conf
# HTTP server block
server {
listen 80;
listen [::]:80;
server_name winjob.ai www.winjob.ai;

location ^~ /.well-known/acme-challenge/ {
root /home/ec2-user/firstweb; # This directory must be the root of your web content for the ACME challenge
allow all; # No restrictions for accessing this location
try_files $uri =404; # Serve files if present, or error
}

location / {
# Ensure that requests to the application root URL are proxied to Gunicorn
proxy_pass http://localhost:5000;
# Include proxy headers for forwarding the original host, IP, and protocol used
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Define the max body size for client requests, if needed
client_max_body_size 10M; # Adjust the size as necessary
}

# ... rest of your server block ...
}

gunicorn

sudo nano /etc/systemd/system/firstweb.service
[Unit]
Description=Gunicorn instance to serve firstweb
After=network.target

[Service]
User=ec2-user
Group=nginx
WorkingDirectory=/home/ec2-user/firstweb
Environment="PATH=/home/ec2-user/firstweb/venv_winjob/bin"
ExecStart=/home/ec2-user/firstweb/venv_winjob/bin/gunicorn --workers 3 --bind unix:/home/ec2-user/firstweb/firstweb.sock app:app

[Install]
WantedBy=multi-user.target

--

--