- Backend: AWS ECS (Fargate) or EC2 instances
- Redis: AWS ElastiCache (Redis)
- Load Balancer: AWS Application Load Balancer (ALB)
- CDN: AWS CloudFront (optional)
# Create Redis cluster
aws elasticache create-replication-group \
--replication-group-id weathernote-redis \
--replication-group-description "WeatherNote Redis Cache" \
--engine redis \
--cache-node-type cache.t3.medium \
--num-cache-clusters 2 \
--automatic-failover-enabledcd WeatherNote_Backend
# Build for production
docker build -t weathernote-backend:latest .
# Tag for ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com
docker tag weathernote-backend:latest YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/weathernote-backend:latest
docker push YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/weathernote-backend:latestCreate task-definition.json:
{
"family": "weathernote-backend",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "1024",
"memory": "2048",
"containerDefinitions": [
{
"name": "weathernote-backend",
"image": "YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/weathernote-backend:latest",
"portMappings": [
{
"containerPort": 8080,
"protocol": "tcp"
}
],
"environment": [
{
"name": "PORT",
"value": "8080"
},
{
"name": "REDIS_URL",
"value": "redis://your-redis-endpoint:6379"
},
{
"name": "CACHE_TTL_SECONDS",
"value": "600"
},
{
"name": "RATE_LIMIT_PER_MINUTE",
"value": "100"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/weathernote-backend",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}Deploy:
# Register task definition
aws ecs register-task-definition --cli-input-json file://task-definition.json
# Create service
aws ecs create-service \
--cluster weathernote-cluster \
--service-name weathernote-backend \
--task-definition weathernote-backend \
--desired-count 3 \
--launch-type FARGATE \
--load-balancers targetGroupArn=YOUR_TARGET_GROUP_ARN,containerName=weathernote-backend,containerPort=8080# Create target group
aws elbv2 create-target-group \
--name weathernote-backend-tg \
--protocol HTTP \
--port 8080 \
--vpc-id YOUR_VPC_ID \
--health-check-path /health \
--health-check-interval-seconds 30 \
--target-type ip
# Create load balancer
aws elbv2 create-load-balancer \
--name weathernote-alb \
--subnets subnet-xxx subnet-yyy \
--security-groups sg-xxx- Backend: Cloud Run (serverless) or GKE
- Redis: Memorystore for Redis
- Load Balancer: Cloud Load Balancing
cd WeatherNote_Backend
# Build and push to GCR
gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/weathernote-backend
# Deploy to Cloud Run
gcloud run deploy weathernote-backend \
--image gcr.io/YOUR_PROJECT_ID/weathernote-backend \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars REDIS_URL=redis://YOUR_REDIS_IP:6379,CACHE_TTL_SECONDS=600 \
--memory 1Gi \
--cpu 2 \
--min-instances 2 \
--max-instances 100Recommended Setup:
- 3-5 x 4GB RAM / 2 vCPU Droplets (Backend)
- 1 x 8GB RAM / 4 vCPU Droplet (Redis)
- Load Balancer
# On each backend server
git clone YOUR_REPO
cd WeatherNote_Backend
# Copy and configure .env
cp .env.example .env
nano .env # Set REDIS_URL to your Redis server IP
# Start backend
docker-compose up -d backend
# Verify
curl http://localhost:8080/health# nginx.conf
upstream backend {
least_conn;
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}
server {
listen 80;
server_name api.weathernote.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Rate limiting
limit_req zone=api burst=20;
}
location /health {
proxy_pass http://backend;
access_log off;
}
}
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;Perfect for initial deployment or smaller scale.
# Install Docker and Docker Compose
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# Clone and deploy
git clone YOUR_REPO
cd WeatherNote_Backend
# Configure
cp .env.example .env
nano .env
# Start everything
docker-compose up -d
# Check logs
docker-compose logs -f
# Monitor
docker stats# Using Certbot (Let's Encrypt)
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d api.weathernote.com# Allow only HTTP/HTTPS and SSH
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable# redis.conf
bind 127.0.0.1 INTERNAL_IP
requirepass YOUR_STRONG_PASSWORD
maxmemory 512mb
maxmemory-policy allkeys-lru# docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin#!/bin/bash
# health-check.sh
BACKEND_URL="http://localhost:8080/health"
response=$(curl -s -o /dev/null -w "%{http_code}" $BACKEND_URL)
if [ $response -eq 200 ]; then
echo "β
Backend is healthy"
exit 0
else
echo "β Backend is down (HTTP $response)"
# Send alert (email, Slack, etc.)
exit 1
fi
# Add to crontab:
# */5 * * * * /path/to/health-check.sh# Create auto-scaling policy
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name weathernote-asg \
--min-size 2 \
--max-size 20 \
--desired-capacity 3 \
--target-group-arns YOUR_TARGET_GROUP_ARN
# CPU-based scaling
aws autoscaling put-scaling-policy \
--auto-scaling-group-name weathernote-asg \
--policy-name scale-up \
--scaling-adjustment 2 \
--adjustment-type ChangeInCapacity \
--cooldown 300- ECS Fargate (3 tasks): ~$50/month
- ElastiCache (t3.medium): ~$50/month
- ALB: ~$20/month
- Total: ~$120/month
- Cloud Run (3 instances): ~$40/month
- Memorystore: ~$60/month
- Load Balancer: ~$20/month
- Total: ~$120/month
- 3 x 4GB Droplets: ~$72/month
- 1 x 8GB Droplet (Redis): ~$48/month
- Load Balancer: ~$10/month
- Total: ~$130/month
- 3 x CPX21 (2 vCPU, 4GB): ~$30/month
- 1 x CPX41 (4 vCPU, 8GB): ~$20/month
- Load Balancer: ~$6/month
- Total: ~$56/month π
Create .github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t weathernote-backend WeatherNote_Backend/
- name: Push to registry
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker tag weathernote-backend:latest ${{ secrets.DOCKER_USERNAME }}/weathernote-backend:latest
docker push ${{ secrets.DOCKER_USERNAME }}/weathernote-backend:latest
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /app/WeatherNote_Backend
docker-compose pull
docker-compose up -d- Update
api_constants.dart:
static const String backendUrl = 'https://api.weathernote.com';- Generate unique client ID:
// In main.dart
import 'package:device_info_plus/device_info_plus.dart';
Future<void> initClientId() async {
final deviceInfo = DeviceInfoPlugin();
String? deviceId;
if (Platform.isAndroid) {
final android = await deviceInfo.androidInfo;
deviceId = android.id;
} else if (Platform.isIOS) {
final ios = await deviceInfo.iosInfo;
deviceId = ios.identifierForVendor;
}
ApiConstants.clientId = deviceId;
}- Rebuild and deploy app:
cd WeatherNote_App
flutter build apk --release
flutter build ios --release# Install Apache Bench
sudo apt install apache2-utils
# Test 10,000 requests with 100 concurrent users
ab -n 10000 -c 100 http://api.weathernote.com/api/v1/weather?lat=40.7&lon=-74.0
# Test with siege
siege -c 100 -t 2M http://api.weathernote.com/health# Check backend logs
docker-compose logs -f backend --tail=100
# Monitor Redis
redis-cli INFO stats
# Check cache hit rate
redis-cli INFO stats | grep keyspace_hits
# Monitor system resources
htopAfter deployment, monitor:
- Cache Hit Rate: Should be >95%
- API Response Time: <100ms (cached), <500ms (fresh)
- Open-Meteo API Calls: <5,000/day for 500k users
- Server CPU: <60% average
- Memory Usage: <70%
You're ready for production! π