Nginx, Node, MySQL on VPS

This is a writeup to setup full node stack on a VPS instance. The min memory requirement for the VM is 256M, but ideally should be 512M or higher. I'm currently using VPS instances from www.ramnode.com. It describes how to setup a VPS (virtual private server) to run Nginx, Node.js and MySQL on CentOS 6.x.

Here's a quick overview of how to secure a VPS (virtual private server) running CentOS 6.x and configure it.

  1. Secure the server
  2. Setup iptables
  3. Setup MySQL
  4. Setup Nginx
  5. Setup Node.js

SECURE THE SERVER

  • passwd root
  • useradd myuser
  • passwd myuser
  • visudo
    • Add line: myuser    ALL=(ALL)    ALL
  • mkdir ~/.ssh
  • cat id_rsa.pub >> ~/.ssh/authorized_keys
  • chmod 700 ~/.ssh
  • chmod 600 ~/.ssh/authorized_keys
  • vi /etc/ssh/sshd_config
    • PermitRootLogin no
    • PasswordAuthentication no
  • sudo service sshd restart
  • nmap -sV 123.45.67.89

SETUP NAMESERVER

Originally I was running the nameserver on the VPS itself. It works perfectly fine, its secure and doesn't use too much memory, but it does not provide the resiliency that you want for DNS. I started using Amazon Route53 and its cheap enough that I've switched to it completely for all my websites. Recently I moved by DNS servers to Google Cloud DNS Incase you're still interested here are the instructions for nameserver setup on CentOS.

SETUP IPTABLES

If you start with a barebones instance of CentOS or RedHat you must configure firewall rules using iptables to prevent your server from being compromised. Its surprising that without hours of spinning off a new VPS or Amazon EC2 instance you can see attempts to breakin to the server as root user via SSH. Its a good practice to disable SSH for root and also drop SSH session after N failed attempts. Also, close all ports except for the ones you plan on using like 22, 80, 443, etc. You can follow the steps outlined in IPTables for CentOS to setup firewall using iptables.

SETUP MYSQL

  1. Install MySQL 5.5 or 5.7 using yum
  2. Configure low memory settings in my.cnf
  3. Setup auto-start for mysqld service
  4. Secure the MySQL installation
# ------Installing MySQL 5.5 (low memory ~36M)------
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm  
sudo rpm -Uvh https://mirror.webtatic.com/yum/el6/latest.rpm  
suo yum install mysql55w mysql55w-server  
sudo vi /etc/my.cnf  
sudo service mysqld start  
sudo chkconfig --level 235 mysqld on  
/usr/bin/mysql_secure_installation

# ------Installing MySQL 5.7 (memory usage ~124M)------
wget http://dev.mysql.com/get/mysql57-community-release-el6-7.noarch.rpm  
sudo yum localinstall mysql57-community-release-el6-7.noarch.rpm  
sudo yum repolist enabled | grep "mysql.*-community.*"  
sudo yum install mysql-community-server  
sudo vi /etc/my.cnf  
sudo service mysqld start  
sudo chkconfig --level 235 mysqld on  
# -- check for errors in /var/log/messages
# -- check for errors and temp root password in /var/log/mysqld.log
/usr/bin/mysql_secure_installation
mysql -u root -p  
    show plugins;
    uninstall plugin validate_password;
    set password for 'root'@'localhost' = 'new_pass'
# File: /etc/my.cnf
# --- Add config lines after [mysql_safe] block

# BSH: low memory for MySQL 5.5 (~36M)
character-set-server=utf8  
collation-server=utf8_general_ci

# BSH: low memory for MySQL 5.7 (~124M)
explicit_defaults_for_timestamp = TRUE  
table_open_cache=4  
query_cache_limit=256K  
query_cache_size=4M  
max_allowed_packet=1M  
sort_buffer_size=64K  
read_buffer_size=256K  
thread_stack=64K  
innodb_buffer_pool_size = 56M  
innodb_flush_neighbors=0  
innodb_flush_log_at_trx_commit=2  
character-set-server=utf8  
collation-server=utf8_general_ci  

