Complete overhaul - changed to pm2-runtime and the excellent prerender-plugin-fscache. Working excellently!
This commit is contained in:
		
							parent
							
								
									85336a52f9
								
							
						
					
					
						commit
						b28d11474e
					
				
							
								
								
									
										1
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | .env | ||||||
							
								
								
									
										10
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Dockerfile
									
									
									
									
									
								
							| @ -17,15 +17,15 @@ ENV CHROME_FLAGS="--headless --disable-gpu --no-sandbox --disable-dev-shm-usage | |||||||
| 
 | 
 | ||||||
| WORKDIR /home/node | WORKDIR /home/node | ||||||
| 
 | 
 | ||||||
|  | USER root | ||||||
|  | RUN mkdir -p /home/node/prerender-cache && chown -R node:node /home/node/prerender-cache | ||||||
| RUN apt-get update && apt-get install -y \ | RUN apt-get update && apt-get install -y \ | ||||||
|     chromium \ |     chromium \ | ||||||
|     --no-install-recommends \ |     --no-install-recommends \ | ||||||
|     && rm -rf /var/lib/apt/lists/* |     && rm -rf /var/lib/apt/lists/* | ||||||
| 
 | 
 | ||||||
| # Already has git |  | ||||||
| # RUN apt-get update -y |  | ||||||
| RUN yarn add prerender prerender-memory-cache pm2 winston nodemailer |  | ||||||
| 
 |  | ||||||
| USER node:node | USER node:node | ||||||
|  | RUN yarn add prerender pm2 prerender-plugin-fscache | ||||||
| 
 | 
 | ||||||
| ENTRYPOINT ["node", "server.js"] | 
 | ||||||
|  | ENTRYPOINT ["pm2-runtime", "start", "ecosystem.config.js"] | ||||||
							
								
								
									
										340
									
								
								PM2Manager
									
									
									
									
									
								
							
							
						
						
									
										340
									
								
								PM2Manager
									
									
									
									
									
								
							| @ -1,340 +0,0 @@ | |||||||
| // To use this enhanced version, you'll need to: |  | ||||||
| 
 |  | ||||||
| // Install additional dependencies: |  | ||||||
| // npm install winston nodemailer node-fetch |  | ||||||
| 
 |  | ||||||
| // Set up environment variables: |  | ||||||
| // export NOTIFICATION_EMAIL=alerts@yourdomain.com |  | ||||||
| // export ADMIN_EMAIL=admin@yourdomain.com |  | ||||||
| // export SMTP_HOST=smtp.yourdomain.com |  | ||||||
| // export SMTP_PORT=587 |  | ||||||
| // export SMTP_USER=your-user |  | ||||||
| // export SMTP_PASS=your-password |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| const { spawn, exec } = require('child_process'); |  | ||||||
| const pm2 = require('pm2'); |  | ||||||
| const fs = require('fs'); |  | ||||||
| const path = require('path'); |  | ||||||
| const winston = require('winston'); |  | ||||||
| const nodemailer = require('nodemailer'); |  | ||||||
| 
 |  | ||||||
| // Configuration file |  | ||||||
| const config = { |  | ||||||
|     app: { |  | ||||||
|         name: 'server', |  | ||||||
|         script: 'server.js', |  | ||||||
|         instances: 4, |  | ||||||
|         max_memory_restart: '300M', |  | ||||||
|         max_restarts: 10, |  | ||||||
|         exp_backoff_restart_delay: 100 |  | ||||||
|     }, |  | ||||||
|     monitoring: { |  | ||||||
|         healthCheckInterval: 30000, |  | ||||||
|         healthCheckEndpoint: 'http://localhost:3000/health', |  | ||||||
|         metricsInterval: 60000, |  | ||||||
|         maxRestartAttempts: 5, |  | ||||||
|         criticalErrors: ['page timed out', 'parse html timed out', 'Timed out waiting for'] |  | ||||||
|     }, |  | ||||||
|     notifications: { |  | ||||||
|         email: { |  | ||||||
|             enabled: true, |  | ||||||
|             from: process.env.NOTIFICATION_EMAIL, |  | ||||||
|             to: process.env.ADMIN_EMAIL, |  | ||||||
|             smtp: { |  | ||||||
|                 host: process.env.SMTP_HOST, |  | ||||||
|                 port: process.env.SMTP_PORT, |  | ||||||
|                 auth: { |  | ||||||
|                     user: process.env.SMTP_USER, |  | ||||||
|                     pass: process.env.SMTP_PASS |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     logging: { |  | ||||||
|         dir: 'logs', |  | ||||||
|         maxSize: '10m', |  | ||||||
|         maxFiles: '7d' |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // Save config to file |  | ||||||
| fs.writeFileSync('pm2-config.json', JSON.stringify(config, null, 2)); |  | ||||||
| 
 |  | ||||||
| // Initialize logger |  | ||||||
| const logger = winston.createLogger({ |  | ||||||
|     level: 'info', |  | ||||||
|     format: winston.format.combine( |  | ||||||
|         winston.format.timestamp(), |  | ||||||
|         winston.format.json() |  | ||||||
|     ), |  | ||||||
|     transports: [ |  | ||||||
|         new winston.transports.Console(), |  | ||||||
|         new winston.transports.File({ |  | ||||||
|             filename: path.join(config.logging.dir, 'error.log'), |  | ||||||
|             level: 'error', |  | ||||||
|             maxsize: 5242880, // 5MB |  | ||||||
|             maxFiles: 5 |  | ||||||
|         }), |  | ||||||
|         new winston.transports.File({ |  | ||||||
|             filename: path.join(config.logging.dir, 'combined.log'), |  | ||||||
|             maxsize: 5242880, |  | ||||||
|             maxFiles: 5 |  | ||||||
|         }) |  | ||||||
|     ] |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| // Metrics tracking |  | ||||||
| class MetricsTracker { |  | ||||||
|     constructor() { |  | ||||||
|         this.metrics = { |  | ||||||
|             startTime: Date.now(), |  | ||||||
|             restarts: 0, |  | ||||||
|             lastRestart: null, |  | ||||||
|             uptimePercentage: 100, |  | ||||||
|             memoryUsage: [], |  | ||||||
|             healthCheckFailures: 0, |  | ||||||
|             criticalErrors: 0 |  | ||||||
|         }; |  | ||||||
|          |  | ||||||
|         // Create metrics directory if it doesn't exist |  | ||||||
|         if (!fs.existsSync('metrics')) { |  | ||||||
|             fs.mkdirSync('metrics'); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     updateMetrics(type, value) { |  | ||||||
|         switch(type) { |  | ||||||
|             case 'restart': |  | ||||||
|                 this.metrics.restarts++; |  | ||||||
|                 this.metrics.lastRestart = new Date().toISOString(); |  | ||||||
|                 break; |  | ||||||
|             case 'memory': |  | ||||||
|                 this.metrics.memoryUsage.push({ |  | ||||||
|                     timestamp: new Date().toISOString(), |  | ||||||
|                     value: value |  | ||||||
|                 }); |  | ||||||
|                 // Keep only last 100 measurements |  | ||||||
|                 if (this.metrics.memoryUsage.length > 100) { |  | ||||||
|                     this.metrics.memoryUsage.shift(); |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             case 'healthCheck': |  | ||||||
|                 if (!value) this.metrics.healthCheckFailures++; |  | ||||||
|                 break; |  | ||||||
|             case 'criticalError': |  | ||||||
|                 this.metrics.criticalErrors++; |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Calculate uptime percentage |  | ||||||
|         const totalTime = Date.now() - this.metrics.startTime; |  | ||||||
|         const downtime = this.metrics.restarts * 10000; // Assuming 10s downtime per restart |  | ||||||
|         this.metrics.uptimePercentage = ((totalTime - downtime) / totalTime * 100).toFixed(2); |  | ||||||
| 
 |  | ||||||
|         // Save metrics to file |  | ||||||
|         fs.writeFileSync( |  | ||||||
|             path.join('metrics', 'metrics.json'), |  | ||||||
|             JSON.stringify(this.metrics, null, 2) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const metrics = new MetricsTracker(); |  | ||||||
| 
 |  | ||||||
| // Notification system |  | ||||||
| const notifier = nodemailer.createTransport(config.notifications.email.smtp); |  | ||||||
| 
 |  | ||||||
| async function sendNotification(subject, message, critical = false) { |  | ||||||
|     if (!config.notifications.email.enabled) return; |  | ||||||
| 
 |  | ||||||
|     try { |  | ||||||
|         await notifier.sendMail({ |  | ||||||
|             from: config.notifications.email.from, |  | ||||||
|             to: config.notifications.email.to, |  | ||||||
|             subject: `[${critical ? 'CRITICAL' : 'INFO'}] ${subject}`, |  | ||||||
|             text: message |  | ||||||
|         }); |  | ||||||
|         logger.info(`Notification sent: ${subject}`); |  | ||||||
|     } catch (error) { |  | ||||||
|         logger.error('Failed to send notification:', error); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Health check function |  | ||||||
| async function performHealthCheck() { |  | ||||||
|     try { |  | ||||||
|         const response = await fetch(config.monitoring.healthCheckEndpoint); |  | ||||||
|         const healthy = response.status === 200; |  | ||||||
|         metrics.updateMetrics('healthCheck', healthy); |  | ||||||
|          |  | ||||||
|         if (!healthy) { |  | ||||||
|             logger.warn('Health check failed'); |  | ||||||
|             sendNotification('Health Check Failed', 'Application health check failed. Investigating...'); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         return true; |  | ||||||
|     } catch (error) { |  | ||||||
|         logger.error('Health check error:', error); |  | ||||||
|         metrics.updateMetrics('healthCheck', false); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| let yourCommand; |  | ||||||
| let isRestarting = false; |  | ||||||
| 
 |  | ||||||
| async function cleanup() { |  | ||||||
|     return new Promise(async (resolve) => { |  | ||||||
|         try { |  | ||||||
|             if (process.env.CHROME_PID) { |  | ||||||
|                 exec(`kill ${process.env.CHROME_PID}`); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (yourCommand) { |  | ||||||
|                 yourCommand.stdout.removeAllListeners('data'); |  | ||||||
|                 yourCommand.kill(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             await new Promise(resolve => pm2.disconnect(resolve)); |  | ||||||
|             logger.info('Cleanup completed successfully'); |  | ||||||
|         } catch (error) { |  | ||||||
|             logger.error('Cleanup error:', error); |  | ||||||
|         } |  | ||||||
|         resolve(); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function pm2Start() { |  | ||||||
|     if (isRestarting) { |  | ||||||
|         logger.info('Restart already in progress, skipping...'); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     try { |  | ||||||
|         isRestarting = true; |  | ||||||
|         await cleanup(); |  | ||||||
| 
 |  | ||||||
|         await new Promise((resolve, reject) => { |  | ||||||
|             pm2.connect((err) => { |  | ||||||
|                 if (err) reject(err); |  | ||||||
|                 else resolve(); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // Start the application with config |  | ||||||
|         await new Promise((resolve, reject) => { |  | ||||||
|             pm2.start(config.app, (err, apps) => { |  | ||||||
|                 if (err) reject(err); |  | ||||||
|                 else resolve(apps); |  | ||||||
|             }); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // Start monitoring processes |  | ||||||
|         yourCommand = spawn('pm2', ['log']); |  | ||||||
|          |  | ||||||
|         yourCommand.on('error', (error) => { |  | ||||||
|             logger.error('PM2 log process error:', error); |  | ||||||
|             metrics.updateMetrics('criticalError'); |  | ||||||
|             restartWithDelay(); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         yourCommand.on('exit', (code, signal) => { |  | ||||||
|             if (code !== 0) { |  | ||||||
|                 logger.error(`PM2 log process exited with code ${code}, signal: ${signal}`); |  | ||||||
|                 restartWithDelay(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         yourCommand.stdout.on('data', (data) => { |  | ||||||
|             const dataStr = data.toString(); |  | ||||||
|             if (config.monitoring.criticalErrors.some(err => dataStr.includes(err))) { |  | ||||||
|                 logger.error("Critical error detected in logs:", dataStr); |  | ||||||
|                 metrics.updateMetrics('criticalError'); |  | ||||||
|                 sendNotification( |  | ||||||
|                     'Critical Error Detected', |  | ||||||
|                     `Error in application logs: ${dataStr}`, |  | ||||||
|                     true |  | ||||||
|                 ); |  | ||||||
|                 restartWithDelay(); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // Start health check interval |  | ||||||
|         setInterval(async () => { |  | ||||||
|             const isHealthy = await performHealthCheck(); |  | ||||||
|             if (!isHealthy) restartWithDelay(); |  | ||||||
|         }, config.monitoring.healthCheckInterval); |  | ||||||
| 
 |  | ||||||
|         // Start metrics collection interval |  | ||||||
|         setInterval(() => { |  | ||||||
|             pm2.describe(config.app.name, (err, processDescription) => { |  | ||||||
|                 if (!err && processDescription[0]) { |  | ||||||
|                     metrics.updateMetrics('memory', processDescription[0].monit.memory); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         }, config.monitoring.metricsInterval); |  | ||||||
| 
 |  | ||||||
|         logger.info('Application started successfully'); |  | ||||||
|         sendNotification('Application Started', 'The application has been started successfully'); |  | ||||||
| 
 |  | ||||||
|     } catch (error) { |  | ||||||
|         logger.error('PM2 start error:', error); |  | ||||||
|         metrics.updateMetrics('criticalError'); |  | ||||||
|         sendNotification( |  | ||||||
|             'Application Start Failed', |  | ||||||
|             `Failed to start application: ${error.message}`, |  | ||||||
|             true |  | ||||||
|         ); |  | ||||||
|         await cleanup(); |  | ||||||
|         restartWithDelay(); |  | ||||||
|     } finally { |  | ||||||
|         isRestarting = false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| let restartAttempts = 0; |  | ||||||
| 
 |  | ||||||
| function restartWithDelay() { |  | ||||||
|     if (restartAttempts >= config.monitoring.maxRestartAttempts) { |  | ||||||
|         logger.error('Maximum restart attempts reached. Exiting...'); |  | ||||||
|         sendNotification( |  | ||||||
|             'Maximum Restart Attempts Reached', |  | ||||||
|             'Application has reached maximum restart attempts and will now exit.', |  | ||||||
|             true |  | ||||||
|         ); |  | ||||||
|         process.exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const delay = Math.min(1000 * Math.pow(2, restartAttempts), 30000); |  | ||||||
|     restartAttempts++; |  | ||||||
|     metrics.updateMetrics('restart'); |  | ||||||
|      |  | ||||||
|     logger.info(`Scheduling restart in ${delay}ms. Attempt ${restartAttempts}/${config.monitoring.maxRestartAttempts}`); |  | ||||||
|     setTimeout(pm2Start, delay); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Process termination handlers |  | ||||||
| process.on('SIGTERM', async () => { |  | ||||||
|     logger.info('Received SIGTERM. Cleaning up...'); |  | ||||||
|     await cleanup(); |  | ||||||
|     process.exit(0); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| process.on('SIGINT', async () => { |  | ||||||
|     logger.info('Received SIGINT. Cleaning up...'); |  | ||||||
|     await cleanup(); |  | ||||||
|     process.exit(0); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| // Create necessary directories |  | ||||||
| if (!fs.existsSync(config.logging.dir)) { |  | ||||||
|     fs.mkdirSync(config.logging.dir); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Start the application |  | ||||||
| pm2Start().catch(error => { |  | ||||||
|     logger.error('Fatal error:', error); |  | ||||||
|     sendNotification('Fatal Error', `Fatal error occurred: ${error.message}`, true); |  | ||||||
|     process.exit(1); |  | ||||||
| }); |  | ||||||
							
								
								
									
										36
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								README.md
									
									
									
									
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | # Prerender PM2 FSCache Server | ||||||
|  | 
 | ||||||
|  | This runs [prerender](https://github.com/prerender/prerender/) as a PM2 cluster, and efficiently caches the results to the filesystem using [prerender-plugin-fscache](https://github.com/PythonicCafe/prerender-plugin-fscache). | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | bikeshopi-prerender-staging  | 2025-02-21T23:20:11.550Z [fscache] Caching https://b.org/b/x9StaGQ3 to /tmp/prerender-cache/c3/c367c69bb9b5b77b4718f528902984ae23071866 | ||||||
|  | 8f528902984ae23071866 | ||||||
|  | bikeshopi-prerender-staging  | 2025-02-21T23:20:19.850Z [fscache] Serving https://b.org/b/x9StaGQ3 from /tmp/prerender-cache/c3/c367c69bb9b5b77b4718f528902984ae23071866 | ||||||
|  | ``` | ||||||
|  | `ecosystem.config.js` and `server.js` are both are mounted from your local filesystem so you can adjust as required     and restart using Docker Compose. | ||||||
|  | 
 | ||||||
|  | ## Environmental Variables for `docker-compose.yml` | ||||||
|  | ``` | ||||||
|  | IMAGE=prerender | ||||||
|  | CONTAINER_NAME=prerender | ||||||
|  | VIRTUAL_HOST=b.org | ||||||
|  | LETSENCRYPT_HOST=b.org  # Uses nginxproxy/acme-companion | ||||||
|  | PHANTOM_WORKERS=3 | ||||||
|  | CACHE_VOLUME=prerender-cache  # docker volume create prerender-cache | ||||||
|  | 
 | ||||||
|  | # optional env variables | ||||||
|  | LETSENCRYPT_TEST=false | ||||||
|  | PHANTOM_WORKERS=4 | ||||||
|  | CACHE_STATUS_CODES=200,301,302,303,304,307,308,404 | ||||||
|  | CACHE_TTL=86400  | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Prender Cache Volume | ||||||
|  | The cache is mounted on an external named volume for persistance. | ||||||
|  | 
 | ||||||
|  | ## PM2 Monit | ||||||
|  | To monitor PM2 processes, run: | ||||||
|  | ``` | ||||||
|  | docker compose exec prerender pm2 monit | ||||||
|  | ``` | ||||||
| @ -1,40 +1,54 @@ | |||||||
| # prerender server | # prerender server with pm2 and prerender-plugin-fscache (file system based so  | ||||||
|  | # resolves caching issues with pm2 and prerender-memory-cache) | ||||||
|  | # | ||||||
|  | # docker compose exec prerender pm2 monit | ||||||
| 
 | 
 | ||||||
| services: | services: | ||||||
|  | 
 | ||||||
|   prerender: |   prerender: | ||||||
|     build: . |     build: . | ||||||
|     image: ${IMAGE:-bikeshopi/prerender-staging} |     image: ${IMAGE:-prerender} | ||||||
|     container_name: ${CONTAINER_NAME:-bikeshopi-prerender-staging}  |     container_name: ${CONTAINER_NAME:-prerender}  | ||||||
|     user: node |     user: node | ||||||
|     restart: unless-stopped |     restart: unless-stopped | ||||||
|     init: true  # makes all the difference, no more timeout / restarts without chrome alive |     init: true  # makes all the difference, no more timeout (not always) / restarts without chrome alive | ||||||
|     expose: |     expose: | ||||||
|       - "3000" |       - "3000" | ||||||
|       - "9222"    |       - "9222"    | ||||||
|     environment: |     environment: | ||||||
|       # - PROXY_READ_TIMEOUT=60s |       - PATH=/home/node/node_modules/pm2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin | ||||||
|       # - PROXY_SET_HEADER_X_REAL_IP=$$remote_addr |  | ||||||
|       # - PROXY_SET_HEADER_X_FORWARDED_FOR=$$proxy_add_x_forwarded_for |  | ||||||
|       # - PROXY_SET_HEADER_HOST=$$http_host |  | ||||||
|       # - PROXY_SET_HEADER_USER_AGENT=$$http_user_agent |  | ||||||
|       - VIRTUAL_PORT=3000 |       - VIRTUAL_PORT=3000 | ||||||
|       - VIRTUAL_HOST=${VIRTUAL_HOST:-prerender-staging.bikeshopi.org} |       - VIRTUAL_HOST=${LETSENCRYPT_HOST} | ||||||
|       - LETSENCRYPT_HOST=${LETSENCRYPT_HOST:-prerender-staging.bikeshopi.org} |       - LETSENCRYPT_HOST=${LETSENCRYPT_HOST} | ||||||
|       - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL:-jr@bikeshopi.org} |       - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL} | ||||||
|       - LETSENCRYPT_TEST=${LETSENCRYPT_TEST:-false} |       - LETSENCRYPT_TEST=${LETSENCRYPT_TEST:-false} | ||||||
|       - CHROME_LOCATION=/usr/bin/chromium |       - CHROME_LOCATION=/usr/bin/chromium | ||||||
|       - PAGE_LOAD_TIMEOUT=3000  # default 20000 (20 seconds) |       - PAGE_LOAD_TIMEOUT=${PAGE_LOAD_TIMEOUT:-3000}  # default 20000 (20 seconds) | ||||||
|       # - CHROME_FLAGS=--headless --disable-gpu --no-sandbox --disable-dev-shm-usage --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 |       - NODE_ENV=production | ||||||
|       - COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-staging}      |       - PHANTOM_WORKERS=${PHANTOM_WORKERS} | ||||||
|  |       # prerender-plugin-fscache to the rescue | ||||||
|  |       - CACHE_STATUS_CODES=${CACHE_STATUS_CODES:-200,301,302,303,304,307,308,404} | ||||||
|  |       - CACHE_TTL=${CACHE_TTL:-86400} | ||||||
|  |       - CACHE_PATH=/home/node/prerender-cache | ||||||
|  |       - CACHE_VOLUME=${CACHE_VOLUME:-prerender-cache}                           | ||||||
|     networks: |     networks: | ||||||
|       letsencrypt: |       letsencrypt: | ||||||
|       default: |       default: | ||||||
|  |     entrypoint: ["pm2-runtime", "start", "ecosystem.config.js"] | ||||||
|     logging: |     logging: | ||||||
|       driver: "json-file"   |       driver: "json-file"   | ||||||
|       options: |       options: | ||||||
|         max-size: "10m" |         max-size: "10m" | ||||||
|         max-file: "3"       |         max-file: "3" | ||||||
|  |     volumes: | ||||||
|  |       - ./ecosystem.config.js:/home/node/ecosystem.config.js | ||||||
|  |       - ./server.js:/home/node/server.js | ||||||
|  |       - prerender-cache:/home/node/prerender-cache    | ||||||
| 
 | 
 | ||||||
| networks: | networks: | ||||||
|   letsencrypt: |   letsencrypt: | ||||||
|     external: true |     external: true | ||||||
|  | 
 | ||||||
|  | volumes: | ||||||
|  |   prerender-cache: | ||||||
|  |       name: ${CACHE_VOLUME} | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								ecosystem.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								ecosystem.config.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | module.exports = [{ | ||||||
|  |     script: './server.js', // Your main Prerender server
 | ||||||
|  |     name: 'prerender-app', | ||||||
|  |     cwd: '/home/node', | ||||||
|  |     exec_mode: 'cluster', | ||||||
|  |     instances: process.env.PHANTOM_WORKERS || 3, // Adjust based on your CPU cores
 | ||||||
|  | }]; | ||||||
| @ -14,7 +14,8 @@ const server = prerender({ | |||||||
|     logRequests: true, |     logRequests: true, | ||||||
|     browserDebuggingPort: 9222, |     browserDebuggingPort: 9222, | ||||||
| }); | }); | ||||||
| server.use(require('prerender-memory-cache')) | 
 | ||||||
|  | server.use(require("prerender-plugin-fscache")); // allows pm2 instances to live happily since written to filesystem
 | ||||||
| server.use(prerender.httpHeaders()); | server.use(prerender.httpHeaders()); | ||||||
| server.use(prerender.removeScriptTags()); | server.use(prerender.removeScriptTags()); | ||||||
| server.use(prerender.sendPrerenderHeader()); | server.use(prerender.sendPrerenderHeader()); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user