Deploy PHP App: LAMP and LEMP Stack Guide
Complete guide to deploying PHP applications on a VPS. LAMP vs LEMP comparison, Apache/Nginx + PHP-FPM + MySQL setup, virtual host configuration, and PHP version management.
Complete guide to deploying Node.js applications on a VPS. Node.js installation with nvm, PM2 process manager, Nginx reverse proxy, SSL certificate, and environment variables management.
Deploying Node.js applications to a production environment is a systematic process with the right tools and configuration. This guide covers everything from Node.js installation with nvm to PM2 process manager, Nginx reverse proxy configuration, and SSL certificate setup.
nvm (Node Version Manager) lets you manage multiple Node.js versions and is recommended for production environments.
# Download and run the nvm install script
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Reload shell
source ~/.bashrc
# Check nvm version
nvm --version
# Install latest LTS version
nvm install --lts
# Install specific version
nvm install 20.11.0
# Set default version
nvm use --lts
nvm alias default node
# Check Node.js and npm versions
node --version
npm --version
Always use the LTS (Long Term Support) version in production. LTS versions receive security updates for a longer period.
# Create application directory
sudo mkdir -p /var/www/myapp
sudo chown $USER:$USER /var/www/myapp
# Clone the repo
cd /var/www
git clone https://github.com/user/myapp.git
cd myapp
# Install dependencies (production)
npm install --production
# Or install all dependencies
npm install
# Create .env file
nano /var/www/myapp/.env
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
JWT_SECRET=your-secret-key-here
REDIS_URL=redis://localhost:6379
# Secure the .env file
chmod 600 /var/www/myapp/.env
Never add the .env file to your Git repository. Add .env to your .gitignore file.
PM2 is the most popular tool for managing Node.js applications in production. It offers automatic restarts, log management, and cluster mode.
# Install PM2 globally
npm install -g pm2
# Check PM2 version
pm2 --version
# Simple start
pm2 start app.js --name myapp
# With specific port
pm2 start app.js --name myapp -- --port 3000
# Using npm start
pm2 start npm --name myapp -- start
# Cluster mode (instances equal to CPU cores)
pm2 start app.js --name myapp -i max
# Specific number of instances
pm2 start app.js --name myapp -i 4
Use an ecosystem.config.js file for more advanced configuration:
nano /var/www/myapp/ecosystem.config.js
module.exports = {
apps: [
{
name: 'myapp',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: '/var/log/pm2/myapp-error.log',
out_file: '/var/log/pm2/myapp-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
max_memory_restart: '500M',
watch: false,
autorestart: true
}
]
};
# Start with ecosystem file
pm2 start ecosystem.config.js --env production
# PM2 basic commands
pm2 list # List running apps
pm2 status # Status summary
pm2 logs myapp # View logs
pm2 logs myapp --lines 100 # Last 100 lines of logs
pm2 restart myapp # Restart
pm2 reload myapp # Zero-downtime reload
pm2 stop myapp # Stop
pm2 delete myapp # Delete
pm2 monit # Real-time monitoring
# Generate startup script
pm2 startup
# Run the output command (example):
sudo env PATH=$PATH:/home/user/.nvm/versions/node/v20.11.0/bin pm2 startup systemd -u user --hp /home/user
# Save current PM2 list
pm2 save
Nginx runs in front of your Node.js application as a reverse proxy, providing SSL termination, static file serving, and load balancing.
# Install Nginx
sudo apt update
sudo apt install nginx -y
# Create configuration file
sudo nano /etc/nginx/sites-available/myapp
server {
listen 80;
server_name example.com www.example.com;
# Static files (optional)
location /static/ {
alias /var/www/myapp/public/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass http://localhost:3000;
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;
proxy_read_timeout 86400;
}
}
# Enable the site
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Obtain SSL certificate
sudo certbot --nginx -d example.com -d www.example.com
# Test automatic renewal
sudo certbot renew --dry-run
A simple workflow for deploying updates:
# Pull new code
cd /var/www/myapp
git pull origin main
# Update dependencies
npm install --production
# Database migration (if applicable)
npm run migrate
# Zero-downtime restart with PM2
pm2 reload myapp
# Check logs
pm2 logs myapp --lines 50
On REXE servers, you can close port 3000 via Path Panel. Since Nginx routes traffic from ports 80/443, external access to your Node.js application port is not needed.
# Open required ports with ufw
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
Your Node.js application is now running in a production environment managed by PM2, behind an Nginx reverse proxy, and secured with SSL. With PM2's cluster mode you can fully utilize CPU cores, and with pm2 reload you can perform zero-downtime updates.
Check PM2 logs with 'pm2 logs myapp'. Common causes: missing environment variables, port conflict, or syntax error. Monitor real-time CPU/RAM usage with 'pm2 monit'. If there's a memory leak, add the 'max_memory_restart' parameter to ecosystem.config.js.
This error usually means the Node.js application is not running. Check that the app is in 'running' state with 'pm2 status'. Review error messages with 'pm2 logs'. Verify Nginx is proxying to the correct port with 'sudo nginx -t'.
Each application must use a different port (3000, 3001, 3002, etc.). Give each a unique name in PM2. Create a separate server block in Nginx for each domain and proxy to the relevant port. All apps become accessible via ports 80/443 through Nginx.
Be careful when switching versions with nvm. Install the new version, test your application, then update the default with 'nvm alias default'. You may also need to regenerate the PM2 startup script with the new Node.js path: run 'pm2 unstartup' then 'pm2 startup'.
Complete guide to deploying PHP applications on a VPS. LAMP vs LEMP comparison, Apache/Nginx + PHP-FPM + MySQL setup, virtual host configuration, and PHP version management.
Guide to installing and optimizing WordPress on a VPS. LEMP stack setup, wp-config.php settings, file permissions, SSL, OPcache performance optimization, and security hardening.
Guide to deploying Python/Django applications on a VPS. Virtualenv setup, Django production settings, Gunicorn WSGI server, Nginx reverse proxy, systemd service, and static file management.