Skip to main content
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:
1

Install dependencies

npm ci
Use npm ci (not npm install) for reproducible builds based on package-lock.json
2

Run production build

npm run 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
3

Preview the build locally

npm run preview
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:
Deploy to Vercel with automatic builds:Install Vercel CLI:
npm install -g vercel
Deploy:
vercel --prod
Or connect GitHub repository:
  1. Go to vercel.com
  2. Import your GitHub repository
  3. Vercel auto-detects Vite configuration
  4. Deploy automatically on every push
Remember to update the base path in vite.config.js if deploying to root domain

Self-Hosted Server

Using Nginx

1

Build the application

npm run build
2

Copy dist folder to server

scp -r dist/ user@server:/var/www/aws-exam-app
3

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;
    }
}
4

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

docker-compose.prod.yml
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

1

Install Certbot

sudo apt install certbot python3-certbot-nginx
2

Obtain SSL certificate

sudo certbot --nginx -d example.com -d www.example.com
3

Verify auto-renewal

sudo certbot renew --dry-run

Performance Optimization

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

Free monitoring service:
  1. Add your domain at uptimerobot.com
  2. Set check interval (1-5 minutes)
  3. Configure alerts (email, SMS, Slack)

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;
}
Add security headers:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval'" always;
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

1

Source code

Keep code in version control (Git)
git push origin main
2

Docker images

Tag and push images to registry
docker tag aws-exam-app:latest your-registry/aws-exam-app:v1.0.0
docker push your-registry/aws-exam-app:v1.0.0
3

Configuration files

Backup Nginx configs and SSL certificates
tar -czf backup-$(date +%Y%m%d).tar.gz /etc/nginx /etc/letsencrypt

Scaling Considerations

Horizontal Scaling

For high-traffic scenarios:
docker-compose.scale.yml
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

  1. Check if container is running: docker ps
  2. View logs: docker logs aws-exam-app
  3. Verify port binding: netstat -tulpn | grep 5173
  4. 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
  1. Enable caching headers
  2. Use CDN
  3. Enable compression
  4. Check network latency
  5. Scale horizontally if needed

Deployment Checklist

1

Pre-deployment

  • Run production build locally
  • Test build with npm run preview
  • Update version numbers
  • Review and commit changes
  • Update vite.config.js base path if needed
2

Deployment

  • Build production assets
  • Upload to server/container registry
  • Configure environment variables
  • Set up HTTPS/SSL
  • Configure security headers
  • Enable monitoring
3

Post-deployment

  • Verify application loads correctly
  • Test all major features
  • Check health endpoints
  • Monitor logs for errors
  • Set up automated backups
  • Configure uptime monitoring

Next Steps

Local Development

Set up local development environment

Docker Deployment

Learn about containerized deployments

Build docs developers (and LLMs) love