DEV Community

Python-T Point
Python-T Point

Posted on • Originally published at pythontpoint.in

๐Ÿš€ Deploy Flask App AWS Free Tier โ€” Easy EC2 & Nginx Setup

โ“ Can you deploy a Flask app on AWS Free Tier without paying a dime? Yes โ€” but only if you avoid the three hidden cost traps most beginners fall into.

You can deploy a Flask app on AWS Free Tier using EC2 and Nginx, with zero ongoing cost, as long as you remain within the Free Tierโ€™s technical and usage boundaries. The risk isnโ€™t in the setup โ€” itโ€™s in unintended resource consumption. Misconfigured public IPs, oversized instances, or unmonitored data transfer can trigger charges. This guide covers a deployment thatโ€™s both Free Tierโ€“compliant and production-like, giving you real infrastructure experience without financial exposure.


โ˜๏ธ EC2 Instance โ€” Launching the Right Machine

An EC2 instance is a virtual server in AWSโ€™s cloud infrastructure. The Free Tier includes 750 hours per month of usage for a t2.micro instance, sufficient for one always-on server. To qualify, you must use a Free Tierโ€“eligible AMI, region, and instance type. Launch via the AWS Console or CLI. If using the CLI, ensure aws configure is set with a Free Tierโ€“supported region like us-east-1 and valid credentials.

$ aws ec2 run-instances \ -image-id ami-0abcdef1234567890 \ -instance-type t2.micro \ -key-name my-flask-key \ -security-group-ids sg-987654321 \ -count 1 \ -tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=flask-prod}]'


{ "Instances": [ { "InstanceId": "i-1234567890abcdef0", "InstanceType": "t2.micro", "State": { "Name": "pending" }, "PublicIpAddress": "54.210.123.45" } ]
}
Enter fullscreen mode Exit fullscreen mode

Under the hood, AWS uses the Nitro hypervisor to virtualize compute and memory. The t2.micro provides 1 vCPU and 1 GiB RAM, with CPU credits governing burst capacity. Once credits are exhausted, performance throttles, but no overage fees apply as long as the instance type remains t2.micro. Use Amazon Linux 2 or Ubuntu 20.04+ AMIs โ€” both are Free Tierโ€“eligible. Avoid Windows or RHEL images; they incur additional licensing costs.

You donโ€™t need Kubernetes to run a Flask app โ€” you need a shell, a process manager, and a reverse proxy.

๐Ÿ” Security Groups โ€” Locking Down Access

Security groups act as stateful firewalls at the VPC level. For a Flask app, allow only:

  • SSH (port 22) โ€” restricted to your IP
  • HTTP (port 80) โ€” open to 0.0.0.0/0
  • HTTPS (port 443) โ€” optional, for SSL These rules are enforced by AWSโ€™s distributed virtual switch layer, not host-level iptables. They persist across instance stops and starts. Avoid broad SSH access (e.g., 0.0.0.0/0); it increases exposure and is not required for Free Tier compliance.

๐Ÿ”‘ SSH Access โ€” Connecting Securely

Connect using your key pair:

$ ssh -i ~/.ssh/my-flask-key.pem ec2-user@54.210.123.45


 __| __|_ ) _| ( / Amazon Linux 2 ___|\___|___| https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-172-31-16-174 ~]$
Enter fullscreen mode Exit fullscreen mode

The ec2-user account has passwordless sudo. Use it for setup, but never run services as root. Keep SSH keys secure and rotate them if compromised.


โš™๏ธ Flask Setup โ€” Running the App Properly

Flaskโ€™s built-in development server is single-threaded and unsuitable for production. Use gunicorn as a WSGI server to handle concurrent requests via multiple worker processes. Update the system and install dependencies:

$ sudo yum update -y
$ sudo yum install python3 python3-pip git -y
Enter fullscreen mode Exit fullscreen mode

Clone your app:

$ git clone https://github.com/yourname/my-flask-app.git
$ cd my-flask-app
Enter fullscreen mode Exit fullscreen mode

Install pinned dependencies:

$ pip3 install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

Ensure requirements.txt specifies exact versions:

Flask==2.3.3
gunicorn==21.2.0
Enter fullscreen mode Exit fullscreen mode

