gentelella/docs/deployment.md

19 KiB

layout title nav_order
default Deployment Guide 6

Deployment Guide

{: .no_toc }

Complete guide to deploying Gentelella Admin Template to production environments {: .fs-6 .fw-300 }

Table of contents

{: .no_toc .text-delta }

  1. TOC {:toc}

Pre-Deployment Checklist

Build Optimization

Before deploying, ensure your build is optimized:

# Run production build
npm run build

# Analyze bundle sizes
npm run build:analyze

# Run performance optimizations
npm run optimize

# Test production build locally
npm run preview

Environment Configuration

Production Environment Variables

Create .env.production:

# API Configuration
VITE_API_URL=https://api.yoursite.com
VITE_APP_NAME=Gentelella Admin
VITE_DEBUG_MODE=false

# CDN Configuration
VITE_CDN_URL=https://cdn.yoursite.com
VITE_ASSETS_URL=https://assets.yoursite.com

# Performance Settings
VITE_PRELOAD_MODULES=charts,forms
VITE_ENABLE_SERVICE_WORKER=true

# Analytics
VITE_GA_TRACKING_ID=UA-XXXXXXXX-X
VITE_HOTJAR_ID=XXXXXXX

Build Configuration

Ensure vite.config.js has production optimizations:

export default defineConfig({
  base: '/your-app-path/', // Set if not deploying to root
  
  build: {
    // Output directory
    outDir: 'dist',
    
    // Asset directory
    assetsDir: 'assets',
    
    // Source maps for production debugging
    sourcemap: process.env.NODE_ENV === 'development',
    
    // Minification
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    
    // Chunk size warning limit
    chunkSizeWarningLimit: 1000,
    
    rollupOptions: {
      output: {
        // Manual chunk splitting for optimal loading
        manualChunks: {
          'vendor-core': ['bootstrap', '@popperjs/core'],
          'vendor-charts': ['chart.js', 'morris.js'],
          'vendor-forms': ['select2', 'tempus-dominus'],
          'vendor-tables': ['datatables.net'],
          'vendor-utils': ['dayjs', 'nprogress']
        }
      }
    }
  }
});

Static Hosting Platforms

Netlify Deployment

  1. Connect Repository

    • Push your code to GitHub/GitLab/Bitbucket
    • Connect repository in Netlify dashboard
  2. Configure Build Settings

    Build command: npm run build
    Publish directory: dist
    
  3. Environment Variables Set in Netlify dashboard under Site Settings → Environment Variables:

    VITE_API_URL=https://api.yoursite.com
    VITE_APP_NAME=Gentelella Admin
    NODE_VERSION=18
    
  4. Custom Domain

    • Add custom domain in Site Settings → Domain Management
    • Configure DNS records

Method 2: Manual Deploy

# Build the project
npm run build

# Install Netlify CLI
npm install -g netlify-cli

# Deploy to Netlify
netlify deploy --prod --dir=dist

Netlify Configuration

Create netlify.toml:

[build]
  command = "npm run build"
  publish = "dist"

[build.environment]
  NODE_VERSION = "18"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

[[headers]]
  for = "/assets/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/*.html"
  [headers.values]
    Cache-Control = "public, max-age=3600"

Vercel Deployment

Git Integration

  1. Connect Repository

    • Import project from GitHub/GitLab
    • Vercel auto-detects Vite configuration
  2. Build Configuration Vercel automatically detects these settings:

    {
      "buildCommand": "npm run build",
      "outputDirectory": "dist",
      "installCommand": "npm install"
    }
    
  3. Environment Variables Set in Vercel dashboard:

    VITE_API_URL=https://api.yoursite.com
    VITE_APP_NAME=Gentelella Admin
    

Manual Deployment

# Install Vercel CLI
npm install -g vercel

# Deploy
vercel --prod

Vercel Configuration

Create vercel.json:

