From 2622512ae2e7732bf8f42b646ed239b882a68395 Mon Sep 17 00:00:00 2001 From: Andrei Date: Mon, 6 Oct 2025 21:20:26 +0000 Subject: [PATCH] feat: Create PM2 + Docker production deployment system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced old production script with PM2-based deployment - Created start-production.sh: automated startup script - Starts Docker containers for databases - Waits for database health checks - Runs migrations automatically - Builds backend/frontend if needed - Starts PM2 processes with ecosystem.config.js - Verifies all services are running - Created stop-production.sh: graceful shutdown script - Stops PM2 processes - Stops Docker containers - Verifies shutdown - Created PRODUCTION_DEPLOYMENT.md: comprehensive deployment guide - Prerequisites and installation steps - Configuration instructions - Nginx reverse proxy setup - SSL certificate setup with Certbot - Management commands for PM2 and Docker - Backup strategy - Troubleshooting guide - Security checklist Production setup: - Backend: Port 3020 → api.parentflowapp.com - Frontend: Port 3030 → web.parentflowapp.com - Docker: PostgreSQL, Redis, MongoDB, MinIO - PM2: Backend and Frontend applications - Target: Server 10.0.0.240 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- PRODUCTION_DEPLOYMENT.md | 453 +++++++++++++++++++++++++++++++++++++++ start-production.sh | 231 +++++++++++++++++--- stop-production.sh | 77 +++++++ 3 files changed, 731 insertions(+), 30 deletions(-) create mode 100644 PRODUCTION_DEPLOYMENT.md create mode 100755 stop-production.sh diff --git a/PRODUCTION_DEPLOYMENT.md b/PRODUCTION_DEPLOYMENT.md new file mode 100644 index 0000000..5439f7e --- /dev/null +++ b/PRODUCTION_DEPLOYMENT.md @@ -0,0 +1,453 @@ +# ParentFlow Production Deployment Guide + +**Target Server**: 10.0.0.240 +**Deployment Method**: PM2 + Docker +**Last Updated**: October 6, 2025 + +## Overview + +Production deployment uses a hybrid approach: +- **Docker Compose**: For databases (PostgreSQL, Redis, MongoDB, MinIO) +- **PM2**: For application services (Backend, Frontend) + +## Architecture + +``` +┌─────────────────────────────────────────────┐ +│ Server: 10.0.0.240 │ +├─────────────────────────────────────────────┤ +│ PM2 Processes: │ +│ - Backend: Port 3020 (Node.js/NestJS) │ +│ - Frontend: Port 3030 (Next.js) │ +├─────────────────────────────────────────────┤ +│ Docker Containers: │ +│ - PostgreSQL: Port 5432 │ +│ - Redis: Port 6379 │ +│ - MongoDB: Port 27017 │ +│ - MinIO: Port 9000 (API) │ +│ Port 9001 (Console) │ +└─────────────────────────────────────────────┘ + ↓ ↓ + api.parentflowapp.com web.parentflowapp.com +``` + +## Prerequisites + +### 1. Install Required Software + +```bash +# Install Node.js 18+ and npm +curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - +sudo apt-get install -y nodejs + +# Install PM2 globally +sudo npm install -g pm2 + +# Install Docker +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker $USER + +# Install Docker Compose +sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +sudo chmod +x /usr/local/bin/docker-compose +``` + +### 2. Clone Repository + +```bash +cd /root +git clone https://git.noru1.ro/andrei/maternal-app.git +cd maternal-app +``` + +### 3. Install Dependencies + +```bash +# Backend dependencies +cd maternal-app/maternal-app-backend +npm install + +# Frontend dependencies +cd ../../maternal-web +npm install +cd ../.. +``` + +## Configuration + +### 1. Environment Variables + +Copy the example environment file and update with production values: + +```bash +cp .env.production.example .env.production +nano .env.production +``` + +**Critical variables to update:** +- `POSTGRES_PASSWORD`: Strong password for PostgreSQL +- `REDIS_PASSWORD`: Strong password for Redis +- `MONGO_PASSWORD`: Strong password for MongoDB +- `JWT_SECRET`: 64-character random string +- `JWT_REFRESH_SECRET`: Different 64-character random string +- `OPENAI_API_KEY`: Your OpenAI API key (for AI features) + +Generate secure secrets: +```bash +# Generate JWT secrets +openssl rand -base64 64 +openssl rand -base64 64 +``` + +### 2. Update ecosystem.config.js + +Ensure the production environment variables in `ecosystem.config.js` match your `.env.production` file. + +### 3. Configure Nginx (Reverse Proxy) + +Create Nginx configuration for domain routing: + +```nginx +# /etc/nginx/sites-available/parentflow + +# Backend API +server { + listen 80; + server_name api.parentflowapp.com; + + location / { + proxy_pass http://localhost:3020; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + 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; + } +} + +# Frontend +server { + listen 80; + server_name web.parentflowapp.com; + + location / { + proxy_pass http://localhost:3030; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } +} +``` + +Enable the site: +```bash +sudo ln -s /etc/nginx/sites-available/parentflow /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +### 4. SSL Certificates (Optional but Recommended) + +```bash +# Install Certbot +sudo apt-get install certbot python3-certbot-nginx + +# Obtain certificates +sudo certbot --nginx -d api.parentflowapp.com -d web.parentflowapp.com +``` + +## Deployment + +### First-Time Deployment + +```bash +cd /root/maternal-app + +# Start production environment +./start-production.sh +``` + +The script will: +1. ✅ Start Docker containers (databases) +2. ✅ Wait for databases to be healthy +3. ✅ Run database migrations +4. ✅ Build backend (if needed) +5. ✅ Build frontend (if needed) +6. ✅ Start PM2 processes +7. ✅ Verify all services + +### Subsequent Deployments + +```bash +cd /root/maternal-app + +# Pull latest changes +git pull origin main + +# Rebuild applications +cd maternal-app/maternal-app-backend +npm install +npm run build + +cd ../../maternal-web +npm install +npm run build + +cd ../.. + +# Restart PM2 processes +pm2 restart all + +# Or use the full restart script +./stop-production.sh +./start-production.sh +``` + +## Management Commands + +### PM2 Commands + +```bash +# View process status +pm2 status + +# View logs +pm2 logs + +# View specific service logs +pm2 logs parentflow-backend +pm2 logs parentflow-frontend + +# Restart services +pm2 restart all +pm2 restart parentflow-backend +pm2 restart parentflow-frontend + +# Stop services +pm2 stop all + +# Delete processes +pm2 delete all + +# Save PM2 process list +pm2 save + +# Setup PM2 to start on system boot +pm2 startup +pm2 save +``` + +### Docker Commands + +```bash +# View running containers +docker ps + +# View logs +docker logs parentflow-postgres-prod +docker logs parentflow-redis-prod +docker logs parentflow-mongodb-prod +docker logs parentflow-minio-prod + +# Follow logs in real-time +docker logs -f parentflow-postgres-prod + +# Access database shell +docker exec -it parentflow-postgres-prod psql -U parentflow_user -d parentflow_production + +# Access Redis CLI +docker exec -it parentflow-redis-prod redis-cli -a parentflow_redis_password_2024 + +# Access MongoDB shell +docker exec -it parentflow-mongodb-prod mongo -u parentflow_admin -p parentflow_mongo_password_2024 + +# Stop all containers +docker-compose -f docker-compose.production.yml down + +# Stop and remove volumes (WARNING: deletes data) +docker-compose -f docker-compose.production.yml down -v +``` + +### Application Management + +```bash +# Start production +./start-production.sh + +# Stop production +./stop-production.sh + +# Check migration status +cd maternal-app/maternal-app-backend +./scripts/check-migrations.sh + +# Run migrations manually +./scripts/master-migration.sh +``` + +## Monitoring + +### Health Checks + +- **Backend**: http://localhost:3020/api/health +- **Frontend**: http://localhost:3030 +- **MinIO Console**: http://localhost:9001 + +### Log Files + +PM2 logs are stored in: +- `~/.pm2/logs/parentflow-backend-out.log` +- `~/.pm2/logs/parentflow-backend-error.log` +- `~/.pm2/logs/parentflow-frontend-out.log` +- `~/.pm2/logs/parentflow-frontend-error.log` + +Docker logs via: +```bash +docker logs +``` + +### System Resources + +```bash +# Monitor PM2 processes +pm2 monit + +# Monitor Docker containers +docker stats + +# System resources +htop +``` + +## Backup Strategy + +### Database Backups + +```bash +# PostgreSQL backup +docker exec parentflow-postgres-prod pg_dump -U parentflow_user parentflow_production > backup-$(date +%Y%m%d).sql + +# Restore PostgreSQL +cat backup-20251006.sql | docker exec -i parentflow-postgres-prod psql -U parentflow_user -d parentflow_production + +# MongoDB backup +docker exec parentflow-mongodb-prod mongodump --username parentflow_admin --password parentflow_mongo_password_2024 --out /data/backup + +# Redis backup (automatic with AOF persistence) +docker exec parentflow-redis-prod redis-cli -a parentflow_redis_password_2024 BGSAVE +``` + +### Automated Backups + +Add to crontab: +```bash +# Daily database backup at 2 AM +0 2 * * * /root/maternal-app/scripts/backup-database.sh +``` + +## Troubleshooting + +### Backend Won't Start + +```bash +# Check logs +pm2 logs parentflow-backend --err + +# Check if port is already in use +lsof -i:3020 + +# Verify database connection +docker exec -it parentflow-postgres-prod psql -U parentflow_user -d parentflow_production -c "SELECT version();" +``` + +### Frontend Won't Start + +```bash +# Check logs +pm2 logs parentflow-frontend --err + +# Rebuild frontend +cd maternal-web +rm -rf .next +npm run build +``` + +### Database Connection Issues + +```bash +# Check if containers are running +docker ps + +# Check container health +docker inspect parentflow-postgres-prod --format='{{.State.Health.Status}}' + +# View container logs +docker logs parentflow-postgres-prod +``` + +### Migrations Failed + +```bash +# Check migration status +cd maternal-app/maternal-app-backend +./scripts/check-migrations.sh + +# Manually run specific migration +PGPASSWORD=parentflow_secure_password_2024 psql -h localhost -p 5432 -U parentflow_user -d parentflow_production -f src/database/migrations/V001_create_core_auth.sql +``` + +## Security Checklist + +- [ ] Updated all default passwords in `.env.production` +- [ ] Generated secure JWT secrets +- [ ] Configured firewall (ufw/iptables) to restrict database ports +- [ ] Enabled SSL certificates with Certbot +- [ ] Configured Nginx rate limiting +- [ ] Set up PM2 with non-root user (recommended) +- [ ] Enabled Docker container resource limits +- [ ] Configured backup strategy +- [ ] Set up monitoring/alerting + +## Performance Optimization + +### PM2 Cluster Mode + +For better performance, run backend in cluster mode: + +```javascript +// ecosystem.config.js +{ + name: 'parentflow-backend', + instances: 'max', // Use all CPU cores + exec_mode: 'cluster', + // ... other settings +} +``` + +### Database Optimization + +- Enable PostgreSQL connection pooling (already configured) +- Monitor slow queries +- Add indexes for frequently queried fields +- Configure Redis maxmemory policy + +## CI/CD Integration + +See `docs/REMAINING_FEATURES.md` for Gitea Actions workflow setup for automated deployments to 10.0.0.240. + +## Support + +For issues or questions: +- Check logs: `pm2 logs` and `docker logs` +- Review documentation: `/root/maternal-app/docs/` +- Check migration status: `./scripts/check-migrations.sh` + +--- + +**Last Updated**: October 6, 2025 +**Deployment Version**: 1.0.0 \ No newline at end of file diff --git a/start-production.sh b/start-production.sh index 65fc32e..81e68d4 100755 --- a/start-production.sh +++ b/start-production.sh @@ -1,53 +1,224 @@ #!/bin/bash -# Start Production Servers Script +# ParentFlow Production Startup Script +# Uses PM2 for frontend/backend, Docker for databases # Ports: Backend 3020, Frontend 3030 +# Server: 10.0.0.240 (or localhost for local production testing) -echo "Starting ParentFlow Production Servers..." +set -e # Exit on any error -# Kill any existing processes on the ports -echo "Cleaning up existing processes..." -lsof -ti:3020 | xargs kill -9 2>/dev/null -lsof -ti:3030 | xargs kill -9 2>/dev/null -sleep 2 +# Color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color -# Start Backend on port 3020 -echo "Starting backend on port 3020..." +echo "==========================================" +echo "ParentFlow Production Startup" +echo "==========================================" +echo "" + +# Check if PM2 is installed +if ! command -v pm2 &> /dev/null; then + echo -e "${RED}ERROR: PM2 is not installed!${NC}" + echo "Install PM2 globally with: npm install -g pm2" + exit 1 +fi + +# Check if Docker is installed +if ! command -v docker &> /dev/null; then + echo -e "${RED}ERROR: Docker is not installed!${NC}" + exit 1 +fi + +# Check if docker-compose is installed +if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then + echo -e "${RED}ERROR: Docker Compose is not installed!${NC}" + exit 1 +fi + +# Step 1: Start Docker services (databases) +echo -e "${BLUE}Step 1: Starting Docker services (databases)...${NC}" +if docker compose version &> /dev/null; then + docker compose -f docker-compose.production.yml up -d +else + docker-compose -f docker-compose.production.yml up -d +fi + +if [ $? -eq 0 ]; then + echo -e "${GREEN}✓ Docker services started${NC}" +else + echo -e "${RED}✗ Failed to start Docker services${NC}" + exit 1 +fi + +# Wait for databases to be ready +echo -e "${YELLOW}Waiting for databases to be healthy...${NC}" +sleep 10 + +# Check database health +echo -e "${BLUE}Checking database health...${NC}" +MAX_RETRIES=30 +RETRY_COUNT=0 + +while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + POSTGRES_HEALTHY=$(docker inspect parentflow-postgres-prod --format='{{.State.Health.Status}}' 2>/dev/null || echo "starting") + REDIS_HEALTHY=$(docker inspect parentflow-redis-prod --format='{{.State.Health.Status}}' 2>/dev/null || echo "starting") + MONGO_HEALTHY=$(docker inspect parentflow-mongodb-prod --format='{{.State.Health.Status}}' 2>/dev/null || echo "starting") + + if [ "$POSTGRES_HEALTHY" = "healthy" ] && [ "$REDIS_HEALTHY" = "healthy" ] && [ "$MONGO_HEALTHY" = "healthy" ]; then + echo -e "${GREEN}✓ All databases are healthy${NC}" + break + fi + + echo -e "${YELLOW}Waiting for databases... ($RETRY_COUNT/$MAX_RETRIES)${NC}" + sleep 2 + ((RETRY_COUNT++)) +done + +if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then + echo -e "${RED}✗ Databases failed to become healthy${NC}" + echo "Check Docker logs with: docker logs parentflow-postgres-prod" + exit 1 +fi + +# Step 2: Run database migrations +echo "" +echo -e "${BLUE}Step 2: Running database migrations...${NC}" cd /root/maternal-app/maternal-app/maternal-app-backend -PORT=3020 API_PORT=3020 NODE_ENV=production nohup node dist/main.js > /root/maternal-app/logs/backend-prod.log 2>&1 & -BACKEND_PID=$! -echo "Backend started with PID: $BACKEND_PID" -# Start Frontend on port 3030 -echo "Starting frontend on port 3030..." +# Check if migration script exists +if [ -f "./scripts/master-migration.sh" ]; then + echo -e "${YELLOW}Running master migration script...${NC}" + DATABASE_HOST=localhost \ + DATABASE_PORT=5432 \ + DATABASE_NAME=parentflow_production \ + DATABASE_USER=parentflow_user \ + DATABASE_PASSWORD=parentflow_secure_password_2024 \ + ./scripts/master-migration.sh || { + echo -e "${YELLOW}Warning: Migrations may have partially failed. Continuing...${NC}" + } +else + echo -e "${YELLOW}Warning: Migration script not found. Skipping migrations.${NC}" +fi + +# Step 3: Build backend (if needed) +echo "" +echo -e "${BLUE}Step 3: Building backend...${NC}" +cd /root/maternal-app/maternal-app/maternal-app-backend + +if [ ! -d "dist" ]; then + echo -e "${YELLOW}Building backend for the first time...${NC}" + npm run build + if [ $? -ne 0 ]; then + echo -e "${RED}✗ Backend build failed${NC}" + exit 1 + fi + echo -e "${GREEN}✓ Backend built successfully${NC}" +else + echo -e "${GREEN}✓ Backend dist directory exists${NC}" +fi + +# Step 4: Build frontend (if needed) +echo "" +echo -e "${BLUE}Step 4: Building frontend...${NC}" cd /root/maternal-app/maternal-web -PORT=3030 NODE_ENV=production nohup npm run start > /root/maternal-app/logs/frontend-prod.log 2>&1 & -FRONTEND_PID=$! -echo "Frontend started with PID: $FRONTEND_PID" -# Wait a moment for servers to start +if [ ! -d ".next" ]; then + echo -e "${YELLOW}Building frontend for the first time...${NC}" + npm run build + if [ $? -ne 0 ]; then + echo -e "${RED}✗ Frontend build failed${NC}" + exit 1 + fi + echo -e "${GREEN}✓ Frontend built successfully${NC}" +else + echo -e "${GREEN}✓ Frontend .next directory exists${NC}" +fi + +# Step 5: Start PM2 processes +echo "" +echo -e "${BLUE}Step 5: Starting PM2 processes...${NC}" + +# Check if ecosystem.config.js exists +if [ ! -f "/root/maternal-app/ecosystem.config.js" ]; then + echo -e "${RED}✗ ecosystem.config.js not found${NC}" + exit 1 +fi + +# Stop any existing PM2 processes +echo -e "${YELLOW}Stopping any existing PM2 processes...${NC}" +pm2 delete all 2>/dev/null || true + +# Start PM2 with ecosystem config (production environment) +cd /root/maternal-app +pm2 start ecosystem.config.js --env production + +if [ $? -eq 0 ]; then + echo -e "${GREEN}✓ PM2 processes started${NC}" +else + echo -e "${RED}✗ Failed to start PM2 processes${NC}" + exit 1 +fi + +# Save PM2 process list +pm2 save + +# Step 6: Verify services are running +echo "" +echo -e "${BLUE}Step 6: Verifying services...${NC}" sleep 5 -# Check if servers are running +# Check PM2 status +echo -e "${YELLOW}PM2 Status:${NC}" +pm2 list + +# Check if ports are listening echo "" -echo "Checking server status..." +echo -e "${YELLOW}Port Status:${NC}" if lsof -i:3020 > /dev/null 2>&1; then - echo "✅ Backend is running on port 3020" + echo -e "${GREEN}✓ Backend is running on port 3020${NC}" else - echo "❌ Backend failed to start on port 3020" + echo -e "${RED}✗ Backend not detected on port 3020${NC}" fi if lsof -i:3030 > /dev/null 2>&1; then - echo "✅ Frontend is running on port 3030" + echo -e "${GREEN}✓ Frontend is running on port 3030${NC}" else - echo "❌ Frontend failed to start on port 3030" + echo -e "${RED}✗ Frontend not detected on port 3030${NC}" fi +# Check Docker containers echo "" -echo "Production servers started!" -echo "Backend: http://localhost:3020" -echo "Frontend: http://localhost:3030" +echo -e "${YELLOW}Docker Containers:${NC}" +docker ps --filter name=parentflow --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" + +# Summary echo "" -echo "To check logs:" -echo " Backend: tail -f /root/maternal-app/logs/backend-prod.log" -echo " Frontend: tail -f /root/maternal-app/logs/frontend-prod.log" \ No newline at end of file +echo "==========================================" +echo -e "${GREEN}Production Environment Started!${NC}" +echo "==========================================" +echo "" +echo "Services:" +echo " Backend API: http://localhost:3020" +echo " Frontend: http://localhost:3030" +echo " PostgreSQL: localhost:5432" +echo " Redis: localhost:6379" +echo " MongoDB: localhost:27017" +echo " MinIO: localhost:9000" +echo " MinIO Console: localhost:9001" +echo "" +echo "Management Commands:" +echo " PM2 status: pm2 status" +echo " PM2 logs: pm2 logs" +echo " PM2 restart: pm2 restart all" +echo " PM2 stop: pm2 stop all" +echo " Docker logs: docker logs parentflow-postgres-prod" +echo " Stop all: pm2 stop all && docker-compose -f docker-compose.production.yml down" +echo "" +echo "Domains (configure in Nginx/DNS):" +echo " Frontend: web.parentflowapp.com → localhost:3030" +echo " Backend: api.parentflowapp.com → localhost:3020" +echo "" +echo -e "${GREEN}✓ Startup complete!${NC}" \ No newline at end of file diff --git a/stop-production.sh b/stop-production.sh new file mode 100755 index 0000000..745eb26 --- /dev/null +++ b/stop-production.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# ParentFlow Production Stop Script +# Stops PM2 processes and Docker containers gracefully + +set -e + +# Color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo "==========================================" +echo "Stopping ParentFlow Production" +echo "==========================================" +echo "" + +# Step 1: Stop PM2 processes +echo -e "${BLUE}Step 1: Stopping PM2 processes...${NC}" +if command -v pm2 &> /dev/null; then + pm2 stop all 2>/dev/null || true + echo -e "${GREEN}✓ PM2 processes stopped${NC}" +else + echo -e "${YELLOW}Warning: PM2 not found${NC}" +fi + +# Step 2: Stop Docker containers +echo "" +echo -e "${BLUE}Step 2: Stopping Docker containers...${NC}" +if docker compose version &> /dev/null; then + docker compose -f docker-compose.production.yml down +else + docker-compose -f docker-compose.production.yml down 2>/dev/null || true +fi + +if [ $? -eq 0 ]; then + echo -e "${GREEN}✓ Docker containers stopped${NC}" +else + echo -e "${YELLOW}Warning: Failed to stop some Docker containers${NC}" +fi + +# Verify everything is stopped +echo "" +echo -e "${BLUE}Verification:${NC}" + +# Check PM2 +if command -v pm2 &> /dev/null; then + RUNNING_PROCESSES=$(pm2 jlist 2>/dev/null | jq -r '.[] | select(.pm2_env.status == "online") | .name' | wc -l) + if [ "$RUNNING_PROCESSES" -eq 0 ]; then + echo -e "${GREEN}✓ No PM2 processes running${NC}" + else + echo -e "${YELLOW}Warning: $RUNNING_PROCESSES PM2 processes still running${NC}" + fi +fi + +# Check Docker +RUNNING_CONTAINERS=$(docker ps --filter name=parentflow --format "{{.Names}}" | wc -l) +if [ "$RUNNING_CONTAINERS" -eq 0 ]; then + echo -e "${GREEN}✓ No ParentFlow Docker containers running${NC}" +else + echo -e "${YELLOW}Warning: $RUNNING_CONTAINERS containers still running${NC}" + docker ps --filter name=parentflow +fi + +echo "" +echo "==========================================" +echo -e "${GREEN}Production environment stopped${NC}" +echo "==========================================" +echo "" +echo "To completely remove Docker volumes (WARNING: deletes data):" +echo " docker-compose -f docker-compose.production.yml down -v" +echo "" +echo "To remove PM2 processes from startup:" +echo " pm2 delete all" +echo " pm2 save" \ No newline at end of file