Test gunicorn locally:

$ gunicorn -workers 2 -bind 127.0.0.1:8000 app:app


[-10-05 14:30:22 +0000] [12345] [INFO] Starting gunicorn 21.2.0
[-10-05 14:30:22 +0000] [12345] [INFO] Listening at: http://127.0.0.1:8000 (12345)
[-10-05 14:30:22 +0000] [12345] [INFO] Using worker: sync
[-10-05 14:30:22 +0000] [12347] [INFO] Booting worker with pid: 12347
Enter fullscreen mode Exit fullscreen mode

Gunicorn forks two worker processes that accept connections via a socket. The OS uses epoll to manage I/O events efficiently, allowing high throughput under load. This model handles concurrent requests far better than Flaskโ€™s development server. To prevent process death on disconnect, use systemd.

๐Ÿ” systemd โ€” Keeping Gunicorn Alive

Create a systemd service:

$ sudo nano /etc/systemd/system/flask-app.service
Enter fullscreen mode Exit fullscreen mode

Add:

[Unit]
Description=Gunicorn instance for Flask app
After=network.target [Service]
User=ec2-user
Group=ec2-user
WorkingDirectory=/home/ec2-user/my-flask-app
ExecStart=/home/ec2-user/.local/bin/gunicorn -workers 2 -bind 127.0.0.1:8000 app:app
Restart=always [Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Enable and start the service:

$ sudo systemctl daemon-reload
$ sudo systemctl start flask-app
$ sudo systemctl enable flask-app
Enter fullscreen mode Exit fullscreen mode

Verify status:

$ sudo systemctl status flask-app


โ— flask-app.service - Gunicorn instance for Flask app Loaded: loaded (/etc/systemd/system/flask-app.service; enabled) Active: active (running) since Thu -10-05 14:35:10 UTC; 1min ago Main PID: 12345 (gunicorn) Tasks: 3
Enter fullscreen mode Exit fullscreen mode

Systemd uses inotify to monitor process state. On failure, it restarts the service based on the Restart=always policy, ensuring high availability without external tools.


๐ŸŒ Nginx โ€” The Reverse Proxy

Nginx acts as a reverse proxy, handling client connections, static file delivery, and HTTP keep-alives. Offloading these tasks from gunicorn improves performance and security. Install and enable Nginx:

$ sudo yum install nginx -y
$ sudo systemctl start nginx
$ sudo systemctl enable nginx
Enter fullscreen mode Exit fullscreen mode

Configure it to forward requests to gunicorn:

$ sudo nano /etc/nginx/conf.d/flask-app.conf
Enter fullscreen mode Exit fullscreen mode

Add:

server { listen 80; server_name 54.210.123.45; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /static { alias /home/ec2-user/my-flask-app/static; }
}
Enter fullscreen mode Exit fullscreen mode

Validate the configuration:

$ sudo nginx -t


nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Enter fullscreen mode Exit fullscreen mode

Reload the service:

$ sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

The request flow:

1. Client connects to http://54.210.123.45

2. Nginx accepts the connection on port 80

3. Static assets under /static are served directly

4. Dynamic routes are proxied to gunicorn via 127.0.0.1:8000

5. Responses are relayed back through Nginx Nginx uses an asynchronous, event-driven architecture with epoll on Linux, enabling a single worker process to manage thousands of concurrent connections with low memory overhead.

๐Ÿ”’ Security โ€” Disabling Default Server

Remove the default Nginx configuration to eliminate unnecessary exposure:

$ sudo rm /etc/nginx/conf.d/default.conf
Enter fullscreen mode Exit fullscreen mode

Disable directory listing in your site config:

 location /static { alias /home/ec2-user/my-flask-app/static; autoindex off; }
Enter fullscreen mode Exit fullscreen mode

This prevents accidental disclosure of file listings if a directory lacks an index file.


๐Ÿ’พ Free Tier โ€” Staying Within Limits

To deploy a Flask app on AWS Free Tier without cost, adhere strictly to:

  • t2.micro instance type (1 vCPU, 1 GiB RAM)
  • 750 hours/month of EC2 usage
  • 30 GB of EBS gp2 storage
  • 15 GB/month outbound data transfer Exceeding any limit incurs charges. For example, running a t3.small instead of t2.micro costs $0.0208/hour โ€” approximately $15/month. Avoid holding unattached public IPv4 addresses. AWS charges $0.005/hour for them. If you stop the instance, release the public IP unless itโ€™s an Elastic IP that you intend to reuse. Set up a billing alert at $0.10/month via Cost Explorer > Budgets :
  • Type: Cost budget
  • Budgeted amount: $0.10
  • Alert threshold: 100% of actual This ensures early notification before charges accumulate.

๐Ÿ“‰ Monitoring โ€” Watching Your Usage

Track Free Tier usage via CloudWatch:

$ aws cloudwatch get-metric-statistics \ -namespace AWS/Usage \ -metric-name ReclaimedScheduledEvents \ -dimensions Name=Service,Value=EC2 Name=ResourceType,Value=InstanceHours Name=FreeTier,Value=Eligible \ -start-time -10-01T00:00:00Z \ -end-time -10-31T23:59:59Z \ -period 2592000 \ -statistics Maximum
Enter fullscreen mode Exit fullscreen mode

Or use the AWS Console: Billing Dashboard > Free Usage.

๐Ÿ”„ Shutdown โ€” Preserving State Safely

To pause usage:

  • Use Stop , not Terminate โ€” this preserves the EBS volume
  • Stopped instances donโ€™t consume instance hours
  • EBS storage still counts toward the 30 GB Free Tier limit Stop via CLI:

    $ aws ec2 stop-instances -instance-ids i-1234567890abcdef0

The instance resumes with the same disk state and private IP. Public IP may change unless you use an Elastic IP.


๐ŸŸฉ Final Thoughts

You now know how to deploy a Flask app on AWS Free Tier using EC2 and Nginx โ€” not just the commands, but the underlying mechanisms: how systemd keeps processes alive, how Nginx proxies requests, and how AWSโ€™s Free Tier limits actually work. This setup isnโ€™t just free โ€” itโ€™s real. The same architecture scales to production with minor tweaks: swap t2.micro for larger instances, add a domain, enable HTTPS with Letโ€™s Encrypt, and use RDS for databases. But the core concepts remain. The goal isnโ€™t to stay on Free Tier forever. Itโ€™s to learn the fundamentals without financial risk. Once you understand how web servers, reverse proxies, and cloud billing interact, you can make informed choices โ€” whether youโ€™re building a side project or designing a startupโ€™s backend.

โ“ Frequently Asked Questions

Can I use a domain name with this setup?

deploy flask app aws free tier

Yes. Buy a domain via Route 53 or another registrar, then point it to your EC2 instanceโ€™s public IP using an A record. Once configured, update the server_name in Nginx to your domain.

๐Ÿ“‘ Table of Contents

  • โ“ Can you deploy a Flask app on AWS Free Tier without paying a dime? Yes โ€” but only if you avoid the three hidden cost traps most beginners fall into.
  • โ˜๏ธ EC2 Instance โ€” Launching the Right Machine
  • ๐Ÿ” Security Groups โ€” Locking Down Access
  • ๐Ÿ”‘ SSH Access โ€” Connecting Securely
  • โš™๏ธ Flask Setup โ€” Running the App Properly
  • ๐Ÿ” systemd โ€” Keeping Gunicorn Alive
  • ๐ŸŒ Nginx โ€” The Reverse Proxy
  • ๐Ÿ”’ Security โ€” Disabling Default Server
  • ๐Ÿ’พ Free Tier โ€” Staying Within Limits
  • ๐Ÿ“‰ Monitoring โ€” Watching Your Usage
  • ๐Ÿ”„ Shutdown โ€” Preserving State Safely
  • ๐ŸŸฉ Final Thoughts
  • โ“ Frequently Asked Questions
  • Can I use a domain name with this setup?
  • How do I add HTTPS to my Flask app on AWS Free Tier?
  • Why canโ€™t I access my Flask app from the browser?
  • ๐Ÿ“š References & Further Reading

๐Ÿ“š References & Further Reading

  • Official EC2 Free Tier docs โ€” eligibility details and limits: aws.amazon.com

Top comments (0)