9.3 KiB
Notes App — AWS Cloud Deployment
What the application does
A Notes web application where users can create, view, and delete short text notes via a web browser. Notes are stored persistently in a PostgreSQL database. The app is publicly accessible over HTTPS at the configured domain.
Cloud services and architecture
Cloud provider: Amazon Web Services (AWS), region eu-central-1 (Frankfurt)
| Service | Purpose |
|---|---|
| Amazon ECS Fargate | Runs the frontend and backend containers serverlessly. Automatically restarts containers on failure. |
| Amazon RDS (PostgreSQL 16) | Managed relational database. Provides persistent storage, automated backups (7-day retention). |
| Application Load Balancer (ALB) | Receives all internet traffic. Routes /api/* to the backend, everything else to the frontend. Redirects HTTP → HTTPS. |
| AWS Certificate Manager (ACM) | Issues and renews a free TLS/HTTPS certificate for the domain. |
| Amazon ECR | Private Docker image registry. Stores the backend and frontend images. |
| AWS Secrets Manager | Stores the database password securely. Never stored in code or Git. |
| Amazon CloudWatch Logs | Collects access logs and application logs from both containers. |
| Amazon VPC + Security Groups | Network isolation. ALB SG allows 80/443 from internet. ECS SG allows traffic only from ALB. RDS SG allows 5432 only from ECS. |
Architecture
Browser (HTTPS)
│
▼
Application Load Balancer (port 443, ACM certificate, HTTP→HTTPS redirect)
│
├── /api/* ──▶ ECS Fargate: Backend (Flask, port 5000)
│ │
│ ▼
│ RDS PostgreSQL (port 5432, private subnet)
│
└── /* ──▶ ECS Fargate: Frontend (Nginx, port 80)
The browser loads the static frontend from the ALB. All API calls (/api/*) are made by the browser directly to the ALB, which forwards them to the backend. The frontend container serves only static files — it does not proxy to the backend. The backend connects to RDS using the DATABASE_URL environment variable.
Cost analysis — 1,000 users/day, 50 GB database
Estimated for eu-central-1 (Frankfurt), 1 year of operation.
| Resource | Specification | Unit price | Monthly | Annual |
|---|---|---|---|---|
| ECS Fargate — Backend | 0.25 vCPU, 0.5 GB RAM, 730 h/mo | $0.04048/vCPU-h + $0.004445/GB-h | ~$8.30 | ~$99.60 |
| ECS Fargate — Frontend | 0.25 vCPU, 0.5 GB RAM, 730 h/mo | same | ~$8.30 | ~$99.60 |
| RDS db.t3.micro | PostgreSQL, 1 vCPU, 1 GB RAM, 730 h/mo | ~$0.022/h | ~$16.10 | ~$193.20 |
| RDS Storage | 50 GB gp2 | $0.138/GB-mo | ~$6.90 | ~$82.80 |
| RDS Automated Backups | 50 GB (free up to DB size) | $0.00 | $0.00 | $0.00 |
| ALB | 1 ALB + ~1,000 req/day (~30k/mo) | $0.0252/h + LCU | ~$18.50 | ~$222.00 |
| ECR | ~500 MB images stored | $0.10/GB-mo | ~$0.05 | ~$0.60 |
| CloudWatch Logs | ~5 GB/mo ingestion | $0.57/GB | ~$2.85 | ~$34.20 |
| Data Transfer Out | ~10 GB/mo (1,000 users × ~330 KB) | $0.09/GB | ~$0.90 | ~$10.80 |
| Secrets Manager | 1 secret | $0.40/secret/mo | $0.40 | $4.80 |
| Total | ~$62.30 | ~$747.60 |
Prices are estimates based on AWS public pricing (2025). Actual costs depend on traffic patterns. Use the AWS Pricing Calculator for exact quotes.
Files and their content
sk1/
├── prepare-app.sh # Creates all AWS resources and deploys the app
├── remove-app.sh # Tears down all AWS resources
├── backup.sh # Creates a manual RDS snapshot
├── .env.example # Template for required environment variables
├── .gitignore # Excludes .env from Git
├── backend/
│ ├── app.py # Flask REST API: GET/POST/DELETE /api/notes, /health
│ ├── requirements.txt # Python dependencies: flask, psycopg2-binary
│ └── Dockerfile # python:3.12-slim, runs app.py on port 5000
├── frontend/
│ ├── index.html # Single-page app: dark UI, fetch() calls to /api/
│ ├── nginx.conf # Nginx: serves index.html for all paths
│ └── Dockerfile # nginx:1.27-alpine, serves static files
└── db/
└── init.sql # Creates notes table, inserts sample rows
Configuration description
All secrets and environment-specific values are passed via environment variables, never hardcoded in source files:
DATABASE_URL— injected into the backend ECS task definition at deploy time. Constructed fromDB_USERNAME,DB_PASSWORD,DB_HOST,DB_NAME.DB_PASSWORD— stored in AWS Secrets Manager undernotes-app/db-password. Set via.envbefore running the script.DOMAIN_NAME— the public domain for the app (e.g.notes.example.com). Used to request the ACM certificate and configure the ALB.AWS_REGION/AWS_ACCOUNT_ID— target AWS account and region.
The .env file is listed in .gitignore and must never be committed to Git.
How to run and use the application
Prerequisites
- AWS CLI installed and configured (
aws configure) with permissions for: ECS, ECR, RDS, ELB, ACM, IAM, EC2, Secrets Manager, CloudWatch Logs - Docker installed and running
- A domain name you control (to add DNS CNAME records)
Deploy
cd sk1
cp .env.example .env
# Edit .env — fill in AWS_REGION, AWS_ACCOUNT_ID, DOMAIN_NAME, DB_PASSWORD
source .env
chmod +x prepare-app.sh remove-app.sh
./prepare-app.sh
During deployment the script will print two DNS records to add:
- A CNAME for ACM certificate validation
- A CNAME pointing your domain to the ALB
Once DNS propagates and the certificate is validated, open https://your-domain.com in a browser.
Use the app
- Type a note in the text box and click + Add Note
- Notes appear below, newest first
- Click ✕ Delete to remove a note
Remove everything
source .env
./remove-app.sh
The ACM certificate is not deleted automatically. Delete it manually if no longer needed:
aws acm delete-certificate --certificate-arn <ARN> --region $AWS_REGION
How to perform a data backup
RDS automated backups are enabled with a 7-day retention period and run automatically every day.
Manual snapshot using the provided script:
source .env
./backup.sh
This creates a timestamped RDS snapshot (e.g. notes-app-manual-20260520-120000) and waits for it to complete.
Restore from snapshot:
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier notes-app-db-restored \
--db-snapshot-identifier <snapshot-id> \
--region $AWS_REGION
Export data as SQL dump (requires psql and RDS publicly accessible or VPN):
PGPASSWORD=$DB_PASSWORD pg_dump \
-h <RDS_ENDPOINT> -U $DB_USERNAME -d $DB_NAME \
> backup_$(date +%Y%m%d).sql
How to view access logs from the internet
Container logs (including Nginx access logs) are streamed to Amazon CloudWatch Logs.
Live tail — frontend (Nginx access log):
aws logs tail /ecs/notes-app/frontend --follow --region $AWS_REGION
Live tail — backend:
aws logs tail /ecs/notes-app/backend --follow --region $AWS_REGION
Query last 100 log events:
aws logs get-log-events \
--log-group-name /ecs/notes-app/frontend \
--log-stream-name $(aws logs describe-log-streams \
--log-group-name /ecs/notes-app/frontend \
--order-by LastEventTime --descending \
--query "logStreams[0].logStreamName" --output text --region $AWS_REGION) \
--limit 100 \
--region $AWS_REGION
Conditions for running prepare-app.sh and remove-app.sh
prepare-app.sh:
- AWS CLI must be installed and configured with credentials that have permissions for: ECS, ECR, RDS, ELB, ACM, IAM, EC2, Secrets Manager, CloudWatch Logs
- Docker must be running locally
.envmust exist with all required variables set (AWS_REGION,AWS_ACCOUNT_ID,DOMAIN_NAME,DB_PASSWORD)- The domain in
DOMAIN_NAMEmust be one you can add DNS CNAME records to - Run from the
sk1/directory:source .env && ./prepare-app.sh - The script is idempotent — safe to run multiple times
remove-app.sh:
- Same AWS CLI and
.envrequirements as above - All deletions are idempotent — safe to run multiple times
- Run from the
sk1/directory:source .env && ./remove-app.sh
backup.sh:
- Same AWS CLI and
.envrequirements as above (AWS_REGIONis sufficient) - RDS instance
notes-app-dbmust be running - Run from the
sk1/directory:source .env && ./backup.sh
Use of artificial intelligence
This project was developed with the assistance of Kiro AI (kiro-cli chat agent, Claude Sonnet model).
| What | How AI was used |
|---|---|
prepare-app.sh, remove-app.sh, backup.sh |
Generated by AI, reviewed and verified by the author against AWS documentation |
README.md |
Generated by AI based on assignment requirements, reviewed and corrected by the author |
backend/app.py, frontend/index.html |
Originally written for a previous assignment, adapted with AI assistance |
nginx.conf, Dockerfiles, init.sql |
Generated by AI, reviewed by the author |
The AI was used as a coding assistant. All generated content was reviewed, understood, and verified by the author before submission.