Complete Node.js container implementation with multi-version support
- Add Dockerfile with AlmaLinux 9 base, Nginx reverse proxy, and PM2 - Support Node.js versions 18, 20, 22 with automated installation - Implement memory-optimized configuration (256MB minimum, 512MB recommended) - Add Memcached session storage for development environments - Create comprehensive documentation (README, USER-GUIDE, MEMORY-GUIDE, CLAUDE.md) - Include example applications (simple website and REST API) - Add Gitea CI/CD pipeline for automated multi-version builds - Provide local development script with helper utilities - Implement health monitoring, log rotation, and backup systems 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										37
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
# Git files
 | 
			
		||||
.git
 | 
			
		||||
.gitignore
 | 
			
		||||
 | 
			
		||||
# Documentation
 | 
			
		||||
README.md
 | 
			
		||||
CLAUDE.md
 | 
			
		||||
 | 
			
		||||
# Local development files
 | 
			
		||||
user/
 | 
			
		||||
instance_*
 | 
			
		||||
local-dev.sh
 | 
			
		||||
 | 
			
		||||
# Logs
 | 
			
		||||
*.log
 | 
			
		||||
logs/
 | 
			
		||||
 | 
			
		||||
# Temporary files
 | 
			
		||||
tmp/
 | 
			
		||||
temp/
 | 
			
		||||
.tmp
 | 
			
		||||
 | 
			
		||||
# IDE files
 | 
			
		||||
.vscode/
 | 
			
		||||
.idea/
 | 
			
		||||
*.swp
 | 
			
		||||
*.swo
 | 
			
		||||
 | 
			
		||||
# OS files
 | 
			
		||||
.DS_Store
 | 
			
		||||
Thumbs.db
 | 
			
		||||
 | 
			
		||||
# Node.js
 | 
			
		||||
node_modules/
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
yarn-debug.log*
 | 
			
		||||
yarn-error.log*
 | 
			
		||||
							
								
								
									
										40
									
								
								.gitea/workflows/build-push.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								.gitea/workflows/build-push.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
name: Cloud Node Container
 | 
			
		||||