{
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/static-build",
      "config": {
        "distDir": "dist"
      }
    }
  ],
  "routes": [
    {
      "handle": "filesystem"
    },
    {
      "src": "/(.*)",
      "dest": "/index.html"
    }
  ],
  "headers": [
    {
      "source": "/assets/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ]
}

GitHub Pages

GitHub Actions Deployment

Create .github/workflows/deploy.yml:

name: Deploy to GitHub Pages

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    permissions:
      contents: read
      pages: write
      id-token: write
    
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
        env:
          VITE_BASE_URL: /your-repo-name/
      
      - name: Setup Pages
        uses: actions/configure-pages@v3
      
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v2
        with:
          path: ./dist
      
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v2

Update Vite Configuration for GitHub Pages

// vite.config.js
export default defineConfig({
  base: process.env.NODE_ENV === 'production' 
    ? '/your-repo-name/' 
    : '/',
  // ... rest of configuration
});

Server Hosting

Nginx Configuration

Basic Setup

# /etc/nginx/sites-available/gentelella
server {
    listen 80;
    server_name yoursite.com www.yoursite.com;
    root /var/www/gentelella/dist;
    index index.html;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/json;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;

    # Cache static assets
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Handle SPA routing
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API proxy (if needed)
    location /api/ {
        proxy_pass http://localhost:8080/;
        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;
    }
}

SSL with Let's Encrypt

# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Get SSL certificate
sudo certbot --nginx -d yoursite.com -d www.yoursite.com

# Auto-renewal (add to crontab)
0 12 * * * /usr/bin/certbot renew --quiet

Apache Configuration

Virtual Host Setup

# /etc/apache2/sites-available/gentelella.conf
<VirtualHost *:80>
    ServerName yoursite.com
    ServerAlias www.yoursite.com
    DocumentRoot /var/www/gentelella/dist
    
    # Enable compression
    LoadModule deflate_module modules/mod_deflate.so
    <Location />
        SetOutputFilter DEFLATE
        SetEnvIfNoCase Request_URI \
            \.(?:gif|jpe?g|png)$ no-gzip dont-vary
        SetEnvIfNoCase Request_URI \
            \.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
    </Location>
    
    # Cache static assets
    <LocationMatch "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$">
        ExpiresActive On
        ExpiresDefault "access plus 1 year"
        Header append Cache-Control "public, immutable"
    </LocationMatch>
    
    # Handle SPA routing
    <Directory /var/www/gentelella/dist>
        RewriteEngine On
        RewriteBase /
        RewriteRule ^index\.html$ - [L]
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . /index.html [L]
    </Directory>
    
    ErrorLog ${APACHE_LOG_DIR}/gentelella_error.log
    CustomLog ${APACHE_LOG_DIR}/gentelella_access.log combined
</VirtualHost>

Container Deployment

Docker Setup

Dockerfile

# Build stage
FROM node:18-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Production stage
FROM nginx:alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html

# Copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Docker Nginx Configuration

# nginx.conf
events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/json;

    server {
        listen       80;
        server_name  localhost;
        root   /usr/share/nginx/html;
        index  index.html index.htm;

        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        location / {
            try_files $uri $uri/ /index.html;
        }
    }
}

Docker Compose

# docker-compose.yml
version: '3.8'
services:
  gentelella:
    build: .
    ports:
      - "80:80"
    environment:
      - NODE_ENV=production
    restart: unless-stopped

  # Optional: Add database, Redis, etc.
  database:
    image: postgres:14-alpine
    environment:
      POSTGRES_DB: gentelella
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Kubernetes Deployment

Deployment Configuration

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gentelella
  labels:
    app: gentelella
spec:
  replicas: 3
  selector:
    matchLabels:
      app: gentelella
  template:
    metadata:
      labels:
        app: gentelella
    spec:
      containers:
      - name: gentelella
        image: your-registry/gentelella:latest
        ports:
        - containerPort: 80
        env:
        - name: NODE_ENV
          value: "production"
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"

Service Configuration

# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: gentelella-service
spec:
  selector:
    app: gentelella
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

Ingress Configuration

# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gentelella-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts:
    - yoursite.com
    secretName: gentelella-tls
  rules:
  - host: yoursite.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: gentelella-service
            port:
              number: 80

CI/CD Pipelines

GitHub Actions

Complete CI/CD Pipeline

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  NODE_VERSION: '18'

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run linting
      run: npm run lint
    
    - name: Run tests
      run: npm run test
    
    - name: Build project
      run: npm run build
    
    - name: Run performance audit
      run: npm run optimize

  deploy-staging:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build for staging
      run: npm run build
      env:
        VITE_API_URL: ${{ secrets.STAGING_API_URL }}
        VITE_APP_NAME: Gentelella Admin (Staging)
    
    - name: Deploy to staging
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./dist
        destination_dir: staging

  deploy-production:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build for production
      run: npm run build
      env:
        VITE_API_URL: ${{ secrets.PRODUCTION_API_URL }}
        VITE_APP_NAME: Gentelella Admin
    
    - name: Deploy to Netlify
      uses: nwtgck/actions-netlify@v2.0
      with:
        publish-dir: './dist'
        production-branch: main
        github-token: ${{ secrets.GITHUB_TOKEN }}
        deploy-message: "Deploy from GitHub Actions"
      env:
        NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
        NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

