โ 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" } ]
}
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 ~]$
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
Clone your app:
$ git clone https://github.com/yourname/my-flask-app.git
$ cd my-flask-app
Install pinned dependencies:
$ pip3 install -r requirements.txt
Ensure requirements.txt specifies exact versions:
Flask==2.3.3
gunicorn==21.2.0
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
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
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
Enable and start the service:
$ sudo systemctl daemon-reload
$ sudo systemctl start flask-app
$ sudo systemctl enable flask-app
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
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
Configure it to forward requests to gunicorn:
$ sudo nano /etc/nginx/conf.d/flask-app.conf
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; }
}
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
Reload the service:
$ sudo systemctl reload nginx
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
Disable directory listing in your site config:
location /static { alias /home/ec2-user/my-flask-app/static; autoindex off; }
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
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?
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)