run-name: ${{ gitea.actor }} pushed a change to main
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  Build-and-Push:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        nodever: [18, 20, 22]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Login to Gitea
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          registry: repo.anhonesthost.net
 | 
			
		||||
          username: ${{ secrets.CI_USER }}
 | 
			
		||||
          password: ${{ secrets.CI_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and Push Image
 | 
			
		||||
        uses: docker/build-push-action@v6
 | 
			
		||||
        with:
 | 
			
		||||
          platforms: linux/amd64
 | 
			
		||||
          push: true
 | 
			
		||||
          build-args: |
 | 
			
		||||
            NODEVER=${{ matrix.nodever }}
 | 
			
		||||
          tags: |
 | 
			
		||||
            repo.anhonesthost.net/cloud-hosting-platform/cnc:node${{ matrix.nodever }}
 | 
			
		||||
            ${{ matrix.nodever == '20' && 'repo.anhonesthost.net/cloud-hosting-platform/cnc:latest' || '' }}
 | 
			
		||||
							
								
								
									
										96
									
								
								CLAUDE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								CLAUDE.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
# CLAUDE.md
 | 
			
		||||
 | 
			
		||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
 | 
			
		||||
 | 
			
		||||
## Project Overview
 | 
			
		||||
 | 
			
		||||
Cloud Node Container (CNC) is a Docker-based Node.js hosting environment that supports multiple Node.js versions (18, 20, 22) with Nginx reverse proxy, designed for both local development and production deployment.
 | 
			
		||||
 | 
			
		||||
## Common Development Commands
 | 
			
		||||
 | 
			
		||||
### Local Development Setup
 | 
			
		||||
```bash
 | 
			
		||||
# Quick start with automated setup (creates helper scripts and default app)
 | 
			
		||||
./local-dev.sh -n local-dev
 | 
			
		||||
 | 
			
		||||
# With specific Node.js version
 | 
			
		||||
./local-dev.sh -n myproject -a 22  # Node.js 22
 | 
			
		||||
 | 
			
		||||
# Helper scripts created by local-dev.sh:
 | 
			
		||||
./instance_start    # Start container
 | 
			
		||||
./instance_stop     # Stop container
 | 
			
		||||
./instance_logs     # View container logs
 | 
			
		||||
./instance_shell    # Access container shell
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Building and Testing
 | 
			
		||||
```bash
 | 
			
		||||
# Build container locally
 | 
			
		||||
docker build -t cloud-node-container:latest .
 | 
			
		||||
 | 
			
		||||
# Build with specific Node.js version
 | 
			
		||||
docker build --build-arg NODEVER=22 -t cnc:node22 .
 | 
			
		||||
 | 
			
		||||
# Run container manually
 | 
			
		||||
docker run -d -p 80:80 -p 443:443 \
 | 
			
		||||
  -e NODEVER=20 -e environment=DEV \
 | 
			
		||||
  -e uid=$(id -u) -e user=$(whoami) -e domain=localhost \
 | 
			
		||||
  -v"$(pwd)/user":/home/$(whoami) \
 | 
			
		||||
  --name test-container cloud-node-container:latest
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Server Deployment
 | 
			
		||||
- Production git directory: `/root/whp`
 | 
			
		||||
- After `git pull`, sync web files: `rsync -av web-files/ /docker/whp/web/`
 | 
			
		||||
 | 
			
		||||
## Architecture and Key Components
 | 
			
		||||
 | 
			
		||||
### Directory Structure
 | 
			
		||||
- `/scripts/` - Container setup scripts (entrypoint, Node.js installers, nginx config)
 | 
			
		||||
- `/configs/` - Nginx, PM2, and default application configurations
 | 
			
		||||
- User applications go in `/home/$user/app/`
 | 
			
		||||
 | 
			
		||||
### Container Behavior
 | 
			
		||||
1. **Entrypoint Flow** (`scripts/entrypoint.sh`):
 | 
			
		||||
   - Creates user with specified UID
 | 
			
		||||
   - Sets up directory structure (/home/$user/app/, logs/)
 | 
			
		||||
   - Configures Nginx reverse proxy based on environment variables
 | 
			
		||||
   - In DEV mode: starts Memcached for session storage
 | 
			
		||||
   - Starts Nginx and PM2-managed Node.js application
 | 
			
		||||
 | 
			
		||||
2. **Environment Modes**:
 | 
			
		||||
   - **DEV** (`environment=DEV`): Local Memcached, automatic backups, verbose logging
 | 
			
		||||
   - **PROD** (default): Expects external services, optimized for production
 | 
			
		||||
 | 
			
		||||
3. **Node.js Version Management**:
 | 
			
		||||
   - Controlled via `NODEVER` environment variable (18, 20, 22)
 | 
			
		||||
   - Each version has dedicated install script in `/scripts/`
 | 
			
		||||
   - PM2 ecosystem configuration adapts to user paths
 | 
			
		||||
 | 
			
		||||
### Key Environment Variables
 | 
			
		||||
- `uid` (required): User ID for file permissions
 | 
			
		||||
- `user` (required): Username for container user
 | 
			
		||||
- `domain` (required): Primary domain for Nginx configuration
 | 
			
		||||
- `serveralias`: Additional domains (comma-separated)
 | 
			
		||||
- `NODEVER`: Node.js version to use (default: 20)
 | 
			
		||||
- `environment`: DEV or PROD mode
 | 
			
		||||
 | 
			
		||||
## Important Technical Details
 | 
			
		||||
 | 
			
		||||
1. **Health Check**: Available at `/ping` endpoint (Node.js app must implement)
 | 
			
		||||
2. **Logs Location**: `/home/$user/logs/nginx/` and `/home/$user/logs/nodejs/`
 | 
			
		||||
3. **Application Backups** (DEV mode): Every 30 minutes to `/home/$user/_backups/`
 | 
			
		||||
4. **Log Rotation**: Compress after 3 days, delete after 7 days
 | 
			
		||||
5. **SSL**: Self-signed certificate auto-generated, Nginx handles SSL termination
 | 
			
		||||
6. **Static Files**: Nginx serves from `/home/$user/app/public/` at `/static/` path
 | 
			
		||||
7. **Process Management**: PM2 handles Node.js application lifecycle
 | 
			
		||||
 | 
			
		||||
## Application Requirements
 | 
			
		||||
 | 
			
		||||
User applications in `/home/$user/app/` should include:
 | 
			
		||||
- `package.json` with dependencies and scripts
 | 
			
		||||
- Main application file (default: `index.js`)
 | 
			
		||||
- Optional: `ecosystem.config.js` for PM2 configuration
 | 
			
		||||
- Optional: `public/` directory for static assets
 | 
			
		||||
 | 
			
		||||
The container provides a default Express.js application with health endpoints and Memcached session support if no user application is found.
 | 
			
		||||
							
								
								
									
										41
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
FROM almalinux/9-base
 | 
			
		||||
ARG NODEVER=20
 | 
			
		||||
 | 
			
		||||
# Install repos, update, install only needed packages, clean up in one layer
 | 
			
		||||
RUN dnf install -y \
 | 
			
		||||
      https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && \
 | 
			
		||||
    dnf update -y && \
 | 
			
		||||
    dnf install -y wget procps cronie iproute nginx openssl curl && \
 | 
			
		||||
    dnf clean all && \
 | 
			
		||||
    rm -rf /var/cache/dnf /usr/share/doc /usr/share/man /usr/share/locale/* \
 | 
			
		||||
           /var/cache/yum /tmp/* /var/tmp/*
 | 
			
		||||
 | 
			
		||||
# Copy scripts into the image and set permissions
 | 
			
		||||
COPY ./scripts/ /scripts/
 | 
			
		||||
RUN chmod +x /scripts/*
 | 
			
		||||
 | 
			
		||||
# Generate self-signed cert, create needed dirs, install Node.js, clean up
 | 
			
		||||
RUN openssl req -newkey rsa:2048 -nodes -keyout /etc/pki/tls/private/localhost.key -x509 -days 3650 -subj "/CN=localhost" -out /etc/pki/tls/certs/localhost.crt && \
 | 
			
		||||
    mkdir -p /var/log/nodejs && \
 | 
			
		||||
    /scripts/install-node$NODEVER.sh && \
 | 
			
		||||
    rm -rf /tmp/*
 | 
			
		||||
 | 
			
		||||
# Install PM2 globally for process management with minimal footprint
 | 
			
		||||
RUN npm install -g pm2@latest --production && \
 | 
			
		||||
    npm cache clean --force && \
 | 
			
		||||
    rm -rf /tmp/*
 | 
			
		||||
 | 
			
		||||
# Copy configs and web files
 | 
			
		||||
COPY ./configs/nginx.conf /etc/nginx/nginx.conf
 | 
			
		||||
COPY ./configs/default.conf /etc/nginx/conf.d/default.conf
 | 
			
		||||
COPY ./configs/index.js /var/www/html/
 | 
			
		||||
COPY ./configs/package.json /var/www/html/
 | 
			
		||||
COPY ./configs/ecosystem.config.js /var/www/html/
 | 
			
		||||
 | 
			
		||||
# Set up cron job for log rotation
 | 
			
		||||
RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab
 | 
			
		||||
 | 
			
		||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
 | 
			
		||||
  CMD curl -f http://localhost:3000/ping || exit 1
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT [ "/scripts/entrypoint.sh" ]
 | 
			
		||||
							
								
								
									
										229
									
								
								MEMORY-GUIDE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								MEMORY-GUIDE.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
			
		||||
# Memory Usage Guide - Cloud Node Container
 | 
			
		||||
 | 
			
		||||
## Memory Requirements & Recommendations
 | 
			
		||||
 | 
			
		||||
### Minimum RAM Requirements
 | 
			
		||||
 | 
			
		||||
| Configuration | Minimum RAM | Recommended RAM | Use Case |
 | 
			
		||||
|---------------|-------------|-----------------|----------|
 | 
			
		||||
| **Production (Basic)** | 256MB | 512MB | Simple websites, APIs with <100 concurrent users |
 | 
			
		||||
| **Production (Standard)** | 512MB | 1GB | Standard web applications, APIs with moderate traffic |
 | 
			
		||||
| **Development** | 512MB | 1GB | Local development with all services (Memcached, logging) |
 | 
			
		||||
| **High Traffic** | 1GB | 2GB+ | Applications with >500 concurrent users or heavy processing |
 | 
			
		||||
 | 
			
		||||
### Memory Breakdown (Optimized Configuration)
 | 
			
		||||
 | 
			
		||||
| Component | Memory Usage | Notes |
 | 
			
		||||
|-----------|--------------|-------|
 | 
			
		||||
| **Base AlmaLinux 9** | ~80-120MB | Minimal base system |
 | 
			
		||||
| **Node.js Runtime** | ~30-50MB | V8 JavaScript engine |
 | 
			
		||||
| **PM2 Process Manager** | ~15-25MB | Process monitoring and management |
 | 
			
		||||
| **Nginx (Optimized)** | ~8-15MB | Single worker, limited buffers |
 | 
			
		||||
| **User Application** | ~50-200MB | Depends on application complexity |
 | 
			
		||||
| **Memcached (DEV mode)** | ~10-15MB | 32MB memory limit, actual usage varies |
 | 
			
		||||
| **System Overhead** | ~30-50MB | Logging, cron, system processes |
 | 
			
		||||
 | 
			
		||||
**Total Typical Usage:** 220-480MB
 | 
			
		||||
 | 
			
		||||
## Memory Optimizations Applied
 | 
			
		||||
 | 
			
		||||
### Container-Level Optimizations
 | 
			
		||||
 | 
			
		||||
1. **Node.js Heap Limit**: Set to 200MB via `--max-old-space-size=200`
 | 
			
		||||
2. **PM2 Memory Restart**: Applications restart at 256MB usage
 | 
			
		||||
3. **Memcached Limit**: 32MB maximum cache size
 | 
			
		||||
4. **Nginx Workers**: Single worker process for memory efficiency
 | 
			
		||||
5. **Buffer Limits**: Reduced client buffer sizes
 | 
			
		||||
6. **Log Buffering**: 2-minute flush intervals to reduce I/O
 | 
			
		||||
 | 
			
		||||
### Application-Level Recommendations
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
// Memory-efficient application practices
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
 | 
			
		||||
// Use compression to reduce memory for responses
 | 
			
		||||
app.use(require('compression')());
 | 
			
		||||
 | 
			
		||||
// Limit request size
 | 
			
		||||
app.use(express.json({ limit: '1mb' }));
 | 
			
		||||
app.use(express.urlencoded({ limit: '1mb', extended: true }));
 | 
			
		||||
 | 
			
		||||
// Stream large files instead of loading into memory
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
app.get('/large-file', (req, res) => {
 | 
			
		||||
  const stream = fs.createReadStream('large-file.pdf');
 | 
			
		||||
  stream.pipe(res);
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Memory Monitoring
 | 
			
		||||
 | 
			
		||||
### Built-in Monitoring Scripts
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Check current memory usage
 | 
			
		||||
docker exec container-name /scripts/memory-info.sh
 | 
			
		||||
 | 
			
		||||
# Monitor PM2 processes
 | 
			
		||||
docker exec container-name pm2 monit
 | 
			
		||||
 | 
			
		||||
# View memory usage over time
 | 
			
		||||
docker stats container-name
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Memory Alerts & Automatic Restarts
 | 
			
		||||
 | 
			
		||||
The container includes automatic memory management:
 | 
			
		||||
 | 
			
		||||
- **PM2 Restart**: Apps restart if they exceed 256MB
 | 
			
		||||
- **Health Checks**: Container monitors `/ping` endpoint
 | 
			
		||||
- **Process Recovery**: Failed processes automatically restart
 | 
			
		||||
- **Memory Leak Protection**: Regular restarts prevent memory buildup
 | 
			
		||||
 | 
			
		||||
## Scaling Guidelines
 | 
			
		||||
 | 
			
		||||
### When to Scale UP (More RAM):
 | 
			
		||||
 | 
			
		||||
- PM2 shows frequent memory restarts
 | 
			
		||||
- Response times increase significantly
 | 
			
		||||
- Multiple applications in one container
 | 
			
		||||
- Heavy data processing or file handling
 | 
			
		||||
- High concurrent user count (>200 simultaneous)
 | 
			
		||||
 | 
			
		||||
### When to Scale OUT (More Containers):
 | 
			
		||||
 | 
			
		||||
- CPU usage consistently >80%
 | 
			
		||||
- Memory usage consistently >80% of allocation
 | 
			
		||||
- Need for geographic distribution
 | 
			
		||||
- Fault tolerance requirements
 | 
			
		||||
 | 
			
		||||
### Memory-Efficient Application Patterns
 | 
			
		||||
 | 
			
		||||
**Good Practices:**
 | 
			
		||||
```javascript
 | 
			
		||||
// Stream processing instead of loading entire files
 | 
			
		||||
const stream = require('stream');
 | 
			
		||||
 | 
			
		||||
// Use connection pooling for databases
 | 
			
		||||
const mysql = require('mysql2');
 | 
			
		||||
const pool = mysql.createPool({
 | 
			
		||||
  host: 'localhost',
 | 
			
		||||
  user: 'user',
 | 
			
		||||
  password: 'password',
 | 
			
		||||
  database: 'db',
 | 
			
		||||
  connectionLimit: 5, // Limit connections
 | 
			
		||||
  acquireTimeout: 60000,
 | 
			
		||||
  timeout: 60000
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Clean up intervals and timeouts
 | 
			
		||||
const cleanup = () => {
 | 
			
		||||
  clearInterval(myInterval);
 | 
			
		||||
  clearTimeout(myTimeout);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
process.on('SIGTERM', cleanup);
 | 
			
		||||
process.on('SIGINT', cleanup);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Avoid These Memory Patterns:**
 | 
			
		||||
```javascript
 | 
			
		||||
// DON'T: Store large amounts in memory
 | 
			
		||||
let globalCache = {}; // Can grow unbounded
 | 
			
		||||
 | 
			
		||||
// DON'T: Create memory leaks with closures
 | 
			
		||||
setInterval(() => {
 | 
			
		||||
  const largeData = new Array(1000000).fill('data');
 | 
			
		||||
  // largeData never gets garbage collected
 | 
			
		||||
}, 1000);
 | 
			
		||||
 | 
			
		||||
// DON'T: Load entire files into memory
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const hugeFile = fs.readFileSync('huge-file.txt'); // Use streams instead
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Container Resource Limits
 | 
			
		||||
 | 
			
		||||
### Setting Memory Limits
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Limit container memory usage
 | 
			
		||||
docker run --memory=512m --memory-swap=512m your-container
 | 
			
		||||
 | 
			
		||||
# Or in docker-compose.yml
 | 
			
		||||
services:
 | 
			
		||||
  node-app:
 | 
			
		||||
    mem_limit: 512m
 | 
			
		||||
    memswap_limit: 512m
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Monitoring Memory Usage
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Real-time memory stats
 | 
			
		||||
docker exec container-name cat /proc/meminfo
 | 
			
		||||
 | 
			
		||||
# Process-specific memory
 | 
			
		||||
docker exec container-name ps aux --sort=-%mem
 | 
			
		||||
 | 
			
		||||
# Application memory usage
 | 
			
		||||
docker exec container-name pm2 show app-name
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Troubleshooting Memory Issues
 | 
			
		||||
 | 
			
		||||
### Common Memory Problems
 | 
			
		||||
 | 
			
		||||
1. **Container OOM (Out of Memory)**
 | 
			
		||||
   - **Symptom**: Container suddenly stops
 | 
			
		||||
   - **Solution**: Increase container memory or optimize application
 | 
			
		||||
 | 
			
		||||
2. **Application Memory Leaks**
 | 
			
		||||
   - **Symptom**: Memory usage steadily increases
 | 
			
		||||
   - **Solution**: Use PM2 memory restart feature, fix code leaks
 | 
			
		||||
 | 
			
		||||
3. **Slow Performance**
 | 
			
		||||
   - **Symptom**: High response times, high memory usage
 | 
			
		||||
   - **Solution**: Enable compression, optimize database queries
 | 
			
		||||
 | 
			
		||||
### Memory Debugging Tools
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# Inside container - check memory usage
 | 
			
		||||
free -h
 | 
			
		||||
cat /proc/meminfo
 | 
			
		||||
ps aux --sort=-%mem
 | 
			
		||||
 | 
			
		||||
# PM2 memory monitoring
 | 
			
		||||
pm2 monit
 | 
			
		||||
pm2 show app-name
 | 
			
		||||
 | 
			
		||||
# Node.js heap snapshots (development)
 | 
			
		||||
# Add to your app:
 | 
			
		||||
const v8 = require('v8');
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const heapSnapshot = v8.writeHeapSnapshot();
 | 
			
		||||
console.log('Heap snapshot written to', heapSnapshot);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Production Memory Recommendations
 | 
			
		||||
 | 
			
		||||
### Small Applications (< 50 concurrent users)
 | 
			
		||||
- **Container RAM**: 512MB
 | 
			
		||||
- **Node.js Heap**: 200MB (default)
 | 
			
		||||
- **Expected Usage**: 250-350MB
 | 
			
		||||
 | 
			
		||||
### Medium Applications (50-200 concurrent users)
 | 
			
		||||
- **Container RAM**: 1GB
 | 
			
		||||
- **Node.js Heap**: 400MB (`--max-old-space-size=400`)
 | 
			
		||||
- **Expected Usage**: 500-700MB
 | 
			
		||||
 | 
			
		||||
### Large Applications (200+ concurrent users)
 | 
			
		||||
- **Container RAM**: 2GB+
 | 
			
		||||
- **Node.js Heap**: 800MB+ (`--max-old-space-size=800`)
 | 
			
		||||
- **Expected Usage**: 1-1.5GB
 | 
			
		||||
- **Consider**: Multiple container instances with load balancing
 | 
			
		||||
 | 
			
		||||
The container is optimized for efficient memory usage while maintaining good performance and reliability.
 | 
			
		||||
							
								
								
									
										246
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										246
									
								
								README.md
									
									
									
									
									
								
							@@ -1,2 +1,246 @@
 | 
			
		||||
# cloud-node-container
 | 
			
		||||
# Cloud Node Container
 | 
			
		||||
 | 
			
		||||
This is a base container for running Node.js applications, supporting multiple Node.js versions (18, 20, 22). The default is Node.js 20. The container is based on AlmaLinux 9 and uses Nginx as a reverse proxy with SSL. It is designed for both development and production use.
 | 
			
		||||
 | 
			
		||||
**You must have Docker or compatible containerization software running.**
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## What's Included?
 | 
			
		||||
 | 
			
		||||
- **Multiple Node.js Versions:** 18, 20, 22 (set with `NODEVER` or `-a` flag)
 | 
			
		||||
- **PM2 Process Manager:** For production-grade Node.js application management
 | 
			
		||||
- **Nginx Reverse Proxy:** SSL-enabled reverse proxy with automatic HTTP to HTTPS redirect
 | 
			
		||||
- **Development Features:** Memcached for sessions, automatic backups, log rotation
 | 
			
		||||
- **Health Monitoring:** Built-in `/ping` endpoint for health checks
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Quick Start: Local Development with `local-dev.sh`
 | 
			
		||||
 | 
			
		||||
The easiest way to start a local development environment is with the provided `local-dev.sh` script. This script automates container setup, volume creation, log directories, and creates a sample Node.js application.
 | 
			
		||||
 | 
			
		||||
### Usage Example
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
./local-dev.sh -n local-dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Flags:**
 | 
			
		||||
- `-n`  Name of the container (required)
 | 
			
		||||
- `-p`  HTTP port (default: 80)
 | 
			
		||||
- `-s`  HTTPS port (default: 443)
 | 
			
		||||
- `-r`  Root path for files (default: current directory)
 | 
			
		||||
- `-a`  Node.js version (default: 20; options: 18, 20, 22)
 | 
			
		||||
- `-v`  Enable verbose mode
 | 
			
		||||
- `-h`  Show help
 | 
			
		||||
 | 
			
		||||
The script will:
 | 
			
		||||
- Create a user directory and log folders
 | 
			
		||||
- Create a default Node.js Express application if none exists
 | 
			
		||||
- Start the container with the correct environment variables
 | 
			
		||||
- Generate helper scripts in your root path:
 | 
			
		||||
  - `instance_start` – Start the container
 | 
			
		||||
  - `instance_stop` – Stop the container
 | 
			
		||||
  - `instance_logs` – View container logs
 | 
			
		||||
  - `instance_shell` – Access container shell
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Manual Docker Usage
 | 
			
		||||
 | 
			
		||||
You can also run the container manually:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
mkdir -p local-development/domain.tld
 | 
			
		||||
cd local-development/domain.tld
 | 
			
		||||
mkdir user
 | 
			
		||||
mkdir -p user/{app,logs/{nginx,nodejs}}
 | 
			
		||||
docker run -d -p 80:80 -p 443:443 -e NODEVER=20 -e environment=DEV --mount type=bind,source="$(pwd)"/user,target=/home/$(whoami) -e uid=$(id -u) -e user=$(whoami) -e domain=localhost --name local-dev cloud-node-container:latest
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Accessing the Container
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
docker exec -it local-dev /bin/bash
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## For Users: How to Deploy Your Application
 | 
			
		||||
 | 
			
		||||
### Step 1: Prepare Your Node.js Application
 | 
			
		||||
 | 
			
		||||
Your Node.js application needs just two files to get started:
 | 
			
		||||
 | 
			
		||||
**Required Files:**
 | 
			
		||||
- `package.json` - Describes your app and its dependencies
 | 
			
		||||
- A main JavaScript file (usually `server.js` or `index.js`)
 | 
			
		||||
 | 
			
		||||
**Optional Files:**
 | 
			
		||||
- `public/` folder for static files (HTML, CSS, images)
 | 
			
		||||
- `ecosystem.config.js` for advanced PM2 configuration
 | 
			
		||||
 | 
			
		||||
### Step 2: What Users Need to Do
 | 
			
		||||
 | 
			
		||||
**If you're not familiar with Node.js, here's what you need to know:**
 | 
			
		||||
 | 
			
		||||
1. **Create a `package.json` file** - This tells Node.js about your app:
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "name": "my-website",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "main": "server.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "node server.js"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "express": "^4.18.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
2. **Create your main server file** - This runs your application:
 | 
			
		||||
```javascript
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
const port = process.env.PORT || 3000;
 | 
			
		||||
 | 
			
		||||
app.use(express.static('public')); // Serve static files
 | 
			
		||||
 | 
			
		||||
app.get('/', (req, res) => {
 | 
			
		||||
  res.send('<h1>My Website is Running!</h1>');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/ping', (req, res) => {
 | 
			
		||||
  res.json({ status: 'ok' }); // Health check for the container
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.listen(port, () => {
 | 
			
		||||
  console.log(`Server running on port ${port}`);
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
3. **Put your files in the right place**:
 | 
			
		||||
   - Copy your `package.json` and `server.js` to `user/app/`
 | 
			
		||||
   - Put HTML, CSS, images in `user/app/public/`
 | 
			
		||||
 | 
			
		||||
4. **Start the container**:
 | 
			
		||||
```bash
 | 
			
		||||
./local-dev.sh -n my-app
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**That's it!** The container will:
 | 
			
		||||
- Install your dependencies automatically
 | 
			
		||||
- Start your application with PM2
 | 
			
		||||
- Handle SSL and reverse proxy
 | 
			
		||||
- Provide health monitoring
 | 
			
		||||
 | 
			
		||||
### Step 3: Example Applications
 | 
			
		||||
 | 
			
		||||
See the `examples/` directory for complete working examples:
 | 
			
		||||
 | 
			
		||||
**Simple Website** (`examples/simple-website/`):
 | 
			
		||||
- Static HTML pages with navigation
 | 
			
		||||
- CSS styling
 | 
			
		||||
- Express.js server for routing
 | 
			
		||||
 | 
			
		||||
**API Server** (`examples/api-server/`):
 | 
			
		||||
- REST API with CRUD operations
 | 
			
		||||
- JSON responses
 | 
			
		||||
- In-memory data storage
 | 
			
		||||
 | 
			
		||||
### What Happens Automatically
 | 
			
		||||
 | 
			
		||||
When you place your app in `user/app/`, the container automatically:
 | 
			
		||||
1. Runs `npm install` to install dependencies
 | 
			
		||||
2. Starts your app with PM2 process manager
 | 
			
		||||
3. Sets up Nginx reverse proxy with SSL
 | 
			
		||||
4. Configures logging and health checks
 | 
			
		||||
5. Starts Memcached for session storage (in DEV mode)
 | 
			
		||||
 | 
			
		||||
### Troubleshooting for New Users
 | 
			
		||||
 | 
			
		||||
- **App won't start?** Check that your `package.json` has a valid "start" script
 | 
			
		||||
- **Can't access your site?** Make sure your app listens on port 3000 and has a `/ping` endpoint
 | 
			
		||||
- **Dependencies missing?** List all required packages in your `package.json` dependencies section
 | 
			
		||||
- **Static files not loading?** Put them in a `public/` folder and use `app.use(express.static('public'))`
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
- **Multiple Node.js Versions:** 18, 20, 22 (set with `NODEVER` environment variable)
 | 
			
		||||
- **Process Management:** PM2 for production-grade Node.js application management
 | 
			
		||||
- **Reverse Proxy:** Nginx handles SSL termination and proxies requests to Node.js
 | 
			
		||||
- **Automatic Backups:** Application files backed up every 30 minutes in DEV mode
 | 
			
		||||
- **Log Management:** Log rotation compresses logs older than 3 days, deletes after 7 days
 | 
			
		||||
- **Session Storage:** Memcached available in DEV mode for session management
 | 
			
		||||
- **SSL:** Self-signed certificate enabled by default with automatic HTTP→HTTPS redirect
 | 
			
		||||
- **Health Checks:** `/ping`, `/info` endpoints for monitoring
 | 
			
		||||
- **Static Files:** Nginx serves static files from `/home/$user/app/public/`
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Environment Variables
 | 
			
		||||
 | 
			
		||||
**Required:**
 | 
			
		||||
- `uid` – User ID for file permissions
 | 
			
		||||
- `user` – Username for file permissions
 | 
			
		||||
- `domain` – Primary domain for configuration
 | 
			
		||||
 | 
			
		||||
**Optional:**
 | 
			
		||||
- `environment` – Set to `DEV` to start Redis and enable development features
 | 
			
		||||
- `serveralias` – Comma-separated list of alternative hostnames
 | 
			
		||||
- `NODEVER` – Node.js version (18, 20, 22)
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Default Application Endpoints
 | 
			
		||||
 | 
			
		||||
The default Express.js application provides:
 | 
			
		||||
- `/` – Basic application info and status
 | 
			
		||||
- `/ping` – Health check endpoint (JSON response)
 | 
			
		||||
- `/info` – Detailed system information
 | 
			
		||||
- `/session` – Session demo endpoint (shows Memcached sessions working)
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Helpful Notes
 | 
			
		||||
 | 
			
		||||
- To restart the instance: `./instance_start` or `docker start {container-name}`
 | 
			
		||||
- To stop: `./instance_stop` or `docker stop {container-name}`
 | 
			
		||||
- To view logs: `./instance_logs` or `docker logs -f {container-name}`
 | 
			
		||||
- To access shell: `./instance_shell` or `docker exec -it {container-name} /bin/bash`
 | 
			
		||||
- To delete a container: `docker rm {container-name}` (does not delete user files)
 | 
			
		||||
- Application logs are in `/home/$user/logs/nodejs/`
 | 
			
		||||
- Nginx logs are in `/home/$user/logs/nginx/`
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## System Requirements
 | 
			
		||||
 | 
			
		||||
### Memory Requirements
 | 
			
		||||
- **Minimum RAM**: 256MB (basic applications)
 | 
			
		||||
- **Recommended RAM**: 512MB (standard applications)  
 | 
			
		||||
- **Development**: 512MB-1GB (with all services enabled)
 | 
			
		||||
 | 
			
		||||
The container is optimized for memory efficiency with automatic memory management and process restarts. See `MEMORY-GUIDE.md` for detailed memory optimization information.
 | 
			
		||||
 | 
			
		||||
### Performance Features
 | 
			
		||||
- Automatic application restart at 256MB memory usage
 | 
			
		||||
- V8 heap limited to 200MB by default
 | 
			
		||||
- Nginx optimized for single-worker, low-memory operation
 | 
			
		||||
- Memcached limited to 32MB cache size
 | 
			
		||||
 | 
			
		||||
## Troubleshooting
 | 
			
		||||
 | 
			
		||||
- The first run may take a few minutes as Node.js and dependencies are installed
 | 
			
		||||
- If you need to change Node.js version, stop and remove the container, then recreate with the desired version
 | 
			
		||||
- For custom applications, ensure your `package.json` has a valid start script
 | 
			
		||||
- Check `/home/$user/logs/nodejs/error.log` for application errors
 | 
			
		||||
- The health check endpoint `/ping` should return a 200 status when the application is running properly
 | 
			
		||||
- **Memory issues**: Run `/scripts/memory-info.sh` inside container to check memory usage
 | 
			
		||||
- **Process monitoring**: Use `pm2 monit` to watch application performance
 | 
			
		||||
							
								
								
									
										275
									
								
								USER-GUIDE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								USER-GUIDE.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,275 @@
 | 
			
		||||
# User Guide: Deploying Applications with Cloud Node Container
 | 
			
		||||
 | 
			
		||||
## Quick Start (For Non-Node.js Users)
 | 
			
		||||
 | 
			
		||||
If you've never used Node.js before, don't worry! This container makes it simple.
 | 
			
		||||
 | 
			
		||||
### What You Need to Know
 | 
			
		||||
 | 
			
		||||
**Node.js** is like PHP, but for JavaScript. Instead of Apache serving PHP files, your JavaScript code runs as a server application.
 | 
			
		||||
 | 
			
		||||
**Express.js** is like a web framework (similar to Laravel for PHP) that makes it easy to handle web requests.
 | 
			
		||||
 | 
			
		||||
### Basic Workflow Comparison
 | 
			
		||||
 | 
			
		||||
| PHP/Apache Workflow | Node.js Container Workflow |
 | 
			
		||||
|---------------------|---------------------------|
 | 
			
		||||
| Put `.php` files in web directory | Put `.js` files in `user/app/` |
 | 
			
		||||
| Apache serves files directly | Express.js handles requests |
 | 
			
		||||
| Database connection in each file | Server runs continuously |
 | 
			
		||||
| `<?php echo "Hello"; ?>` | `res.send("Hello")` |
 | 
			
		||||
 | 
			
		||||
## Step-by-Step: Deploy Your First App
 | 
			
		||||
 | 
			
		||||
### 1. Create Your Application Files
 | 
			
		||||
 | 
			
		||||
Create these two files in your `user/app/` directory:
 | 
			
		||||
 | 
			
		||||
**File: `package.json`**
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "name": "my-first-app",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "main": "server.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "node server.js"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "express": "^4.18.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**File: `server.js`**
 | 
			
		||||
```javascript
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
const port = process.env.PORT || 3000;
 | 
			
		||||
 | 
			
		||||
// Serve static files (HTML, CSS, images) from 'public' folder
 | 
			
		||||
app.use(express.static('public'));
 | 
			
		||||
 | 
			
		||||
// Route for home page
 | 
			
		||||
app.get('/', (req, res) => {
 | 
			
		||||
    res.send(`
 | 
			
		||||
        <h1>Welcome to My Website!</h1>
 | 
			
		||||
        <p>This is running on Node.js</p>
 | 
			
		||||
        <a href="/about">About Page</a>
 | 
			
		||||
    `);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Route for about page
 | 
			
		||||
app.get('/about', (req, res) => {
 | 
			
		||||
    res.send(`
 | 
			
		||||
        <h1>About Us</h1>
 | 
			
		||||
        <p>This website is powered by Node.js and Express</p>
 | 
			
		||||
        <a href="/">Back to Home</a>
 | 
			
		||||
    `);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Health check (required by container)
 | 
			
		||||
app.get('/ping', (req, res) => {
 | 
			
		||||
    res.json({ status: 'ok', timestamp: new Date() });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Start the server
 | 
			
		||||
app.listen(port, () => {
 | 
			
		||||
    console.log(`Server running on port ${port}`);
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2. Start Your Container
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
./local-dev.sh -n my-first-app
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3. Access Your Site
 | 
			
		||||
 | 
			
		||||
Visit `https://localhost` (accept the SSL warning) and you'll see your website!
 | 
			
		||||
 | 
			
		||||
## Common Use Cases
 | 
			
		||||
 | 
			
		||||
### Static Website (Like HTML/CSS sites)
 | 
			
		||||
 | 
			
		||||
Put your HTML files in `user/app/public/` and use this simple server:
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
 | 
			
		||||
app.use(express.static('public'));
 | 
			
		||||
app.get('/ping', (req, res) => res.json({ status: 'ok' }));
 | 
			
		||||
 | 
			
		||||
app.listen(3000, () => console.log('Static site running'));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Form Processing (Like PHP forms)
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
 | 
			
		||||
app.use(express.urlencoded({ extended: true })); // Process form data
 | 
			
		||||
app.use(express.static('public'));
 | 
			
		||||
 | 
			
		||||
// Show contact form
 | 
			
		||||
app.get('/contact', (req, res) => {
 | 
			
		||||
    res.send(`
 | 
			
		||||
        <form method="POST" action="/contact">
 | 
			
		||||
            <input name="name" placeholder="Your Name" required>
 | 
			
		||||
            <input name="email" type="email" placeholder="Email" required>
 | 
			
		||||
            <textarea name="message" placeholder="Message" required></textarea>
 | 
			
		||||
            <button type="submit">Send</button>
 | 
			
		||||
        </form>
 | 
			
		||||
    `);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Process form submission
 | 
			
		||||
app.post('/contact', (req, res) => {
 | 
			
		||||
    const { name, email, message } = req.body;
 | 
			
		||||
    
 | 
			
		||||
    // Here you would save to database, send email, etc.
 | 
			
		||||
    console.log('Contact form:', { name, email, message });
 | 
			
		||||
    
 | 
			
		||||
    res.send(`<h1>Thanks ${name}!</h1><p>We received your message.</p>`);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/ping', (req, res) => res.json({ status: 'ok' }));
 | 
			
		||||
app.listen(3000);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Database Connection (Like MySQL in PHP)
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
 | 
			
		||||
// In package.json dependencies, add: "mysql2": "^3.6.0"
 | 
			
		||||
const mysql = require('mysql2');
 | 
			
		||||
 | 
			
		||||
const db = mysql.createConnection({
 | 
			
		||||
    host: 'localhost',
 | 
			
		||||
    user: 'your_user',
 | 
			
		||||
    password: 'your_password',
 | 
			
		||||
    database: 'your_database'
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/users', (req, res) => {
 | 
			
		||||
    db.query('SELECT * FROM users', (err, results) => {
 | 
			
		||||
        if (err) {
 | 
			
		||||
            res.status(500).json({ error: err.message });
 | 
			
		||||
        } else {
 | 
			
		||||
            res.json(results);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/ping', (req, res) => res.json({ status: 'ok' }));
 | 
			
		||||
app.listen(3000);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## File Structure Examples
 | 
			
		||||
 | 
			
		||||
### Simple Website
 | 
			
		||||
```
 | 
			
		||||
user/app/
 | 
			
		||||
├── package.json
 | 
			
		||||
├── server.js
 | 
			
		||||
└── public/
 | 
			
		||||
    ├── index.html
 | 
			
		||||
    ├── about.html
 | 
			
		||||
    ├── style.css
 | 
			
		||||
    └── images/
 | 
			
		||||
        └── logo.png
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### API Application
 | 
			
		||||
```
 | 
			
		||||
user/app/
 | 
			
		||||
├── package.json
 | 
			
		||||
├── server.js
 | 
			
		||||
├── routes/
 | 
			
		||||
│   ├── users.js
 | 
			
		||||
│   └── products.js
 | 
			
		||||
└── public/
 | 
			
		||||
    └── api-docs.html
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Key Differences from PHP
 | 
			
		||||
 | 
			
		||||
| Concept | PHP | Node.js |
 | 
			
		||||
|---------|-----|---------|
 | 
			
		||||
| **File serving** | Apache serves .php files directly | Express routes handle requests |
 | 
			
		||||
| **Variables** | `$variable` | `let variable` |
 | 
			
		||||
| **Arrays** | `$arr = array()` | `let arr = []` |
 | 
			
		||||
| **Echo/Print** | `echo "Hello"` | `res.send("Hello")` |
 | 
			
		||||
| **Include files** | `include 'file.php'` | `require('./file.js')` |
 | 
			
		||||
| **Database** | Connect in each file | Connect once, reuse connection |
 | 
			
		||||
| **Sessions** | `$_SESSION` | `req.session` (with middleware) |
 | 
			
		||||
| **Forms** | `$_POST` | `req.body` (with middleware) |
 | 
			
		||||
 | 
			
		||||
## Troubleshooting
 | 
			
		||||
 | 
			
		||||
### Common Issues for PHP Users
 | 
			
		||||
 | 
			
		||||
**Issue:** "Cannot GET /" error
 | 
			
		||||
**Solution:** Make sure you have an `app.get('/', ...)` route defined
 | 
			
		||||
 | 
			
		||||
**Issue:** Static files (CSS/images) not loading  
 | 
			
		||||
**Solution:** Put them in `public/` folder and add `app.use(express.static('public'))`
 | 
			
		||||
 | 
			
		||||
**Issue:** Forms not working
 | 
			
		||||
**Solution:** Add `app.use(express.urlencoded({ extended: true }))` before your routes
 | 
			
		||||
 | 
			
		||||
**Issue:** App crashes on restart
 | 
			
		||||
**Solution:** PM2 will automatically restart it, but check logs in `user/logs/nodejs/`
 | 
			
		||||
 | 
			
		||||
### Getting Help
 | 
			
		||||
 | 
			
		||||
- Check container logs: `./instance_logs`
 | 
			
		||||
- Check your app specifically: `docker exec -it your-container-name pm2 logs`
 | 
			
		||||
- Access container shell: `./instance_shell`
 | 
			
		||||
- Health check: Visit `/ping` to see if your app is responding
 | 
			
		||||
 | 
			
		||||
## Advanced Features
 | 
			
		||||
 | 
			
		||||
### Using Sessions (like PHP $_SESSION)
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
// Add to package.json dependencies: "express-session": "^1.17.3"
 | 
			
		||||
const session = require('express-session');
 | 
			
		||||
 | 
			
		||||
app.use(session({
 | 
			
		||||
    secret: 'your-secret-key',
 | 
			
		||||
    resave: false,
 | 
			
		||||
    saveUninitialized: false
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
app.get('/login', (req, res) => {
 | 
			
		||||
    req.session.user = 'john_doe';
 | 
			
		||||
    res.send('Logged in!');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/profile', (req, res) => {
 | 
			
		||||
    if (req.session.user) {
 | 
			
		||||
        res.send(`Hello ${req.session.user}!`);
 | 
			
		||||
    } else {
 | 
			
		||||
        res.send('Please log in');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### File Uploads
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
// Add to package.json: "multer": "^1.4.5-lts.1"
 | 
			
		||||
const multer = require('multer');
 | 
			
		||||
const upload = multer({ dest: 'uploads/' });
 | 
			
		||||
 | 
			
		||||
app.post('/upload', upload.single('file'), (req, res) => {
 | 
			
		||||
    res.send(`File uploaded: ${req.file.originalname}`);
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The container handles all the complex server setup, so you can focus on building your application!
 | 
			
		||||
							
								
								
									
										31
									
								
								configs/ecosystem.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								configs/ecosystem.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
  apps: [{
 | 
			
		||||
    name: 'node-app',
 | 
			
		||||
    script: 'index.js',
 | 
			
		||||
    instances: 1,
 | 
			
		||||
    autorestart: true,
 | 
			
		||||
    watch: false,
 | 
			
		||||
    max_memory_restart: '256M', // Restart if app uses more than 256MB
 | 
			
		||||
    kill_timeout: 3000,
 | 
			
		||||
    wait_ready: true,
 | 
			
		||||
    listen_timeout: 3000,
 | 
			
		||||
    env: {
 | 
			
		||||
      NODE_ENV: 'development',
 | 
			
		||||
      PORT: 3000,
 | 
			
		||||
      NODE_OPTIONS: '--max-old-space-size=200' // Limit V8 heap to 200MB
 | 
			
		||||
    },
 | 
			
		||||
    env_production: {
 | 
			
		||||
      NODE_ENV: 'production',
 | 
			
		||||
      PORT: 3000,
 | 
			
		||||
      NODE_OPTIONS: '--max-old-space-size=200'
 | 
			
		||||
    },
 | 
			
		||||
    log_file: '/home/myuser/logs/nodejs/app.log',
 | 
			
		||||
    error_file: '/home/myuser/logs/nodejs/error.log',
 | 
			
		||||
    out_file: '/home/myuser/logs/nodejs/out.log',
 | 
			
		||||
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
 | 
			
		||||
    log_type: 'json',
 | 
			
		||||
    merge_logs: true,
 | 
			
		||||
    max_restarts: 5,
 | 
			
		||||
    min_uptime: '10s'
 | 
			
		||||
  }]
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										101
									
								
								configs/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								configs/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const session = require('express-session');
 | 
			
		||||
const app = express();
 | 
			
		||||
const port = process.env.PORT || 3000;
 | 
			
		||||
 | 
			
		||||
// Middleware
 | 
			
		||||
app.use(express.json());
 | 
			
		||||
app.use(express.static('public'));
 | 
			
		||||
 | 
			
		||||
// Session configuration with Memcache (only in DEV mode when memcached is available)
 | 
			
		||||
if (process.env.NODE_ENV !== 'production') {
 | 
			
		||||
  try {
 | 
			
		||||
    const MemcachedStore = require('connect-memcached')(session);
 | 
			
		||||
    app.use(session({
 | 
			
		||||
      store: new MemcachedStore({
 | 
			
		||||
        hosts: ['localhost:11211']
 | 
			
		||||
      }),
 | 
			
		||||
      secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production',
 | 
			
		||||
      resave: false,
 | 
			
		||||
      saveUninitialized: false,
 | 
			
		||||
      cookie: { 
 | 
			
		||||
        secure: false, // Allow HTTP in development
 | 
			
		||||
        maxAge: 24 * 60 * 60 * 1000 // 24 hours
 | 
			
		||||
      }
 | 
			
		||||
    }));
 | 
			
		||||
    console.log('Memcached session store initialized');
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.log('Memcached not available, using memory store');
 | 
			
		||||
    app.use(session({
 | 
			
		||||
      secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production',
 | 
			
		||||
      resave: false,
 | 
			
		||||
      saveUninitialized: false,
 | 
			
		||||
      cookie: { 
 | 
			
		||||
        secure: false,
 | 
			
		||||
        maxAge: 24 * 60 * 60 * 1000
 | 
			
		||||
      }
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
} else {
 | 
			
		||||
  // Production session configuration (expects external session store)
 | 
			
		||||
  app.use(session({
 | 
			
		||||
    secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production',
 | 
			
		||||
    resave: false,
 | 
			
		||||
    saveUninitialized: false,
 | 
			
		||||
    cookie: { 
 | 
			
		||||
      secure: true, // HTTPS only in production
 | 
			
		||||
      maxAge: 24 * 60 * 60 * 1000
 | 
			
		||||
    }
 | 
			
		||||
  }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Health check endpoint
 | 
			
		||||
app.get('/ping', (req, res) => {
 | 
			
		||||
  res.json({ 
 | 
			
		||||
    status: 'ok', 
 | 
			
		||||
    timestamp: new Date().toISOString(),
 | 
			
		||||
    uptime: process.uptime(),
 | 
			
		||||
    version: process.env.npm_package_version || '1.0.0'
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Default route
 | 
			
		||||
app.get('/', (req, res) => {
 | 
			
		||||
  res.json({
 | 
			
		||||
    message: 'Cloud Node Container is running!',
 | 
			
		||||
    nodeVersion: process.version,
 | 
			
		||||
    environment: process.env.NODE_ENV || 'development',
 | 
			
		||||
    timestamp: new Date().toISOString()
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Info endpoint
 | 
			
		||||
app.get('/info', (req, res) => {
 | 
			
		||||
  res.json({
 | 
			
		||||
    nodeVersion: process.version,
 | 
			
		||||
    platform: process.platform,
 | 
			
		||||
    arch: process.arch,
 | 
			
		||||
    uptime: process.uptime(),
 | 
			
		||||
    memory: process.memoryUsage(),
 | 
			
		||||
    env: process.env.NODE_ENV || 'development'
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Session demo endpoint
 | 
			
		||||
app.get('/session', (req, res) => {
 | 
			
		||||
  if (!req.session.visits) {
 | 
			
		||||
    req.session.visits = 0;
 | 
			
		||||
  }
 | 
			
		||||
  req.session.visits++;
 | 
			
		||||
  
 | 
			
		||||
  res.json({
 | 
			
		||||
    sessionId: req.sessionID,
 | 
			
		||||
    visits: req.session.visits,
 | 
			
		||||
    message: 'Session is working with ' + (process.env.NODE_ENV !== 'production' ? 'Memcached' : 'default store')
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.listen(port, () => {
 | 
			
		||||
  console.log(`Server running on port ${port}`);
 | 
			
		||||
  console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										45
									
								
								configs/nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								configs/nginx.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
user nginx;
 | 
			
		||||
worker_processes 1; # Single worker for memory efficiency
 | 
			
		||||
worker_rlimit_nofile 1024;
 | 
			
		||||
error_log /var/log/nginx/error.log warn; # Less verbose logging
 | 
			
		||||
pid /run/nginx.pid;
 | 
			
		||||
 | 
			
		||||
events {
 | 
			
		||||
    worker_connections 512; # Reduced for memory efficiency
 | 
			
		||||
    use epoll;
 | 
			
		||||
    multi_accept on;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
http {
 | 
			
		||||
    # Memory-optimized settings
 | 
			
		||||
    client_body_buffer_size 16k;
 | 
			
		||||
    client_header_buffer_size 1k;
 | 
			
		||||
    client_max_body_size 8m;
 | 
			
		||||
    large_client_header_buffers 2 1k;
 | 
			
		||||
    
 | 
			
		||||
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
 | 
			
		||||
                      '$status $body_bytes_sent "$http_referer" '
 | 
			
		||||
                      '"$http_user_agent" "$http_x_forwarded_for"';
 | 
			
		||||
 | 
			
		||||
    access_log  /var/log/nginx/access.log  main buffer=16k flush=2m;
 | 
			
		||||
 | 
			
		||||
    sendfile            on;
 | 
			
		||||
    tcp_nopush          on;
 | 
			
		||||
    tcp_nodelay         on;
 | 
			
		||||
    keepalive_timeout   30; # Reduced from 65
 | 
			
		||||
    keepalive_requests  100;
 | 
			
		||||
    types_hash_max_size 1024; # Reduced from 2048
 | 
			
		||||
    server_tokens       off;
 | 
			
		||||
 | 
			
		||||
    include             /etc/nginx/mime.types;
 | 
			
		||||
    default_type        application/octet-stream;
 | 
			
		||||
 | 
			
		||||
    # Optimized gzip compression
 | 
			
		||||
    gzip on;
 | 
			
		||||
    gzip_vary on;
 | 
			
		||||
    gzip_min_length 1024;
 | 
			
		||||
    gzip_comp_level 2; # Lower compression for less CPU/memory usage
 | 
			
		||||
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/conf.d/*.conf;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								configs/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								configs/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "cloud-node-container",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "Default Node.js application for Cloud Node Container",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "node index.js",
 | 
			
		||||
    "dev": "nodemon index.js",
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "express": "^4.18.2",
 | 
			
		||||
    "express-session": "^1.17.3",
 | 
			
		||||
    "connect-memcached": "^1.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "nodemon": "^3.0.1"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "nodejs",
 | 
			
		||||
    "express",
 | 
			
		||||
    "container",
 | 
			
		||||
    "docker"
 | 
			
		||||
  ],
 | 
			
		||||
  "author": "",
 | 
			
		||||
  "license": "MIT"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								examples/api-server/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/api-server/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "simple-api",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "A simple REST API server",
 | 
			
		||||
  "main": "server.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "node server.js"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "express": "^4.18.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								examples/api-server/server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								examples/api-server/server.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const app = express();
 | 
			
		||||
const port = process.env.PORT || 3000;
 | 
			
		||||
 | 
			
		||||
// Middleware
 | 
			
		||||
app.use(express.json());
 | 
			
		||||
 | 
			
		||||
// In-memory data store (in production, you'd use a database)
 | 
			
		||||
let users = [
 | 
			
		||||
  { id: 1, name: 'John Doe', email: 'john@example.com' },
 | 
			
		||||
  { id: 2, name: 'Jane Smith', email: 'jane@example.com' }
 | 
			
		||||
];
 | 
			
		||||
let nextId = 3;
 | 
			
		||||
 | 
			
		||||
// Health check endpoint
 | 
			
		||||
app.get('/ping', (req, res) => {
 | 
			
		||||
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// API Routes
 | 
			
		||||
app.get('/api/users', (req, res) => {
 | 
			
		||||
  res.json(users);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/api/users/:id', (req, res) => {
 | 
			
		||||
  const user = users.find(u => u.id === parseInt(req.params.id));
 | 
			
		||||
  if (!user) {
 | 
			
		||||
    return res.status(404).json({ error: 'User not found' });
 | 
			
		||||
  }
 | 
			
		||||
  res.json(user);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.post('/api/users', (req, res) => {
 | 
			
		||||
  const { name, email } = req.body;
 | 
			
		||||
  if (!name || !email) {
 | 
			
		||||
    return res.status(400).json({ error: 'Name and email are required' });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  const user = { id: nextId++, name, email };
 | 
			
		||||
  users.push(user);
 | 
			
		||||
  res.status(201).json(user);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.put('/api/users/:id', (req, res) => {
 | 
			
		||||
  const user = users.find(u => u.id === parseInt(req.params.id));
 | 
			
		||||
  if (!user) {
 | 
			
		||||
    return res.status(404).json({ error: 'User not found' });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  const { name, email } = req.body;
 | 
			
		||||
  if (name) user.name = name;
 | 
			
		||||
  if (email) user.email = email;
 | 
			
		||||
  
 | 
			
		||||
  res.json(user);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.delete('/api/users/:id', (req, res) => {
 | 
			
		||||
  const index = users.findIndex(u => u.id === parseInt(req.params.id));
 | 
			
		||||
  if (index === -1) {
 | 
			
		||||
    return res.status(404).json({ error: 'User not found' });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  users.splice(index, 1);
 | 
			
		||||
  res.status(204).send();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Basic documentation endpoint
 | 
			
		||||
app.get('/', (req, res) => {
 | 
			
		||||
  res.json({
 | 
			
		||||
    message: 'Simple API Server',
 | 
			
		||||
    endpoints: {
 | 
			
		||||
      'GET /api/users': 'Get all users',
 | 
			
		||||
      'GET /api/users/:id': 'Get user by ID',
 | 
			
		||||
      'POST /api/users': 'Create new user',
 | 
			
		||||
      'PUT /api/users/:id': 'Update user by ID',
 | 
			
		||||
      'DELETE /api/users/:id': 'Delete user by ID'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.listen(port, () => {
 | 
			
		||||
  console.log(`API server running on port ${port}`);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										12
									
								
								examples/simple-website/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/simple-website/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "simple-website",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "A simple website with static pages",
 | 
			
		||||
  "main": "server.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "node server.js"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "express": "^4.18.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								examples/simple-website/public/about.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/simple-website/public/about.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>About - My Simple Website</title>
 | 
			
		||||
    <link rel="stylesheet" href="style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <nav>
 | 
			
		||||
        <a href="/">Home</a>
 | 
			
		||||
        <a href="/about">About</a>
 | 
			
		||||
        <a href="/contact">Contact</a>
 | 
			
		||||
    </nav>
 | 
			
		||||
    
 | 
			
		||||
    <main>
 | 
			
		||||
        <h1>About Us</h1>
 | 
			
		||||
        <p>This website is powered by Node.js and Express, running in a containerized environment.</p>
 | 
			
		||||
        <p>It demonstrates how easy it is to deploy web applications with the Cloud Node Container.</p>
 | 
			
		||||
    </main>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										33
									
								
								examples/simple-website/public/contact.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								examples/simple-website/public/contact.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Contact - My Simple Website</title>
 | 
			
		||||
    <link rel="stylesheet" href="style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <nav>
 | 
			
		||||
        <a href="/">Home</a>
 | 
			
		||||
        <a href="/about">About</a>
 | 
			
		||||
        <a href="/contact">Contact</a>
 | 
			
		||||
    </nav>
 | 
			
		||||
    
 | 
			
		||||
    <main>
 | 
			
		||||
        <h1>Contact Us</h1>
 | 
			
		||||
        <p>Get in touch with us!</p>
 | 
			
		||||
        <form>
 | 
			
		||||
            <label for="name">Name:</label>
 | 
			
		||||
            <input type="text" id="name" name="name" required>
 | 
			
		||||
            
 | 
			
		||||
            <label for="email">Email:</label>
 | 
			
		||||
            <input type="email" id="email" name="email" required>
 | 
			
		||||
            
 | 
			
		||||
            <label for="message">Message:</label>
 | 
			
		||||
            <textarea id="message" name="message" required></textarea>
 | 
			
		||||
            
 | 
			
		||||
            <button type="submit">Send Message</button>
 | 
			
		||||
        </form>
 | 
			
		||||
    </main>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										26
									
								
								examples/simple-website/public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								examples/simple-website/public/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>My Simple Website</title>
 | 
			
		||||
    <link rel="stylesheet" href="style.css">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <nav>
 | 
			
		||||
        <a href="/">Home</a>
 | 
			
		||||
        <a href="/about">About</a>
 | 
			
		||||
        <a href="/contact">Contact</a>
 | 
			
		||||
    </nav>
 | 
			
		||||
    
 | 
			
		||||
    <main>
 | 
			
		||||
        <h1>Welcome to My Website</h1>
 | 
			
		||||
        <p>This is a simple website running on Node.js in a Docker container!</p>
 | 
			
		||||
        <p>Current time: <span id="time"></span></p>
 | 
			
		||||
    </main>
 | 
			
		||||
    
 | 
			
		||||
    <script>
 | 
			
		||||
        document.getElementById('time').textContent = new Date().toLocaleString();
 | 
			
		||||
    </script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										65
									
								
								examples/simple-website/public/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								examples/simple-website/public/style.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
body {
 | 
			
		||||
    font-family: Arial, sans-serif;
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    background-color: #f4f4f4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav {
 | 
			
		||||
    background-color: #333;
 | 
			
		||||
    padding: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav a {
 | 
			
		||||
    color: white;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    margin-right: 1rem;
 | 
			
		||||
    padding: 0.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav a:hover {
 | 
			
		||||
    background-color: #555;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
    max-width: 800px;
 | 
			
		||||
    margin: 2rem auto;
 | 
			
		||||
    padding: 2rem;
 | 
			
		||||
    background-color: white;
 | 
			
		||||
    border-radius: 8px;
 | 
			
		||||
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
    color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    gap: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
label {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
input, textarea {
 | 
			
		||||
    padding: 0.5rem;
 | 
			
		||||
    border: 1px solid #ccc;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button {
 | 
			
		||||
    padding: 0.75rem;
 | 
			
		||||
    background-color: #007bff;
 | 
			
		||||
    color: white;
 | 
			
		||||
    border: none;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button:hover {
 | 
			
		||||
    background-color: #0056b3;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								examples/simple-website/server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								examples/simple-website/server.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const app = express();
 | 
			
		||||
const port = process.env.PORT || 3000;
 | 
			
		||||
 | 
			
		||||
// Serve static files from public directory
 | 
			
		||||
app.use(express.static('public'));
 | 
			
		||||
 | 
			
		||||
// Basic routes
 | 
			
		||||
app.get('/', (req, res) => {
 | 
			
		||||
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/about', (req, res) => {
 | 
			
		||||
  res.sendFile(path.join(__dirname, 'public', 'about.html'));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.get('/contact', (req, res) => {
 | 
			
		||||
  res.sendFile(path.join(__dirname, 'public', 'contact.html'));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Health check for the container
 | 
			
		||||
app.get('/ping', (req, res) => {
 | 
			
		||||
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.listen(port, () => {
 | 
			
		||||
  console.log(`Simple website running on port ${port}`);
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										95
									
								
								local-dev.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										95
									
								
								local-dev.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
name=''
 | 
			
		||||
http_port='80'
 | 
			
		||||
https_port='443'
 | 
			
		||||
root_path="$(pwd)"
 | 
			
		||||
verbose='false'
 | 
			
		||||
 | 
			
		||||
while getopts 'n:p:s:r:a:vh' flag; do
 | 
			
		||||
  case "${flag}" in
 | 
			
		||||
    n) name="${OPTARG}" ;;
 | 
			
		||||
    p) http_port="${OPTARG}" ;;
 | 
			
		||||
    s) https_port="${OPTARG}" ;;
 | 
			
		||||
    r) root_path="${OPTARG}" ;;
 | 
			
		||||
    a) nodever="${OPTARG}" ;;
 | 
			
		||||
    v) verbose='true' ;;
 | 
			
		||||
    h) echo "Variables"
 | 
			
		||||
       echo "-n = Name of Container, Required"
 | 
			
		||||
       echo "-p = Non-https Port Override, default 80"
 | 
			
		||||
       echo "-s = Https Port Override, default 443"
 | 
			
		||||
       echo "-r = Root Path for files, defaults to current working path"
 | 
			
		||||
       echo "-a = Node.js Version, Default to 20 (options: 18, 20, 22)"
 | 
			
		||||
       echo "-v = Enable Verbose Mode"
 | 
			
		||||
       exit 1 ;;
 | 
			
		||||
  esac
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# Check if Docker is installed
 | 
			
		||||
echo "Checking if Docker is Installed..."
 | 
			
		||||
check_docker=$(command -v docker)
 | 
			
		||||
if [ $? != 0 ]; then
 | 
			
		||||
  echo "Docker must be installed to run this application"
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
echo "Docker looks to be installed"
 | 
			
		||||
 | 
			
		||||
if [ -z "$name" ]; then
 | 
			
		||||
    echo "Name not set, please set it with -n"
 | 
			
		||||
    exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ -z "$nodever" ]; then
 | 
			
		||||
    nodever=20;
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "Building Docker Image..."
 | 
			
		||||
user=$(whoami)
 | 
			
		||||
uid=$(id -u)
 | 
			
		||||
if [ ! -d "$root_path/user" ]; then
 | 
			
		||||
  mkdir -p "$root_path/user";
 | 
			
		||||
  mkdir -p "$root_path/user/logs/{nginx,nodejs}";
 | 
			
		||||
  mkdir -p "$root_path/user/app";
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Create default Node.js app if it doesn't exist
 | 
			
		||||
if [ ! -f "$root_path/user/app/package.json" ]; then
 | 
			
		||||
  echo "Creating default Node.js application..."
 | 
			
		||||
  cp configs/package.json "$root_path/user/app/"
 | 
			
		||||
  cp configs/index.js "$root_path/user/app/"
 | 
			
		||||
  cp configs/ecosystem.config.js "$root_path/user/app/"
 | 
			
		||||
  # Update ecosystem config with correct user path
 | 
			
		||||
  sed -i "s/\/home\/myuser/\/home\/$user/g" "$root_path/user/app/ecosystem.config.js"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$check_docker run --pull=always -d -p "$http_port":80 -p "$https_port":443 -e NODEVER=$nodever -e environment=DEV --mount type=bind,source="$root_path"/user,target=/home/"$user" --mount type=bind,source="$(pwd)"/user/logs/nginx,target=/var/log/nginx --mount type=bind,source="$(pwd)"/user/logs/nodejs,target=/var/log/nodejs -e uid="$uid" -e user="$user" -e domain="$name-local.dev" --name "$name" cloud-node-container:latest
 | 
			
		||||
 | 
			
		||||
echo "Creating management scripts in root directory..."
 | 
			
		||||
echo "#!/usr/bin/env bash" > "$root_path/instance_start"
 | 
			
		||||
echo "docker start $name" >> "$root_path/instance_start"
 | 
			
		||||
echo "#!/usr/bin/env bash" > "$root_path/instance_stop"
 | 
			
		||||
echo "docker stop $name" >> "$root_path/instance_stop"
 | 
			
		||||
echo "#!/usr/bin/env bash" > "$root_path/instance_logs"
 | 
			
		||||
echo "docker logs -f $name" >> "$root_path/instance_logs"
 | 
			
		||||
echo "#!/usr/bin/env bash" > "$root_path/instance_shell"
 | 
			
		||||
echo "docker exec -it $name /bin/bash" >> "$root_path/instance_shell"
 | 
			
		||||
chmod +x $root_path/instance_*
 | 
			
		||||
 | 
			
		||||
echo "Waiting 30 seconds for setup to finish..."
 | 
			
		||||
sleep 30
 | 
			
		||||
 | 
			
		||||
echo "Local Development Instance Created!"
 | 
			
		||||
echo "- Container name: $name"
 | 
			
		||||
echo "- HTTP port: $http_port"
 | 
			
		||||
echo "- HTTPS port: $https_port"
 | 
			
		||||
echo "- Node.js version: $nodever"
 | 
			
		||||
echo ""
 | 
			
		||||
echo "Management scripts created:"
 | 
			
		||||
echo "  ./instance_start - Start the container"
 | 
			
		||||
echo "  ./instance_stop  - Stop the container" 
 | 
			
		||||
echo "  ./instance_logs  - View container logs"
 | 
			
		||||
echo "  ./instance_shell - Access container shell"
 | 
			
		||||
echo ""
 | 
			
		||||
echo "Your application files are in: $root_path/user/app/"
 | 
			
		||||
echo "Visit https://localhost:$https_port (accept SSL warning) to see your app"
 | 
			
		||||
 | 
			
		||||
exit 0
 | 
			
		||||
							
								
								
									
										17
									
								
								scripts/backup.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								scripts/backup.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
USER=$1
 | 
			
		||||
BACKUP_DIR="/home/$USER/_backups"
 | 
			
		||||
DATE=$(date +%Y%m%d_%H%M%S)
 | 
			
		||||
 | 
			
		||||
# Create backup directory if it doesn't exist
 | 
			
		||||
mkdir -p $BACKUP_DIR
 | 
			
		||||
 | 
			
		||||
# Backup application files
 | 
			
		||||
tar -czf $BACKUP_DIR/app_backup_$DATE.tar.gz -C /home/$USER app/
 | 
			
		||||
 | 
			
		||||
# Keep only last 10 backups
 | 
			
		||||
cd $BACKUP_DIR
 | 
			
		||||
ls -t app_backup_*.tar.gz | tail -n +11 | xargs -r rm
 | 
			
		||||
 | 
			
		||||
echo "Backup completed: app_backup_$DATE.tar.gz"
 | 
			
		||||
							
								
								
									
										54
									
								
								scripts/create-nginx-config.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										54
									
								
								scripts/create-nginx-config.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
# Create nginx configuration for reverse proxy to Node.js app
 | 
			
		||||
cat > /etc/nginx/conf.d/default.conf << EOF
 | 
			
		||||
upstream nodejs_backend {
 | 
			
		||||
    server 127.0.0.1:3000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
server {
 | 
			
		||||
    listen 80;
 | 
			
		||||
    server_name $domain $serveralias;
 | 
			
		||||
    
 | 
			
		||||
    # Redirect HTTP to HTTPS
 | 
			
		||||
    return 301 https://\$server_name\$request_uri;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
server {
 | 
			
		||||
    listen 443 ssl http2;
 | 
			
		||||
    server_name $domain $serveralias;
 | 
			
		||||
    
 | 
			
		||||
    ssl_certificate /etc/pki/tls/certs/localhost.crt;
 | 
			
		||||
    ssl_certificate_key /etc/pki/tls/private/localhost.key;
 | 
			
		||||
    
 | 
			
		||||
    ssl_protocols TLSv1.2 TLSv1.3;
 | 
			
		||||
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
 | 
			
		||||
    
 | 
			
		||||
    access_log /home/$user/logs/nginx/access.log;
 | 
			
		||||
    error_log /home/$user/logs/nginx/error.log;
 | 
			
		||||
    
 | 
			
		||||
    location / {
 | 
			
		||||
        proxy_pass http://nodejs_backend;
 | 
			
		||||
        proxy_http_version 1.1;
 | 
			
		||||
        proxy_set_header Upgrade \$http_upgrade;
 | 
			
		||||
        proxy_set_header Connection 'upgrade';
 | 
			
		||||
        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;
 | 
			
		||||
        proxy_cache_bypass \$http_upgrade;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    location /ping {
 | 
			
		||||
        proxy_pass http://nodejs_backend/ping;
 | 
			
		||||
        access_log off;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Static files
 | 
			
		||||
    location /static/ {
 | 
			
		||||
        alias /home/$user/app/public/;
 | 
			
		||||
        expires 30d;
 | 
			
		||||
        add_header Cache-Control "public, immutable";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
EOF
 | 
			
		||||
							
								
								
									
										68
									
								
								scripts/entrypoint.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										68
									
								
								scripts/entrypoint.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
if [ -z "$NODEVER" ]; then
 | 
			
		||||
  NODEVER="20";
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ -z "$environment" ]; then
 | 
			
		||||
  environment="PROD"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
adduser -u $uid $user
 | 
			
		||||
 | 
			
		||||
mkdir -p /home/$user/app
 | 
			
		||||
mkdir -p /home/$user/logs/{nginx,nodejs}
 | 
			
		||||
 | 
			
		||||
# Link log directories
 | 
			
		||||
rm -rf /var/log/nginx
 | 
			
		||||
ln -s /home/$user/logs/nginx /var/log/nginx
 | 
			
		||||
ln -s /home/$user/logs/nodejs /var/log/nodejs
 | 
			
		||||
 | 
			
		||||
# Configure nginx for reverse proxy
 | 
			
		||||
/scripts/create-nginx-config.sh
 | 
			
		||||
 | 
			
		||||
# Set ownership and permissions
 | 
			
		||||
chown -R $user:$user /home/$user
 | 
			
		||||
chmod -R 755 /home/$user
 | 
			
		||||
 | 
			
		||||
# Start nginx
 | 
			
		||||
nginx
 | 
			
		||||
 | 
			
		||||
if [[ $environment == 'DEV' ]]; then
 | 
			
		||||
  echo "Starting Dev Deployment"
 | 
			
		||||
  mkdir -p /home/$user/_backups
 | 
			
		||||
  
 | 
			
		||||
  # Ensure microdnf is available for installing additional packages in DEV mode
 | 
			
		||||
  if ! command -v microdnf &> /dev/null; then
 | 
			
		||||
    echo "microdnf not found, installing with dnf..."
 | 
			
		||||
    dnf install -y microdnf && dnf clean all
 | 
			
		||||
  fi
 | 
			
		||||
  
 | 
			
		||||
  # Install Memcached for session storage in DEV mode with memory limit
 | 
			
		||||
  microdnf install -y memcached
 | 
			
		||||
  # Start memcached with 32MB memory limit
 | 
			
		||||
  nohup memcached -d -u $user -p 11211 -m 32
 | 
			
		||||
  
 | 
			
		||||
  # Set up automatic backups
 | 
			
		||||
  echo "*/30 * * * * root /scripts/backup.sh $user" >> /etc/crontab
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Start cron for log rotation and backups
 | 
			
		||||
/usr/sbin/crond
 | 
			
		||||
 | 
			
		||||
# If there's an app in the user directory, start it with PM2
 | 
			
		||||
if [ -f /home/$user/app/package.json ]; then
 | 
			
		||||
  cd /home/$user/app
 | 
			
		||||
  su -c "npm install" $user
 | 
			
		||||
  su -c "pm2 start ecosystem.config.js" $user
 | 
			
		||||
else
 | 
			
		||||
  # Start default app
 | 
			
		||||
  cd /var/www/html
 | 
			
		||||
  npm install
 | 
			
		||||
  su -c "pm2 start ecosystem.config.js" $user
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Follow logs
 | 
			
		||||
tail -f /home/$user/logs/nginx/* /home/$user/logs/nodejs/*
 | 
			
		||||
 | 
			
		||||
exit 0
 | 
			
		||||
							
								
								
									
										6
									
								
								scripts/install-node18.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								scripts/install-node18.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
 | 
			
		||||
dnf install -y nodejs
 | 
			
		||||
npm install -g npm@latest
 | 
			
		||||
node --version
 | 
			
		||||
npm --version
 | 
			
		||||
							
								
								
									
										6
									
								
								scripts/install-node20.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								scripts/install-node20.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
 | 
			
		||||
dnf install -y nodejs
 | 
			
		||||
npm install -g npm@latest
 | 
			
		||||
node --version
 | 
			
		||||
npm --version
 | 
			
		||||
							
								
								
									
										6
									
								
								scripts/install-node22.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								scripts/install-node22.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
curl -fsSL https://rpm.nodesource.com/setup_22.x | bash -
 | 
			
		||||
dnf install -y nodejs
 | 
			
		||||
npm install -g npm@latest
 | 
			
		||||
node --version
 | 
			
		||||
npm --version
 | 
			
		||||
							
								
								
									
										18
									
								
								scripts/log-rotate.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								scripts/log-rotate.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
# Log rotation script for nginx and Node.js logs
 | 
			
		||||
LOG_DIR="/home/*/logs"
 | 
			
		||||
 | 
			
		||||
# Compress logs older than 3 days
 | 
			
		||||
find $LOG_DIR -name "*.log" -type f -mtime +3 -exec gzip {} \;
 | 
			
		||||
 | 
			
		||||
# Delete compressed logs older than 7 days
 | 
			
		||||
find $LOG_DIR -name "*.gz" -type f -mtime +7 -delete
 | 
			
		||||
 | 
			
		||||
# Rotate nginx logs
 | 
			
		||||
if [ -f /var/run/nginx.pid ]; then
 | 
			
		||||
    kill -USR1 $(cat /var/run/nginx.pid)
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Rotate PM2 logs
 | 
			
		||||
pm2 flush
 | 
			
		||||
							
								
								
									
										58
									
								
								scripts/memory-info.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										58
									
								
								scripts/memory-info.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
echo "=== Cloud Node Container Memory Usage ==="
 | 
			
		||||
echo "Date: $(date)"
 | 
			
		||||
echo ""
 | 
			
		||||
 | 
			
		||||
echo "=== System Memory ==="
 | 
			
		||||
free -h
 | 
			
		||||
 | 
			
		||||
echo ""
 | 
			
		||||
echo "=== Process Memory Usage ==="
 | 
			
		||||
echo "Process                    PID    VSZ    RSS   %MEM"
 | 
			
		||||
echo "----------------------------------------"
 | 
			
		||||
 | 
			
		||||
# Nginx
 | 
			
		||||
nginx_pid=$(pgrep nginx | head -1)
 | 
			
		||||
if [ ! -z "$nginx_pid" ]; then
 | 
			
		||||
    ps -o pid,vsz,rss,%mem,comm -p $nginx_pid | tail -n +2 | while read pid vsz rss mem comm; do
 | 
			
		||||
        printf "%-25s %5s %6s %6s %5s\n" "$comm" "$pid" "${vsz}K" "${rss}K" "$mem%"
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Node.js/PM2
 | 
			
		||||
pm2_pids=$(pgrep -f "PM2\|node")
 | 
			
		||||
if [ ! -z "$pm2_pids" ]; then
 | 
			
		||||
    echo "$pm2_pids" | while read pid; do
 | 
			
		||||
        ps -o pid,vsz,rss,%mem,comm -p $pid 2>/dev/null | tail -n +2 | while read pid vsz rss mem comm; do
 | 
			
		||||
            printf "%-25s %5s %6s %6s %5s\n" "$comm" "$pid" "${vsz}K" "${rss}K" "$mem%"
 | 
			
		||||
        done
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Memcached
 | 
			
		||||
memcached_pid=$(pgrep memcached)
 | 
			
		||||
if [ ! -z "$memcached_pid" ]; then
 | 
			
		||||
    ps -o pid,vsz,rss,%mem,comm -p $memcached_pid | tail -n +2 | while read pid vsz rss mem comm; do
 | 
			
		||||
        printf "%-25s %5s %6s %6s %5s\n" "$comm" "$pid" "${vsz}K" "${rss}K" "$mem%"
 | 
			
		||||
    done
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo ""
 | 
			
		||||
echo "=== Container Resource Limits ==="
 | 
			
		||||
if [ -f /sys/fs/cgroup/memory/memory.limit_in_bytes ]; then
 | 
			
		||||
    limit=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
 | 
			
		||||
    if [ $limit -lt 9223372036854775807 ]; then
 | 
			
		||||
        echo "Memory Limit: $((limit / 1024 / 1024))MB"
 | 
			
		||||
    else
 | 
			
		||||
        echo "Memory Limit: No limit set"
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo ""
 | 
			
		||||
echo "=== PM2 Process Info ==="
 | 
			
		||||
if command -v pm2 &> /dev/null; then
 | 
			
		||||
    pm2 list 2>/dev/null | grep -E "(App name|online|stopped|errored)"
 | 
			
		||||
    echo ""
 | 
			
		||||
    pm2 show 0 2>/dev/null | grep -E "(memory|cpu|uptime)" || echo "No PM2 processes running"
 | 
			
		||||
fi
 | 
			
		||||
		Reference in New Issue
	
	Block a user