Skip to main content

Production Deployment

This guide covers deploying the André Ruperto Portfolio to production. The application consists of a React frontend and Express backend that can be deployed together or separately.

Pre-deployment Checklist

1

Environment variables

Ensure all production environment variables are configured:
# backend/.env
DATABASE_URL=postgresql://user:password@production-host:5432/portfolio
RESEND_API_KEY=re_production_key
ADMIN_PASSWORD=strong_production_password
JWT_SECRET=production_secret_64_chars
PORT=3001
NODE_ENV=production
Never use development credentials in production!
2

Database setup

Set up your production PostgreSQL database:
# Run migrations on production database
npm run backend:migrate

# Generate Prisma client
npm run backend:generate

# Optionally seed data
npm run backend:seed
3

Build the application

Create optimized production builds:
# Build frontend
npm run build:prod

# This runs:
# 1. npm run copy:images
# 2. npm run clean:dist
# 3. vite build --mode production
The build output will be in backend/dist/.
4

Test the build

Test the production build locally:
npm run preview

Deployment Options

Option 1: Traditional VPS (DigitalOcean, Linode, etc.)

1

Set up server

Install required software on your server:
# Update system
sudo apt update && sudo apt upgrade -y

# Install Node.js 18+
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs

# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib

# Install Nginx (for reverse proxy)
sudo apt install -y nginx
2

Clone and build

Clone the repository and build:
git clone https://github.com/AndreRuperto/portfolio-AndreRuperto.git
cd portfolio-AndreRuperto

# Install dependencies
npm install
npm run backend:install

# Build
npm run build:prod
3

Configure Nginx

Set up Nginx as a reverse proxy:
/etc/nginx/sites-available/portfolio
server {
    listen 80;
    server_name andreruperto.dev www.andreruperto.dev;
    
    # Frontend static files
    location / {
        root /var/www/portfolio/backend/dist;
        try_files $uri $uri/ /index.html;
    }
    
    # Backend API
    location /api {
        proxy_pass http://localhost:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/portfolio /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
4

Set up SSL with Let's Encrypt

Install and configure SSL certificates:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d andreruperto.dev -d www.andreruperto.dev
5

Create systemd service

Create a service to keep the backend running:
/etc/systemd/system/portfolio-backend.service
[Unit]
Description=Portfolio Backend
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/portfolio/backend
ExecStart=/usr/bin/node src/server.js
Restart=always
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable portfolio-backend
sudo systemctl start portfolio-backend
sudo systemctl status portfolio-backend

Option 2: Vercel (Frontend) + Railway (Backend)

1

Deploy backend to Railway

  1. Sign up at railway.app
  2. Create a new project
  3. Add PostgreSQL database
  4. Deploy from GitHub:
    • Connect repository
    • Set root directory to backend/
    • Add environment variables
  5. Note the backend URL
2

Deploy frontend to Vercel

  1. Sign up at vercel.com
  2. Import project from GitHub
  3. Configure build settings:
    • Build Command: npm run build:prod
    • Output Directory: backend/dist
  4. Add environment variables (if needed)
  5. Update API proxy to point to Railway backend

Option 3: Docker Deployment

1

Create Dockerfile

Dockerfile
FROM node:18-alpine

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY backend/package*.json ./backend/

# Install dependencies
RUN npm install
RUN cd backend && npm install

# Copy source code
COPY . .

# Build frontend
RUN npm run build:prod

# Generate Prisma client
RUN npm run backend:generate

WORKDIR /app/backend

EXPOSE 3001

CMD ["npm", "start"]
2

Create docker-compose.yml

docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: portfolio
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
  
  backend:
    build: .
    depends_on:
      - postgres
    environment:
      DATABASE_URL: postgresql://postgres:postgres@postgres:5432/portfolio
      NODE_ENV: production
    ports:
      - "3001:3001"
    volumes:
      - ./backend/.env:/app/backend/.env

volumes:
  postgres_data:
3

Deploy with Docker

# Build and run
docker-compose up -d

# Run migrations
docker-compose exec backend npm run migrate

# View logs
docker-compose logs -f backend

Build Scripts

The project provides optimized build scripts:

Production Build

npm run build:prod
This command:
  1. Copies images to build directory
  2. Cleans previous build artifacts
  3. Runs Vite build with production optimizations
  4. Outputs to backend/dist/

Development Build

npm run build:dev
This command:
  1. Builds with source maps
  2. Less aggressive optimizations
  3. Easier debugging

Post-Deployment

1

Verify deployment

Check that everything is working:
# Test frontend
curl https://andreruperto.dev

# Test backend API
curl https://andreruperto.dev/api/health
2

Monitor logs

Set up log monitoring:
# View backend logs (systemd)
sudo journalctl -u portfolio-backend -f

# View Nginx logs
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
3

Set up monitoring

Consider setting up:
  • Uptime monitoring (UptimeRobot, Pingdom)
  • Error tracking (Sentry)
  • Analytics (Google Analytics, Plausible)

CI/CD with GitHub Actions

Automate deployments with GitHub Actions:
.github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: |
          npm install
          npm run backend:install
      
      - name: Build
        run: npm run build:prod
      
      - name: Deploy to server
        uses: easingthemes/ssh-deploy@main
        env:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
          REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
          REMOTE_USER: ${{ secrets.REMOTE_USER }}
          TARGET: /var/www/portfolio

Updating Production

To update your production deployment:
# Pull latest changes
git pull origin main

# Install new dependencies
npm install
npm run backend:install

# Run migrations if schema changed
npm run backend:migrate

# Rebuild
npm run build:prod

# Restart backend
sudo systemctl restart portfolio-backend

# Reload Nginx
sudo systemctl reload nginx

Performance Optimization

  • Enable gzip compression in Nginx
  • Set up CDN for static assets
  • Configure browser caching
  • Enable HTTP/2
  • Optimize images before deployment
  • Use PostgreSQL connection pooling

Security Checklist

  • HTTPS enabled with valid SSL certificate
  • Strong, unique passwords and secrets
  • CORS properly configured
  • Rate limiting enabled on API
  • Database not publicly accessible
  • Environment variables properly secured
  • Regular security updates
  • Firewall configured (UFW, iptables)

Support

For deployment issues:
  • Check server logs
  • Verify environment variables
  • Ensure PostgreSQL is accessible
  • Test API endpoints individually
  • Review Nginx configuration

Build docs developers (and LLMs) love