Add Apache2 and lighttpd static build deployment options

This commit is contained in:
2026-04-18 16:40:14 -04:00
parent 6c40bf080d
commit 7329204125
5 changed files with 344 additions and 242 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
{
"timestamp": "2026-04-18T20:33:42.828Z"
"timestamp": "2026-04-18T20:40:09.901Z"
}
+1 -1
View File
@@ -1,3 +1,3 @@
{
"timestamp": "2026-04-18T20:33:44.083Z"
"timestamp": "2026-04-18T20:40:11.126Z"
}
+1 -1
View File
@@ -2,5 +2,5 @@
"files": {},
"turnCycles": 0,
"maxCycles": 3,
"lastUpdated": "2026-04-18T20:33:44.083Z"
"lastUpdated": "2026-04-18T20:40:11.126Z"
}
+393 -301
View File
@@ -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)
```
### Option B: Vite Dev Server (Development)
```
Cloudflare → VPS (Caddy) → Vite Dev Server (port 5173)
```
## Quick Start: Static Build
The simplest and most efficient approach:
```bash
cd /opt/peters-portfolio-site
git pull
npm install
npm run build
# Now serve the dist/ folder with any web server
```
## Option 1: Apache2
### Install Apache
```bash
sudo apt update
sudo apt install apache2
sudo systemctl enable apache2
```
### 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
<VirtualHost *:80>
ServerName pwlmyers.org
ServerAlias www.pwlmyers.org
DocumentRoot /var/www/pwlmyers.org
# Enable rewrite for SPA routing
<Directory /var/www/pwlmyers.org>
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]
</Directory>
# Enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>
# Cache static assets
<IfModule mod_expires.c>
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"
</IfModule>
ErrorLog ${APACHE_LOG_DIR}/pwlmyers-error.log
CustomLog ${APACHE_LOG_DIR}/pwlmyers-access.log combined
</VirtualHost>
```
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 {
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
}
}
```
## Option 4: Nginx
### Install Nginx
```bash
sudo apt install nginx
sudo systemctl enable nginx
```
### Build
```bash
cd /opt/peters-portfolio-site
npm install
npm run build
cp -r public/docs dist/
```
### Nginx Configuration
Create `/etc/nginx/sites-available/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;
}
```
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
```
## Updating Static Build
Create a deploy script `/opt/peters-portfolio-site/deploy.sh`:
```bash
#!/bin/bash
set -e
cd /opt/peters-portfolio-site
echo "Pulling latest changes..."
git pull
echo "Installing dependencies..."
npm install
echo "Building..."
npm run build
echo "Copying docs..."
cp -r public/docs dist/
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!"
```
Make executable:
```bash
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
### 404 on refresh (SPA routing)
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
sudo chown -R www-data:www-data /var/www/pwlmyers.org
sudo chmod -R 755 /var/www/pwlmyers.org
```
### Check logs
```bash
# Apache
sudo tail -f /var/log/apache2/pwlmyers-error.log
# lighttpd
sudo tail -f /var/log/lighttpd/error.log
# Nginx
sudo tail -f /var/log/nginx/error.log
```
## Prerequisites
- Oracle Cloud VPS (or similar)
- Domain name (Cloudflare recommended)
- Docker + Docker Compose installed
- Git access to repository
All options require:
- Domain pointing to VPS (A record)
- Ports 80/443 open in Oracle Cloud security list
- Node.js 20.19+ for building
## 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
```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 .
```
### 4. Configure Caddy
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
```
Create `docker_configs/caddy/conf/Caddyfile`:
```caddy
pwlmyers.org {
reverse_proxy 172.20.1.232:5173
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`)
### 5. Docker Compose for Caddy
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
```
### 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
```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
```
### 8. Verify Deployment
```bash
# Check Caddy is running
docker ps | grep caddy
# 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/
```
## Updates
### Pull and Update
```bash
cd /opt/peters-portfolio-site
# Pull latest changes
git pull
# Reinstall if package.json changed
npm install
# Copy any new docs
cp -r docs/* public/docs/
# Restart Vite
pkill -f vite || true
nohup npm run dev > vite.log 2>&1 &
# Restart Caddy if config changed
docker compose -f compose.caddy.yaml restart
```
### View Logs
```bash
# Vite logs
tail -f vite.log
# Caddy logs
docker logs -f ubuntu-caddy-1
tail -f docker_configs/caddy/logs/access.log
```
## Troubleshooting
### SSL Certificate Issues
If Caddy fails to get Let's Encrypt certificate:
```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
```
### Port Already in Use
```bash
# Find what's using port 80/443
sudo netstat -tlnp | grep -E ':(80|443)'
# Stop conflicting service
sudo systemctl stop nginx apache2
```
### Vite Not Accessible
```bash
# Check Vite is listening on all interfaces
netstat -tlnp | grep 5173
# 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.
+10
View File
@@ -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