Files
peters-portfolio-site/DEPLOYMENT.md
T

8.7 KiB

Deployment Guide

Complete setup for deploying the portfolio site with various web servers.

Architecture Options

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:

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

sudo apt update
sudo apt install apache2
sudo systemctl enable apache2

Build and Deploy

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:

<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:

sudo a2ensite pwlmyers.org.conf
sudo a2enmod rewrite deflate expires headers
sudo systemctl restart apache2

SSL with Certbot

sudo apt install certbot python3-certbot-apache
sudo certbot --apache -d pwlmyers.org

Option 2: lighttpd

Install lighttpd

sudo apt update
sudo apt install lighttpd
sudo systemctl enable lighttpd

Build and Deploy

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:

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:

sudo lighty-enable-mod pwlmyers
sudo systemctl restart lighttpd

SSL with Certbot

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

cd /opt/peters-portfolio-site
npm install
npm run build
cp -r public/docs dist/

Caddyfile

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

sudo apt install nginx
sudo systemctl enable nginx

Build

cd /opt/peters-portfolio-site
npm install
npm run build
cp -r public/docs dist/

Nginx Configuration

Create /etc/nginx/sites-available/pwlmyers.org:

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:

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:

#!/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:

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

sudo chown -R www-data:www-data /var/www/pwlmyers.org
sudo chmod -R 755 /var/www/pwlmyers.org

Check logs

# 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

All options require:

  • Domain pointing to VPS (A record)
  • Ports 80/443 open in Oracle Cloud security list
  • Node.js 20.19+ for building

See README.md for development setup.