SETUP NGINX

  1. Install nginx yum repository
  2. vi /etc/yum.repos.d/nginx.repo
    [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/6/$basearch/ gpgcheck=0 enabled=0
  3. yum --enablerepo=nginx install nginx
  4. chkconfig --list httpd
  5. chkconfig --level 3 httpd off
  6. chkconfig --level 3 nginx on
  7. service nginx start
  8. vi /usr/share/nginx/html/index.html
  9. vi /etc/nginx/nginx.conf
  10. vi /etc/nginx/conf.d/default.conf

/etc/nginx/nginx.conf

user  nginx;  
worker_processes  1;  
error_log  /var/log/nginx/error.log warn;  
pid        /var/run/nginx.pid;

events {  
    worker_connections  1024;
}

http {  
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;

  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;

  sendfile on;
  keepalive_timeout 65;
  gzip on;
  server_tokens off;
  include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/default.conf

server {  
  listen       80;
  listen       [::]:80;
  server_name  localhost mydomain.com www.mydomain.com;
  root         /usr/share/nginx/html;

  etag on;

  location / {
    proxy_pass http://127.0.0.1:5050;
    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_set_header HOST $http_host;
    proxy_hide_header X-Powered-By;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }
}

server {  
  listen       443 ssl;
  listen       [::]:443 ssl;
  server_name  localhost mydomain.com www.mydomain.com;
  root         /usr/share/nginx/html;

  #BSH: set ssl cert properties
  keepalive_timeout     70;
  ssl_certificate       /etc/ssl/certs/www_mydomain_com.pem;
  ssl_certificate_key   /etc/ssl/certs/www_mydomain_com_private.key;
  ssl_session_cache     shared:SSL:10m;
  ssl_session_timeout   10m;

  etag on;

  location / {
    proxy_pass http://127.0.0.1:5050;
    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_set_header HOST $http_host;
    proxy_hide_header X-Powered-By;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }
}

SETUP NODE.JS

SETUP PM2

  • su root
  • npm install pm2 -g
  • vi /etc/init.d/nodejs
  • chmod 755 /etc/init.d/nodejs
  • chkconfig --add nodejs
  • chkconfig --list nodejs
  • vi /etc/logrotate.d/pm2
  • service nodejs start

/etc/init.d/nodejs

#!/bin/bash
#
# file: /etc/init.d/nodejs
# processname: nodejs
#
# chkconfig: 345 80 20
#
### BEGIN INIT INFO
# Provides: nodejs
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start: $network
# Should-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO

export NODE_ENV=production  
export HOME=/home/my_user

NAME=nodejs  
PM2=/usr/lib/node_modules/pm2/bin/pm2  
NODE=/usr/bin/node  
USER=my_user

APP_DIR=/home/my_user/my_app  
APP_START=app.js  
APP_NAME=$NAME

super() {  
    su -l $USER -c "$1 $2 $3 $4 $5 $6 $7 $8"
}

start() {  
    echo "Starting $NAME"
    cd $APP_DIR
    super NODE_ENV=production $NODE $PM2 start ${APP_DIR}/${APP_START} -x --name $APP_NAME
}

stop() {  
    echo "Stopping $NAME"
    cd $APP_DIR
    super NODE_ENV=production $NODE $PM2 stop ${APP_DIR}/${APP_START} -x --name $APP_NAME
}

restart() {  
    echo "Restarting $NAME"
    stop
    start
}

status() {  
    echo "Status for $NAME:"
    cd $APP_DIR
    super $NODE $PM2 list
    RETVAL=$?
}

case "$1" in  
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        status
        ;;
    restart)
        restart
        ;;
    *)
        echo "Usage: {start|stop|status|restart}"
        exit 1
        ;;
esac  
exit $RETVAL  

/etc/logrotate.d/pm2

/home/my_user/.pm2/pm2.log /home/my_user/.pm2/logs/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    copytruncate
    dateext
}

Baldeep Hira

bay area programmer working on mobile/tablet/web apps and enterprise cloud apps; ui/ux, html5 and everything else for a prettier web and world

  • San Francisco Bay Area
comments powered by Disqus