GitLab CI/CD

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  NODE_VERSION: "18"

cache:
  paths:
    - node_modules/

test:
  stage: test
  image: node:$NODE_VERSION
  script:
    - npm ci
    - npm run lint
    - npm run test
    - npm run build

build-staging:
  stage: build
  image: node:$NODE_VERSION
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  only:
    - develop

build-production:
  stage: build
  image: node:$NODE_VERSION
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  only:
    - main

deploy-staging:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache curl
    - curl -X POST "$STAGING_WEBHOOK_URL"
  dependencies:
    - build-staging
  only:
    - develop

deploy-production:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache curl
    - curl -X POST "$PRODUCTION_WEBHOOK_URL"
  dependencies:
    - build-production
  only:
    - main

Monitoring and Maintenance

Health Checks

Basic Health Check Endpoint

// health.js
export function setupHealthCheck() {
  // Simple health check
  if (window.location.pathname === '/health') {
    document.body.innerHTML = JSON.stringify({
      status: 'healthy',
      timestamp: new Date().toISOString(),
      version: process.env.npm_package_version
    });
  }
}

Service Worker Health Check

// sw.js
self.addEventListener('message', event => {
  if (event.data && event.data.type === 'HEALTH_CHECK') {
    event.ports[0].postMessage({
      status: 'healthy',
      timestamp: new Date().toISOString()
    });
  }
});

Error Tracking

Sentry Integration

import * as Sentry from "@sentry/browser";

Sentry.init({
  dsn: process.env.VITE_SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 1.0,
});

// Custom error boundary
window.addEventListener('error', (event) => {
  Sentry.captureException(event.error);
});

window.addEventListener('unhandledrejection', (event) => {
  Sentry.captureException(event.reason);
});

Performance Monitoring

<!-- Real User Monitoring -->
<script>
  // Monitor Core Web Vitals
  import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';

  function sendToAnalytics(metric) {
    fetch('/analytics', {
      method: 'POST',
      body: JSON.stringify(metric),
      headers: {'Content-Type': 'application/json'}
    });
  }

  getCLS(sendToAnalytics);
  getFID(sendToAnalytics);
  getFCP(sendToAnalytics);
  getLCP(sendToAnalytics);
  getTTFB(sendToAnalytics);
</script>

Security Considerations

Content Security Policy

<!-- Add to index.html -->
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; 
               script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; 
               style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; 
               font-src 'self' https://fonts.gstatic.com; 
               img-src 'self' data: https:;">

Environment Secrets

# Use environment variables for sensitive data
export VITE_API_KEY="your-api-key"
export DATABASE_URL="postgresql://user:pass@host:port/db"

# Never commit .env files with secrets
echo ".env.local" >> .gitignore
echo ".env.production" >> .gitignore

HTTPS Enforcement

// Redirect HTTP to HTTPS in production
if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
  location.replace(`https:${location.href.substring(location.protocol.length)}`);
}

Troubleshooting

Common Deployment Issues

1. Build Failures

# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install

# Check Node.js version
node --version
npm --version

2. Asset Loading Issues

// Check base URL configuration
// vite.config.js
export default defineConfig({
  base: process.env.NODE_ENV === 'production' 
    ? '/your-app-path/' 
    : '/',
});

3. API Connection Issues

// Check CORS configuration
// vite.config.js
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false
      }
    }
  }
});

Next Steps

  • [Monitoring Setup]({{ site.baseurl }}/docs/monitoring/) - Set up comprehensive monitoring
  • [Security Guide]({{ site.baseurl }}/docs/security/) - Implement security best practices
  • [API Integration]({{ site.baseurl }}/docs/api-integration/) - Connect with backend APIs

{: .highlight } 💡 Pro Tip: Always test your deployment in a staging environment that mirrors production before deploying to production. Use feature flags to safely roll out new features.