From 7329204125adb88e92aba39052e5df51ed3e73d4 Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Sat, 18 Apr 2026 16:40:14 -0400 Subject: [PATCH] Add Apache2 and lighttpd static build deployment options --- .pi-lens/cache/jscpd.meta.json | 2 +- .pi-lens/cache/knip.meta.json | 2 +- .pi-lens/turn-state.json | 2 +- DEPLOYMENT.md | 570 +++++++++++++++++++-------------- README.md | 10 + 5 files changed, 344 insertions(+), 242 deletions(-) diff --git a/.pi-lens/cache/jscpd.meta.json b/.pi-lens/cache/jscpd.meta.json index e271098..df661b4 100644 --- a/.pi-lens/cache/jscpd.meta.json +++ b/.pi-lens/cache/jscpd.meta.json @@ -1,3 +1,3 @@ { - "timestamp": "2026-04-18T20:33:42.828Z" + "timestamp": "2026-04-18T20:40:09.901Z" } \ No newline at end of file diff --git a/.pi-lens/cache/knip.meta.json b/.pi-lens/cache/knip.meta.json index 38fbc79..ca37bce 100644 --- a/.pi-lens/cache/knip.meta.json +++ b/.pi-lens/cache/knip.meta.json @@ -1,3 +1,3 @@ { - "timestamp": "2026-04-18T20:33:44.083Z" + "timestamp": "2026-04-18T20:40:11.126Z" } \ No newline at end of file diff --git a/.pi-lens/turn-state.json b/.pi-lens/turn-state.json index 6953fb0..c07b335 100644 --- a/.pi-lens/turn-state.json +++ b/.pi-lens/turn-state.json @@ -2,5 +2,5 @@ "files": {}, "turnCycles": 0, "maxCycles": 3, - "lastUpdated": "2026-04-18T20:33:44.083Z" + "lastUpdated": "2026-04-18T20:40:11.126Z" } \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 5476588..06bbe33 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,311 +1,403 @@ # Deployment Guide -Complete setup for deploying the portfolio site with Docker, Caddy, and Oracle Cloud VPS. +Complete setup for deploying the portfolio site with various web servers. -## Architecture +## Architecture Options +### Option A: Static Build (Recommended for Production) ``` -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Cloudflare │────▶│ Oracle VPS │────▶│ Vite Dev │ -│ (DNS + Proxy) │ │ (Caddy) │ │ Server │ -│ │ │ Port 80/443 │────▶│ Port 5173 │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ - ▼ - ┌─────────────────┐ - │ Docker │ - │ (Caddy) │ - └─────────────────┘ +Cloudflare → VPS (Apache/lighttpd/Caddy) → dist/ (static files) ``` -## Prerequisites +### Option B: Vite Dev Server (Development) +``` +Cloudflare → VPS (Caddy) → Vite Dev Server (port 5173) +``` -- Oracle Cloud VPS (or similar) -- Domain name (Cloudflare recommended) -- Docker + Docker Compose installed -- Git access to repository +## Quick Start: Static Build -## Server Setup - -### 1. Provision VPS - -**Oracle Cloud Free Tier:** -1. Create instance (Ubuntu 22.04/24.04 ARM or AMD) -2. Add ingress rules for ports 22, 80, 443 -3. Note the public IP - -**Open Ports in Oracle Console:** -1. Networking → Virtual Cloud Networks -2. Click your VCN → Subnets -3. Security Lists → Default Security List -4. Add Ingress Rules: - - TCP 80 from 0.0.0.0/0 - - TCP 443 from 0.0.0.0/0 - - TCP 22 from your IP (SSH) - -### 2. Install Dependencies +The simplest and most efficient approach: ```bash -# Update system -sudo apt update && sudo apt upgrade -y - -# Install Docker -curl -fsSL https://get.docker.com | sh -sudo usermod -aG docker $USER -newgrp docker - -# Install Node.js 22 -curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - -sudo apt install -y nodejs - -# Verify -node -v # Should be 20.19+ or 22.12+ -npm -v -docker --version -``` - -### 3. Clone Repository - -```bash -sudo mkdir -p /opt/peters-portfolio-site -sudo chown $USER:$USER /opt/peters-portfolio-site cd /opt/peters-portfolio-site -git clone https://git.dustin.coffee/hobokenchicken/peters-portfolio-site.git . +git pull +npm install +npm run build +# Now serve the dist/ folder with any web server ``` -### 4. Configure Caddy +## Option 1: Apache2 + +### Install Apache -Create directory structure: ```bash -mkdir -p docker_configs/caddy/conf -mkdir -p docker_configs/caddy/site -mkdir -p docker_configs/caddy/caddy_data -mkdir -p docker_configs/caddy/caddy_config -mkdir -p docker_configs/caddy/logs +sudo apt update +sudo apt install apache2 +sudo systemctl enable apache2 ``` -Create `docker_configs/caddy/conf/Caddyfile`: +### Build and Deploy + +```bash +cd /opt/peters-portfolio-site +npm install +npm run build + +# Copy docs to dist for serving +cp -r public/docs dist/ + +# Deploy to Apache +sudo rm -rf /var/www/pwlmyers.org/* +sudo cp -r dist/* /var/www/pwlmyers.org/ +sudo chown -R www-data:www-data /var/www/pwlmyers.org +``` + +### Apache Configuration + +Create `/etc/apache2/sites-available/pwlmyers.org.conf`: + +```apache + + ServerName pwlmyers.org + ServerAlias www.pwlmyers.org + DocumentRoot /var/www/pwlmyers.org + + # Enable rewrite for SPA routing + + Options -Indexes +FollowSymLinks + AllowOverride All + Require all granted + + # Handle client-side routing + RewriteEngine On + RewriteBase / + RewriteRule ^index\.html$ - [L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule . /index.html [L] + + + # Enable compression + + AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css + AddOutputFilterByType DEFLATE application/javascript application/json + + + # Cache static assets + + ExpiresActive On + ExpiresByType image/png "access plus 1 month" + ExpiresByType image/jpeg "access plus 1 month" + ExpiresByType text/css "access plus 1 week" + ExpiresByType application/javascript "access plus 1 week" + + + ErrorLog ${APACHE_LOG_DIR}/pwlmyers-error.log + CustomLog ${APACHE_LOG_DIR}/pwlmyers-access.log combined + +``` + +Enable the site: +```bash +sudo a2ensite pwlmyers.org.conf +sudo a2enmod rewrite deflate expires headers +sudo systemctl restart apache2 +``` + +### SSL with Certbot + +```bash +sudo apt install certbot python3-certbot-apache +sudo certbot --apache -d pwlmyers.org +``` + +## Option 2: lighttpd + +### Install lighttpd + +```bash +sudo apt update +sudo apt install lighttpd +sudo systemctl enable lighttpd +``` + +### Build and Deploy + +```bash +cd /opt/peters-portfolio-site +npm install +npm run build +cp -r public/docs dist/ + +sudo mkdir -p /var/www/pwlmyers.org +sudo rm -rf /var/www/pwlmyers.org/* +sudo cp -r dist/* /var/www/pwlmyers.org/ +sudo chown -R www-data:www-data /var/www/pwlmyers.org +``` + +### lighttpd Configuration + +Edit `/etc/lighttpd/lighttpd.conf` or create `/etc/lighttpd/conf-available/99-pwlmyers.conf`: + +```lighttpd +server.document-root = "/var/www/pwlmyers.org" +server.port = 80 +server.bind = "0.0.0.0" +server.tag = "lighttpd" + +# Server name +server.name = "pwlmyers.org" + +# Index file +index-file.names = ( "index.html" ) + +# MIME types +mimetype.assign = ( + ".html" => "text/html", + ".htm" => "text/html", + ".css" => "text/css", + ".js" => "application/javascript", + ".json" => "application/json", + ".png" => "image/png", + ".jpg" => "image/jpeg", + ".jpeg" => "image/jpeg", + ".gif" => "image/gif", + ".svg" => "image/svg+xml", + ".ico" => "image/x-icon", + ".pdf" => "application/pdf", + ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", + ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document" +) + +# Enable mod_rewrite for SPA routing +server.modules += ( "mod_rewrite" ) +url.rewrite-if-not-file = ( + "^/(.*)$" => "/index.html" +) + +# Enable compression +server.modules += ( "mod_deflate" ) +deflate.mimetypes = ( "text/html", "text/plain", "text/css", "application/javascript" ) +deflate.allowed-encodings = ( "gzip", "deflate" ) + +# Security headers +server.modules += ( "mod_setenv" ) +setenv.add-response-header += ( + "X-Frame-Options" => "SAMEORIGIN", + "X-Content-Type-Options" => "nosniff", + "X-XSS-Protection" => "1; mode=block", + "Referrer-Policy" => "strict-origin-when-cross-origin" +) + +# Logging +server.errorlog = "/var/log/lighttpd/error.log" +accesslog.filename = "/var/log/lighttpd/access.log" + +# File upload limits (for large PPTX/PDF files) +server.max-request-size = 104857600 # 100MB +``` + +Enable and restart: +```bash +sudo lighty-enable-mod pwlmyers +sudo systemctl restart lighttpd +``` + +### SSL with Certbot + +```bash +sudo apt install certbot +sudo certbot certonly --standalone -d pwlmyers.org +``` + +Then configure lighttpd for SSL (see certbot output for paths). + +## Option 3: Caddy (Static Files) + +### Build + +```bash +cd /opt/peters-portfolio-site +npm install +npm run build +cp -r public/docs dist/ +``` + +### Caddyfile ```caddy pwlmyers.org { - reverse_proxy 172.20.1.232:5173 + root * /opt/peters-portfolio-site/dist + file_server + try_files {path} {path}/ /index.html + + # Handle docs directory + handle /docs/* { + uri strip_prefix /docs + root * /opt/peters-portfolio-site/dist/docs + file_server + } log { output file /var/log/caddy/access.log - format json - } - - # Optional: Add headers - header { - X-Content-Type-Options nosniff - X-Frame-Options DENY - X-XSS-Protection "1; mode=block" } } ``` -**Note:** Adjust `172.20.1.232` to match your Vite server's IP (check with `ip addr show`) +## Option 4: Nginx -### 5. Docker Compose for Caddy +### Install Nginx -Create `compose.caddy.yaml`: - -```yaml -services: - caddy: - image: caddy:latest - restart: unless-stopped - ports: - - "80:80" - - "443:443" - - "443:443/udp" - volumes: - - ./docker_configs/caddy/conf:/etc/caddy - - ./docker_configs/caddy/site:/srv - - ./docker_configs/caddy/caddy_data:/data - - ./docker_configs/caddy/caddy_config:/config - - ./docker_configs/caddy/logs:/var/log/caddy - networks: - - caddy_network - -networks: - caddy_network: - driver: bridge +```bash +sudo apt install nginx +sudo systemctl enable nginx ``` -### 6. DNS Configuration (Cloudflare) - -1. Log into Cloudflare dashboard -2. Select your domain -3. **DNS** tab → Add records: - - Type: `A` - - Name: `@` (root) or `www` - - Content: `150.136.209.11` (your VPS IP) - - Proxy status: 🟠 Orange cloud (proxied) or ⚪ Gray cloud (DNS only) - - TTL: Auto - -4. **SSL/TLS** tab: - - Mode: **Full (strict)** or **Full** - - Always Use HTTPS: On - -### 7. Deploy Application +### Build ```bash cd /opt/peters-portfolio-site - -# Copy docs to public -cp -r docs/* public/docs/ - -# Install dependencies npm install - -# Start Vite dev server (background) -nohup npm run dev > vite.log 2>&1 & - -# Start Caddy -docker compose -f compose.caddy.yaml up -d +npm run build +cp -r public/docs dist/ ``` -### 8. Verify Deployment +### Nginx Configuration -```bash -# Check Caddy is running -docker ps | grep caddy +Create `/etc/nginx/sites-available/pwlmyers.org`: -# Check Vite is running -ps aux | grep vite -curl http://172.20.1.232:5173/ - -# Test from VPS -curl -H "Host: pwlmyers.org" http://localhost/ - -# Test HTTPS (after DNS propagates) -curl -v https://pwlmyers.org/ +```nginx +server { + listen 80; + server_name pwlmyers.org www.pwlmyers.org; + root /var/www/pwlmyers.org; + index index.html; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml; + + # Cache static assets + location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf)$ { + expires 1M; + add_header Cache-Control "public, immutable"; + } + + # Serve docs correctly + location /docs/ { + alias /var/www/pwlmyers.org/docs/; + autoindex off; + } + + # SPA routing - serve index.html for all non-file routes + location / { + try_files $uri $uri/ /index.html; + } + + # 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; + + # File upload size (for docs) + client_max_body_size 100M; +} ``` -## Updates +Enable: +```bash +sudo ln -s /etc/nginx/sites-available/pwlmyers.org /etc/nginx/sites-enabled/ +sudo rm /etc/nginx/sites-enabled/default # Remove default +sudo nginx -t +sudo systemctl restart nginx +``` -### Pull and Update +## Updating Static Build + +Create a deploy script `/opt/peters-portfolio-site/deploy.sh`: ```bash +#!/bin/bash +set -e + cd /opt/peters-portfolio-site -# Pull latest changes +echo "Pulling latest changes..." git pull -# Reinstall if package.json changed +echo "Installing dependencies..." npm install -# Copy any new docs -cp -r docs/* public/docs/ +echo "Building..." +npm run build -# Restart Vite -pkill -f vite || true -nohup npm run dev > vite.log 2>&1 & +echo "Copying docs..." +cp -r public/docs dist/ -# Restart Caddy if config changed -docker compose -f compose.caddy.yaml restart +echo "Deploying to web server..." +# For Apache: +sudo rm -rf /var/www/pwlmyers.org/* +sudo cp -r dist/* /var/www/pwlmyers.org/ +sudo chown -R www-data:www-data /var/www/pwlmyers.org + +# For lighttpd: +# sudo rm -rf /var/www/pwlmyers.org/* +# sudo cp -r dist/* /var/www/pwlmyers.org/ +# sudo chown -R www-data:www-data /var/www/pwlmyers.org +# sudo systemctl restart lighttpd + +echo "Done!" ``` -### View Logs - +Make executable: ```bash -# Vite logs -tail -f vite.log - -# Caddy logs -docker logs -f ubuntu-caddy-1 -tail -f docker_configs/caddy/logs/access.log +chmod +x /opt/peters-portfolio-site/deploy.sh ``` +## Comparison + +| Server | RAM Usage | Ease of Config | SSL | Best For | +|--------|-----------|----------------|-----|----------| +| Apache | High | Medium | Certbot | Complex setups | +| Nginx | Low | Medium | Certbot | High traffic | +| lighttpd | Very Low | Easy | Manual | Resource constrained | +| Caddy | Low | Very Easy | Auto | Simplicity | + ## Troubleshooting -### SSL Certificate Issues +### 404 on refresh (SPA routing) -If Caddy fails to get Let's Encrypt certificate: +Ensure rewrite rules are configured to serve `index.html` for non-file paths. + +### Docs not downloading + +Check MIME types are configured for `.pptx`, `.docx`, `.pdf`. + +### Permission denied ```bash -# Check Cloudflare proxy status -dig pwlmyers.org +short -# Should return your VPS IP, not Cloudflare IPs - -# If using Cloudflare proxy (orange cloud): -# - Use Cloudflare Origin Certificate -# - Or switch to DNS only (gray cloud) for Let's Encrypt +sudo chown -R www-data:www-data /var/www/pwlmyers.org +sudo chmod -R 755 /var/www/pwlmyers.org ``` -### Port Already in Use +### Check logs ```bash -# Find what's using port 80/443 -sudo netstat -tlnp | grep -E ':(80|443)' +# Apache +sudo tail -f /var/log/apache2/pwlmyers-error.log -# Stop conflicting service -sudo systemctl stop nginx apache2 +# lighttpd +sudo tail -f /var/log/lighttpd/error.log + +# Nginx +sudo tail -f /var/log/nginx/error.log ``` -### Vite Not Accessible +## Prerequisites -```bash -# Check Vite is listening on all interfaces -netstat -tlnp | grep 5173 +All options require: +- Domain pointing to VPS (A record) +- Ports 80/443 open in Oracle Cloud security list +- Node.js 20.19+ for building -# Should show 0.0.0.0:5173, not 127.0.0.1:5173 - -# Check allowedHosts in vite.config.ts -cat vite.config.ts -``` - -### Docker Network Issues - -```bash -# Check Caddy can reach Vite -docker exec ubuntu-caddy-1 wget -qO- http://172.20.1.232:5173/ - -# If using Docker network instead of host IP: -docker network ls -docker network inspect caddy_caddy_network -``` - -## Alternative: Systemd Service - -Instead of `nohup`, create a systemd service for Vite: - -```bash -sudo tee /etc/systemd/system/peters-portfolio.service > /dev/null <<'EOF' -[Unit] -Description=Peter's Portfolio Vite Dev Server -After=network.target - -[Service] -Type=simple -User=ubuntu -WorkingDirectory=/opt/peters-portfolio-site -ExecStart=/usr/bin/npm run dev -Restart=always -RestartSec=10 -Environment=NODE_ENV=development - -[Install] -WantedBy=multi-user.target -EOF - -sudo systemctl daemon-reload -sudo systemctl enable peters-portfolio -sudo systemctl start peters-portfolio -sudo systemctl status peters-portfolio -``` - -## Security Notes - -- Keep `node_modules` updated: `npm audit fix` -- Use Cloudflare proxy for DDoS protection -- Consider fail2ban for SSH brute force protection -- Regular backups of `docs/` directory - -## Support - -For issues with: -- **Application code:** Check README.md troubleshooting section -- **Server/Docker:** Check logs with commands above -- **DNS/SSL:** Verify Cloudflare settings and Oracle firewall rules +See [README.md](README.md) for development setup. diff --git a/README.md b/README.md index fcd38a0..2cfa6c9 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,16 @@ npm run dev The dev server runs at `http://localhost:5173/` by default. +### Production Build (Recommended) + +```bash +npm install +npm run build +# Serve dist/ folder with Apache, Nginx, lighttpd, or Caddy +``` + +See [DEPLOYMENT.md](DEPLOYMENT.md) for server configuration examples. + ### Build for Production ```bash