- Clarified that uid/user must match host system values - Added note that PM2 runs as specified user for security - Created troubleshooting section with common issues: - 502 Bad Gateway errors - Permission errors - App not starting - Provided specific commands for debugging each issue This documentation helps users understand and resolve common container deployment issues. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
289 lines
10 KiB
Markdown
289 lines
10 KiB
Markdown
# 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 repo.anhonesthost.net/cloud-hosting-platform/cnoc: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 (auto-generated if not provided)
|
||
|
||
### 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
|
||
- Generate PM2 configuration from your package.json
|
||
- Start your application with PM2
|
||
- Handle SSL and reverse proxy
|
||
- Provide health monitoring
|
||
|
||
#### Important package.json fields:
|
||
- **name**: Used as the PM2 process name (defaults to 'node-app')
|
||
- **main**: Entry point file (defaults to 'index.js')
|
||
- **scripts.start**: Alternative way to specify entry point (e.g., "node server.js")
|
||
|
||
The container automatically generates an ecosystem.config.js file from your package.json if you don't provide one.
|
||
|
||
#### PM2 Configuration Notes:
|
||
- The auto-generated PM2 config uses **fork mode** (not cluster mode) for simplicity and compatibility
|
||
- `wait_ready` is set to `false` by default - your app doesn't need to send any special signals to PM2
|
||
- PM2 runs as the specified user (not root) for security - ensure your `uid` and `user` match your host system
|
||
- If you need advanced PM2 features, you can provide your own `ecosystem.config.js` file
|
||
|
||
**Advanced Usage**: If you want to use PM2's `wait_ready` feature for health checking:
|
||
1. Create your own `ecosystem.config.js` with `wait_ready: true`
|
||
2. Add `process.send('ready')` in your app when it's ready to accept connections:
|
||
```javascript
|
||
app.listen(port, () => {
|
||
console.log(`Server running on port ${port}`);
|
||
if (process.send) {
|
||
process.send('ready');
|
||
}
|
||
});
|
||
```
|
||
|
||
### 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 (must match your host user ID)
|
||
- `user` – Username for file permissions (must match your host username)
|
||
- `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
|
||
|
||
### Common Issues and Solutions
|
||
|
||
**502 Bad Gateway errors:**
|
||
- Check if your app is running on port 3000 (or the PORT environment variable)
|
||
- Look at nginx error logs: `/home/$user/logs/nginx/error.log`
|
||
- Verify PM2 status: `docker exec <container> su - <user> -c "pm2 status"`
|
||
|
||
**Permission errors:**
|
||
- Ensure `uid` and `user` environment variables match your host system
|
||
- Use `$(id -u)` and `$(whoami)` in your docker run command
|
||
- Files should be owned by your user, not root
|
||
|
||
**App not starting:**
|
||
- Check PM2 logs: `/home/$user/logs/nodejs/error.log`
|
||
- Verify your package.json has correct `main` or `scripts.start` field
|
||
- Ensure all dependencies are listed in package.json |