Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rafaverde/macondoLinkManager/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers deploying Macondo Link Manager to production environments. The application is currently deployed using Vercel (frontend) and Railway (backend + database).

Production URLs

The current production deployment:

Architecture Overview

Macondo Link Manager uses a split deployment strategy:
┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│   Vercel    │─────▶│   Railway   │─────▶│  PostgreSQL │
│  (Frontend) │ HTTPS │  (Backend)  │      │  (Railway)  │
└─────────────┘      └─────────────┘      └─────────────┘
  • Frontend (Next.js): Deployed on Vercel with custom domain
  • Backend (Fastify): Deployed on Railway with custom domain
  • Database (PostgreSQL): Managed database on Railway

Deployment Options

Vercel + Railway

Current production setup - Recommended for most use cases

Docker Deployment

Self-hosted option using Docker containers
This is the current production setup used by Macondo.

Prerequisites

  • Vercel account
  • Railway account
  • Custom domain (optional but recommended)
  • Google OAuth credentials configured for production URLs

Deploy Backend to Railway

1
Create Railway project
2
  • Go to Railway
  • Click New Project
  • Select Deploy from GitHub repo
  • Connect your repository
  • 3
    Add PostgreSQL database
    4
  • In your Railway project, click New
  • Select DatabasePostgreSQL
  • Railway will automatically provision the database
  • Copy the DATABASE_URL connection string
  • 5
    Configure backend service
    6
  • Click on your API service
  • Go to SettingsRoot Directory
  • Set to api
  • Under Build, set build command:
    npm install && npx prisma generate && npm run build
    
  • Set start command:
    sh -c "./scripts/download-geolite.sh || true; node dist/server.js"
    
  • 7
    Set environment variables
    8
    Add the following environment variables in Railway:
    9
    DATABASE_URL=${{Postgres.DATABASE_URL}}
    BASE_URL=https://li.mcd.ppg.br
    GOOGLE_CLIENT_ID=your_production_client_id
    GOOGLE_CLIENT_SECRET=your_production_client_secret
    JWT_SECRET=your_production_jwt_secret
    FRONTEND_URL=https://app.mcd.ppg.br
    NODE_ENV=production
    NODE_VERSION=20
    PORT=3333
    
    10
    DATABASE_URL will be automatically populated by Railway when you reference the PostgreSQL service.
    11
    Run database migrations
    12
    After deployment, run migrations using Railway CLI:
    13
    # Install Railway CLI
    npm i -g @railway/cli
    
    # Login
    railway login
    
    # Link to your project
    railway link
    
    # Run migrations
    railway run npx prisma migrate deploy
    
    14
    Alternatively, add a migration step to your Dockerfile:
    15
    CMD ["sh", "-c", "npx prisma migrate deploy && node dist/server.js"]
    
    16
    Configure custom domain
    17
  • In Railway, go to your API service
  • Click SettingsNetworking
  • Add custom domain: li.mcd.ppg.br
  • Update your DNS records as instructed
  • Wait for SSL certificate provisioning
  • Deploy Frontend to Vercel

    1
    Create Vercel project
    2
  • Go to Vercel
  • Click New Project
  • Import your Git repository
  • Configure the project:
    • Framework: Next.js
    • Root Directory: web
    • Build Command: npm run build
    • Output Directory: .next
  • 3
    Set environment variables
    4
    Add environment variables in Vercel:
    5
    NEXT_PUBLIC_API_URL=https://li.mcd.ppg.br
    
    6
    Deploy
    7
    Click Deploy. Vercel will automatically build and deploy your frontend.
    8
    Configure custom domain
    9
  • Go to SettingsDomains
  • Add custom domain: app.mcd.ppg.br
  • Update DNS records as instructed
  • Vercel automatically provisions SSL certificates
  • Update OAuth Redirect URIs

    Don’t forget to update Google OAuth settings with production URLs.
    In Google Cloud Console:
    1. Go to APIs & ServicesCredentials
    2. Edit your OAuth 2.0 Client ID
    3. Add authorized redirect URI:
      https://li.mcd.ppg.br/auth/google/callback
      
    4. Add authorized JavaScript origins:
      https://app.mcd.ppg.br
      

    Docker Deployment

    For self-hosted deployments using Docker.

    Production Dockerfile

    The API includes a multi-stage production Dockerfile:
    # Stage 1: Build
    FROM node:20-alpine AS build
    WORKDIR /app
    
    COPY package*.json ./
    RUN npm install
    
    COPY . .
    COPY data ./data
    
    RUN apk add --no-cache curl tar
    RUN npx prisma generate
    RUN npm run build
    
    # Stage 2: Runtime
    FROM node:20-alpine
    WORKDIR /app
    
    RUN apk add --no-cache curl tar
    
    COPY --from=build /app/package*.json ./
    COPY --from=build /app/node_modules ./node_modules
    COPY --from=build /app/dist ./dist
    COPY --from=build /app/prisma ./prisma
    COPY --from=build /app/data ./data
    COPY --from=build /app/scripts ./scripts
    
    ENV NODE_ENV=production
    EXPOSE 3333
    
    CMD ["sh", "-c", "./scripts/download-geolite.sh || true; node dist/server.js"]
    

    Deploy with Docker Compose

    1
    Create production docker-compose
    2
    Create docker-compose.prod.yml:
    3
    services:
      postgres:
        image: postgres:15-alpine
        container_name: macondo_links_db_prod
        restart: always
        environment:
          POSTGRES_DB: ${POSTGRES_DB}
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
        volumes:
          - postgres_data:/var/lib/postgresql/data
        networks:
          - macondo-network
    
      api:
        build:
          context: ./api
          dockerfile: Dockerfile
        container_name: macondo_links_api_prod
        restart: always
        ports:
          - "3333:3333"
        environment:
          DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
          BASE_URL: ${BASE_URL}
          GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
          GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
          JWT_SECRET: ${JWT_SECRET}
          FRONTEND_URL: ${FRONTEND_URL}
          NODE_ENV: production
          PORT: 3333
        depends_on:
          - postgres
        networks:
          - macondo-network
    
      web:
        build:
          context: ./web
          dockerfile: Dockerfile
        container_name: macondo_links_web_prod
        restart: always
        ports:
          - "3000:3000"
        environment:
          NEXT_PUBLIC_API_URL: ${BASE_URL}
        depends_on:
          - api
        networks:
          - macondo-network
    
    volumes:
      postgres_data:
    
    networks:
      macondo-network:
        driver: bridge
    
    4
    Configure production environment
    5
    Create .env.production:
    6
    POSTGRES_DB=macondo_links_prod
    POSTGRES_USER=postgres
    POSTGRES_PASSWORD=strong_production_password
    
    BASE_URL=https://li.mcd.ppg.br
    FRONTEND_URL=https://app.mcd.ppg.br
    
    GOOGLE_CLIENT_ID=production_client_id
    GOOGLE_CLIENT_SECRET=production_client_secret
    JWT_SECRET=strong_random_jwt_secret
    
    NODE_ENV=production
    
    7
    Use strong passwords and secrets in production. Never commit .env.production to version control.
    8
    Build and deploy
    9
    # Build images
    docker-compose -f docker-compose.prod.yml build
    
    # Start services
    docker-compose -f docker-compose.prod.yml up -d
    
    # Run database migrations
    docker-compose -f docker-compose.prod.yml exec api npx prisma migrate deploy
    
    # Check status
    docker-compose -f docker-compose.prod.yml ps
    
    10
    Set up reverse proxy
    11
    Use Nginx or Caddy to handle SSL and route traffic:
    12
    # Nginx configuration
    server {
        listen 443 ssl http2;
        server_name li.mcd.ppg.br;
    
        ssl_certificate /path/to/cert.pem;
        ssl_certificate_key /path/to/key.pem;
    
        location / {
            proxy_pass http://localhost:3333;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    
    server {
        listen 443 ssl http2;
        server_name app.mcd.ppg.br;
    
        ssl_certificate /path/to/cert.pem;
        ssl_certificate_key /path/to/key.pem;
    
        location / {
            proxy_pass http://localhost:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    

    Database Migrations in Production

    Always backup your database before running migrations in production.

    Railway

    Run migrations using Railway CLI:
    railway run npx prisma migrate deploy
    

    Docker

    Run migrations in the API container:
    docker-compose exec api npx prisma migrate deploy
    

    Manual

    For direct database access:
    # Set production DATABASE_URL
    export DATABASE_URL="postgresql://..."
    
    # Deploy migrations
    cd api
    npx prisma migrate deploy
    

    Environment Variables

    Critical Production Variables

    VariableRequiredDescription
    DATABASE_URLPostgreSQL connection string
    GOOGLE_CLIENT_IDGoogle OAuth Client ID
    GOOGLE_CLIENT_SECRETGoogle OAuth Secret
    JWT_SECRETJWT signing secret (must be strong)
    BASE_URLPublic API URL (e.g., https://li.mcd.ppg.br)
    FRONTEND_URLPublic frontend URL (e.g., https://app.mcd.ppg.br)
    NODE_ENVMust be production
    PORTAPI port (default: 3333)
    MAXMIND_LICENSE_KEY⚠️Optional: for GeoIP data
    Generate a strong JWT_SECRET using a cryptographically secure random generator:
    openssl rand -base64 32
    

    Health Checks

    Monitor your deployment with health checks:
    curl https://li.mcd.ppg.br/health
    
    Expected response:
    {
      "status": "ok",
      "dbConnection": "healthy"
    }
    

    Set up monitoring

    Recommended monitoring tools:
    • Railway: Built-in metrics and logs
    • Vercel: Analytics and deployment logs
    • UptimeRobot: External uptime monitoring
    • Sentry: Error tracking (to be implemented)

    Domain Configuration

    DNS Records

    For custom domains, configure these DNS records: Frontend (Vercel):
    app.mcd.ppg.br  CNAME  cname.vercel-dns.com
    
    Backend (Railway):
    li.mcd.ppg.br   CNAME  provided-by-railway.up.railway.app
    

    SSL Certificates

    Both Vercel and Railway automatically provision and renew SSL certificates using Let’s Encrypt. For self-hosted deployments, use:
    • Certbot for Let’s Encrypt certificates
    • Caddy for automatic HTTPS

    Rollback Strategy

    Vercel

    Vercel keeps all previous deployments:
    1. Go to Deployments
    2. Find the stable version
    3. Click Promote to Production

    Railway

    Railway maintains deployment history:
    1. Go to Deployments
    2. Select a previous deployment
    3. Click Redeploy

    Docker

    Tag your images for easy rollback:
    # Tag current version
    docker tag macondo-api:latest macondo-api:v1.4.0
    
    # Rollback to specific version
    docker-compose down
    docker-compose up -d macondo-api:v1.3.0
    

    Database Backups

    Railway Automated Backups

    Railway Pro plan includes automated daily backups.

    Manual Backup

    Create manual backups:
    # Backup
    pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql
    
    # Restore
    psql $DATABASE_URL < backup_20260309_120000.sql
    

    Automated Backup Script

    #!/bin/bash
    BACKUP_DIR="/backups"
    DATABASE_URL="your_database_url"
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    
    mkdir -p $BACKUP_DIR
    pg_dump $DATABASE_URL | gzip > $BACKUP_DIR/backup_$TIMESTAMP.sql.gz
    
    # Keep only last 30 days
    find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +30 -delete
    
    Schedule with cron:
    0 2 * * * /path/to/backup.sh
    

    Performance Optimization

    Backend

    • Enable connection pooling in Prisma
    • Use Redis for caching (future enhancement)
    • Optimize database queries with indexes
    • Enable compression in Fastify

    Frontend

    • Enable Next.js static optimization
    • Use Vercel Edge Network for fast global delivery
    • Implement proper caching headers
    • Optimize images with Next.js Image component

    Database

    • Create indexes on frequently queried fields
    • Use connection pooling (PgBouncer)
    • Monitor slow queries
    • Regular VACUUM and ANALYZE

    Security Checklist

    • Strong JWT_SECRET (32+ characters, random)
    • HTTPS enabled on all domains
    • Google OAuth restricted to corporate domain
    • Environment variables not committed to Git
    • Database backups configured
    • Health check endpoint monitored
    • CORS configured correctly
    • Rate limiting enabled (future)
    • SQL injection protection (Prisma ORM)
    • XSS protection (Next.js built-in)

    Troubleshooting

    Check Railway logs:
    railway logs
    
    Common causes:
    • Database connection failure
    • Migration not run
    • Environment variables missing
    • Application crash on startup
    Verify FRONTEND_URL is set correctly in backend environment variables and matches your frontend domain exactly.
    Check:
    1. Google OAuth redirect URI matches production URL
    2. BASE_URL and FRONTEND_URL are correct
    3. Domain is properly configured with SSL
    4. Corporate domain restriction is set correctly
    Before running migrations:
    # Check database connection
    railway run npx prisma db pull
    
    # Validate schema
    railway run npx prisma validate
    
    # Deploy migrations
    railway run npx prisma migrate deploy
    

    Next Steps

    After deployment:
    • Set up monitoring and alerting
    • Configure automated backups
    • Review Database Management for ongoing maintenance
    • Plan for scaling based on usage metrics
    • Consider implementing CDN for static assets

    Build docs developers (and LLMs) love