Nginx, PHP on VPS

This is an update to the original post for setting up Apache, PHP on CentOS. Last year has been great and there is an abundance of vendors providing VPS on the cheap. My goto website for checking the latest on VPS vendors is www.lowendbox.com . For this post I went with a tiny 128MB VPS from www.ramnode.com . This server setup is for Cent OS 6.x with Nginx + PHP-FPM + MySQL. It describes how to setup a VPS (virtual private server) to run Nginx, PHP and MySQL on CentOS. The VPS uses a tiny amount of memory (total server memory is 128MB) and is running on CentOS 6.x. The intent is to setup a fully functional server that has been secured using iptables and configured to use Nginx, PHP-FPM and MySQL for production. It uses CentOS services to manage all the applications and is configured to use log rotation.

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

  • Secure the server
  • Setup nameserver
  • Setup iptables
  • Setup MySQL
  • Setup PHP5
  • Configure Nginx
  1. passwd root
  2. useradd myuser
  3. passwd myuser
  4. visudo
  5. Add line: myuser ALL=(ALL) ALL
  6. mkdir ~/.ssh
  7. cat id_rsa.pub » ~/.ssh/authorized_keys
  8. chmod 700 ~/.ssh
  9. chmod 600 ~/.ssh/authorized_keys
  10. vi /etc/ssh/sshd_config
  11. PermitRootLogin no
  12. PasswordAuthentication no
  13. sudo service sshd restart
  14. nmap -sV 123.45.67.89

In my original version 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. Incase you’re still interested here are the instructions for nameserver setup on CentOS.

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.

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

# ------Installing MySQL 5.7 (memory usage ~124M)------
wget http://dev.mysql.com/get/mysql57-community-release-el6-11.noarch.rpm
sudo yum localinstall mysql57-community-release-el6-11.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/mysqlsecureinstallation
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
  1. yum install php php-fpm php-common php-mysql php-pdo php-pecl-apc php-cli php-mcrypt php-xml php-gd php-mbstring
  2. vi /etc/php.ini
  • expose_php = Off
  • display_errors = Off
  • session.name = OKID
  • session.cookie_httponly = 1
  1. chkconfig –levels 235 php-fpm on
  2. service php-fpm start
  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
  1. yum –enablerepo=nginx install nginx
  2. chkconfig –list httpd
  3. chkconfig –level 3 httpd off
  4. chkconfig –level 3 nginx on
  5. service nginx start
  6. vi /usr/share/nginx/html/index.html
  7. vi /etc/nginx/nginx.conf
  8. vi /etc/nginx/conf.d/default.conf
  9. nginx log rotation - /etc/logrotate.d/nginx
  • edit config: vi /etc/logrotate.d/nginx
  • test run: /etc/cron.daily/logrotate
#FILE: /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;
}
#FILE: /etc/nginx/conf.d/default.conf

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

  #BSH: enable etag
  etag on;

  location / {
    index  index.html index.php;

    #BSH: redirect all non file, non dir requests to index.php
    try_files $uri $uri/ /index.php;
  }

  #BSH: ensure all /login requests use https
  location ^~ /login {
      return 301 https://$host$request_uri;
  }

  #BSH: ensure all /app requests use https
  location ^~ /app {
      return 301 https://$host$request_uri;
  }

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

  # -----pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000-----
  location ~ \.php$ {
    root           /usr/share/nginx/html;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }
}

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;
  ...
}
  1. cd /etc/init.d
  2. vi named
  3. chkconfig –list
  4. chkconfig –level 235 named on
  5. chkconfig –level 3 nginx on
  6. chkconfig –level 3 httpd off