This guide covers best practices for deploying the AWS Certified App to production environments with optimal performance, security, and reliability.
Production Build
Building for Production
Create an optimized production build:
Install dependencies
Use npm ci (not npm install) for reproducible builds based on package-lock.json
Run production build
Expected output:vite v7.2.4 building for production...
✓ 145 modules transformed.
dist/index.html 0.65 kB │ gzip: 0.38 kB
dist/assets/index-BfTvK0Qh.css 3.21 kB │ gzip: 1.18 kB
dist/assets/index-D4RqP5gj.js 156.42 kB │ gzip: 50.32 kB
✓ built in 2.45s
Preview the build locally
Access at: http://localhost:4173
Build Output
The npm run build command generates optimized files in the dist/ directory:
dist/
├── index.html # Optimized HTML entry point
├── assets/
│ ├── index-[hash].css # Minified CSS bundle
│ ├── index-[hash].js # Minified JS bundle
│ └── [images] # Optimized images
└── [other static files] # Public assets
Vite automatically:
- Minifies HTML, CSS, and JavaScript
- Generates content-hashed filenames for cache busting
- Tree-shakes unused code
- Optimizes images and assets
Static Hosting
The built application is a static site that can be hosted anywhere:
Popular Hosting Options
Vercel
Netlify
GitHub Pages
AWS S3 + CloudFront
Deploy to Vercel with automatic builds:Install Vercel CLI:Deploy:Or connect GitHub repository:
- Go to vercel.com
- Import your GitHub repository
- Vercel auto-detects Vite configuration
- Deploy automatically on every push
Remember to update the base path in vite.config.js if deploying to root domain
Deploy to Netlify:Install Netlify CLI:npm install -g netlify-cli
Deploy:npm run build
netlify deploy --prod --dir=dist
Or use Netlify UI:
- Connect your Git repository at netlify.com
- Set build command:
npm run build
- Set publish directory:
dist
- Deploy
Deploy to GitHub Pages:Create deploy.sh script:#!/usr/bin/env sh
set -e
npm run build
cd dist
git init
git add -A
git commit -m 'deploy'
git push -f [email protected]:username/repo.git main:gh-pages
cd -
Run deployment:chmod +x deploy.sh
./deploy.sh
Update vite.config.js with your repository name: Deploy to AWS:1. Build the application:2. Upload to S3:aws s3 sync dist/ s3://your-bucket-name --delete
3. Invalidate CloudFront cache:aws cloudfront create-invalidation \
--distribution-id YOUR_DIST_ID \
--paths "/*"
S3 + CloudFront provides global CDN distribution with low latency
Self-Hosted Server
Using Nginx
Copy dist folder to server
scp -r dist/ user@server:/var/www/aws-exam-app
Configure Nginx
Create /etc/nginx/sites-available/aws-exam-app:server {
listen 80;
server_name example.com;
root /var/www/aws-exam-app;
index index.html;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
}
Enable and restart Nginx
sudo ln -s /etc/nginx/sites-available/aws-exam-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
Using serve (Simple HTTP Server)
For quick deployments, use the serve package:
# Install serve globally
npm install -g serve
# Build and serve
npm run build
serve -s dist -l 5173
This is the same approach used in the Docker image
Docker Production Deployment
Deploy using Docker with production optimizations:
Using Docker Hub Image
# Pull the official image
docker pull thisisrober/aws-exam-prep-app:latest
# Run with production settings
docker run -d \
--name aws-exam-app \
-p 80:5173 \
-e NODE_ENV=production \
--restart unless-stopped \
--memory="512m" \
--cpus="0.5" \
thisisrober/aws-exam-prep-app:latest
Docker Compose Production Setup
version: '3.8'
services:
app:
image: thisisrober/aws-exam-prep-app:latest
container_name: aws-exam-app
ports:
- "80:5173"
environment:
- NODE_ENV=production
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:5173', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s
networks:
- app-network
# Optional: Add Nginx reverse proxy
nginx:
image: nginx:alpine
container_name: nginx-proxy
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
restart: unless-stopped
networks:
- app-network
networks:
app-network:
driver: bridge
Deploy with:
docker compose -f docker-compose.prod.yml up -d
HTTPS Configuration
Using Let’s Encrypt with Certbot
Install Certbot
sudo apt install certbot python3-certbot-nginx
Obtain SSL certificate
sudo certbot --nginx -d example.com -d www.example.com
Verify auto-renewal
sudo certbot renew --dry-run
CDN Configuration
Cloudflare
Use Cloudflare for:
- Global CDN caching
- DDoS protection
- Automatic HTTPS
- Image optimization
AWS CloudFront
Configure CloudFront for:
- Edge caching
- Custom domain support
- Geo-restriction
- Real-time logs
Caching Strategy
Recommended cache headers:
# HTML files (short cache)
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
# JavaScript and CSS (content-hashed)
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Images and fonts
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
Compression
Enable gzip/brotli compression:
# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
# Brotli (if available)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
Monitoring and Logging
Health Checks
Implement health check endpoints:
# Docker health check
curl http://localhost:5173
# Expected: HTTP 200 with HTML content
Uptime Monitoring
UptimeRobot
Pingdom
Custom Script
Free monitoring service:
- Add your domain at uptimerobot.com
- Set check interval (1-5 minutes)
- Configure alerts (email, SMS, Slack)
Professional monitoring:
- Real-time alerts
- Performance insights
- Transaction monitoring
Simple bash script:#!/bin/bash
URL="http://example.com"
STATUS=$(curl -o /dev/null -s -w "%{http_code}" $URL)
if [ $STATUS -ne 200 ]; then
echo "Site down! Status: $STATUS"
# Send alert (email, slack, etc.)
fi
Access Logs
Monitor Nginx access logs:
# Real-time log monitoring
sudo tail -f /var/log/nginx/access.log
# Analyze with GoAccess
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED
Docker Logs
# View container logs
docker logs aws-exam-app
# Follow logs in real-time
docker logs -f aws-exam-app
# Export logs to file
docker logs aws-exam-app > app.log 2>&1
Security Best Practices
Always implement these security measures in production:
Force HTTPS redirects:server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
Prevent abuse with rate limiting:limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location / {
limit_req zone=mylimit burst=20 nodelay;
}
}
Configure firewall to allow only necessary ports:# UFW (Ubuntu)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp
sudo ufw enable
Backup and Recovery
Backup Strategy
Source code
Keep code in version control (Git) Docker images
Tag and push images to registrydocker tag aws-exam-app:latest your-registry/aws-exam-app:v1.0.0
docker push your-registry/aws-exam-app:v1.0.0
Configuration files
Backup Nginx configs and SSL certificatestar -czf backup-$(date +%Y%m%d).tar.gz /etc/nginx /etc/letsencrypt
Scaling Considerations
Horizontal Scaling
For high-traffic scenarios:
services:
app:
image: thisisrober/aws-exam-prep-app:latest
deploy:
replicas: 3
ports:
- "5173"
load-balancer:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx-lb.conf:/etc/nginx/nginx.conf
depends_on:
- app
Scale up:
docker compose up -d --scale app=5
Troubleshooting Production Issues
- Check if container is running:
docker ps
- View logs:
docker logs aws-exam-app
- Verify port binding:
netstat -tulpn | grep 5173
- Check Nginx config:
sudo nginx -t
# Verify certificate
sudo certbot certificates
# Renew if needed
sudo certbot renew
# Test HTTPS
curl -I https://example.com
Limit container resources:docker update --memory="256m" aws-exam-app
- Enable caching headers
- Use CDN
- Enable compression
- Check network latency
- Scale horizontally if needed
Deployment Checklist
Next Steps
Local Development
Set up local development environment
Docker Deployment
Learn about containerized deployments