Files
cloud-node-container/MEMORY-GUIDE.md
T
shadowdaoandClaude Opus 5 80b03c884e Use dumb-init --single-child for tini signal parity
Code review caught that dumb-init is not a drop-in replacement for tini as
invoked. tini without -g forwards a signal only to its direct child;
dumb-init without --single-child calls setsid() and forwards to the entire
process group. Verified against dumb-init 1.2.5 with a parent that catches
SIGTERM and does not forward it: under the default the grandchild is still
signalled, under --single-child it is not.

Without the flag, `docker stop` delivered SIGTERM to the tenant's node
process directly and simultaneously with pm2, bypassing pm2's kill_timeout
shutdown sequencing. Since the wedged-container fix in #1 introduced tini
specifically for its signal forwarding, the previous commit's claim that
the two are equivalent did not hold.

Also corrects MEMORY-GUIDE.md, which still described header buffers as
"reduced" after they were raised from 2 1k to the nginx default of 4 8k.
The nginx memory budget is unchanged: these buffers are allocated per
request only when a request needs them, not preallocated per connection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 09:06:10 -07:00

6.7 KiB

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 10 ~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; header buffers allocated on demand
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 body buffer (16k) and 8MB max body size. Header buffers use the nginx default (large_client_header_buffers 4 8k) rather than a reduced value -- a smaller limit rejects ordinary session cookies with a 400. These are allocated per request only when a request actually needs them, not preallocated per connection.
  6. Log Buffering: 2-minute flush intervals to reduce I/O

Application-Level Recommendations

// 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

# 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:

// 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:

// 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

# 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

# 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

# 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.