≡ Menu

Debian Web Server: Nginx with APC and Varnish to Optimize WordPress

This tutorial is how to make a Web Server under Debian 6 using Nginx web server, MySQL as a database server, PHP-FPM as a fastCGI server, APC as an optcode cache and Varnish as a caching proxy to optimize a website using wordpress.

I have tried this method using Debian 6.0.5 32BIT on XEN VPS with 512 Memory.

Step 1: Adding main debian repos

Because i’m using idROOT as Indonesia VPS Provider, so i’m using Indonesian repository which is located at Kambing.ui.ac.id

deb http://kambing.ui.ac.id/debian/ squeeze main contrib non-free
deb http://kambing.ui.ac.id/debian/ squeeze-updates main contrib non-free
deb http://kambing.ui.ac.id/debian-security/ squeeze/updates main contrib non-free

Step 2: Adding dot-deb repo

# echo deb http://packages.dotdeb.org stable all >> /etc/apt/sources.list
# echo deb-src http://packages.dotdeb.org stable all >> /etc/apt/sources.list
# wget http://www.dotdeb.org/dotdeb.gpg
# cat dotdeb.gpg | apt-key add -

Step 3: Update and adding base software

# apt-get update && apt-get upgrade
# apt-get install nano sudo curl lsb-release

Step 4: Adding Varnish repo and Install Varnish

# curl http://repo.varnish-cache.org/debian/GPG-key.txt | apt-key add -
# echo "deb http://repo.varnish-cache.org/debian/ $(lsb_release -s -c) varnish-3.0" >> /etc/apt/sources.list.d/varnish.list
# apt-get update
# apt-get install varnish

Step 5: Adding a system user and Add user to sudo

# adduser [USER]
# visudo

find: root ALL and then add this line below : [USER] ALL=(ALL) ALL

Step 5: Installing Software

# apt-get install nginx mysql-server mysql-client memcached php5 php-apc php-auth php-net-smtp php-net-socket php-pear php5-curl php5-gd php5-mcrypt php5-mysql php5-fpm php5-memcached php5-tidy

Step 6: Configuring MySql

# mv /etc/mysql/my.cnf /etc/mysql/my.cnf.bak
# nano /etc/mysql/my.cnf

Insert this code into /etc/mysql/my.cnf

[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = -5
open_files_limit = 8192

[mysqld]

user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
skip-external-locking
bind-address = 127.0.0.1
key_buffer = 16M
max_allowed_packet = 16M
thread_stack = 192K
thread_cache_size = 8
myisam-recover = BACKUP
query_cache_limit = 4M
query_cache_size = 250M
query_cache_type = 1
query_prealloc_size = 65K
query_alloc_block_size = 128K
tmp_table_size = 32M
wait_timeout=500
expire_logs_days = 10
max_binlog_size = 100M

[mysqldump]
quick
quote-names
max_allowed_packet = 16M

[mysql]
no-auto-rehash # faster start of mysql but no tab completition

[isamchk]
key_buffer = 16M
sort_buffer = 8M
read_buffer = 4M
write_buffer = 4M
!includedir /etc/mysql/conf.d/

Step 6: Configuring nginx

# mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
# nano /etc/nginx/nginx.conf

Insert this code into /etc/nginx/nginx.conf

user www-data;
worker_processes 1;
pid /var/run/nginx.pid;
events {
use epoll;
worker_connections 512;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
error_log /var/log/nginx/error.log;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 3;
server_tokens off;
access_log off;
client_max_body_size 32m;
client_body_timeout 60;
client_header_timeout 60;
send_timeout 60;
reset_timedout_connection on;
gzip on;
gzip_disable "MSIE [1-6].(?!.*SV1)";
gzip_vary on;
gzip_static on;
gzip_proxied any;
gzip_comp_level 9;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
# /home/[USER]/logs/
# nano /etc/nginx/sites-available/default

Insert this code into /etc/nginx/sites-available/default

server {
listen 8080;
server_name www.asep.us asep.us;
access_log /home/asep/logs/access.log;
error_log /home/asep/logs/error.log;
root /home/asep/www;
location / {
index index.php index.html index.htm;
try_files $uri $uri/ /index.php?$args;
}

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# Directives to send expires headers and turn off 404 error logging.
location ~* .(js|css|png|jpg|jpeg|gif|ico)$ {
expires 24h;
log_not_found off;
}

# Pass uploaded files to wp-includes/ms-files.php.
rewrite /files/$ /index.php last;

location ~ .php$ {
include fastcgi_params;
if ($uri !~ "^/images/") {
fastcgi_pass 127.0.0.1:9000;
}
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_intercept_errors on;
fastcgi_ignore_client_abort off;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 360;
fastcgi_read_timeout 360;
fastcgi_buffer_size 128k;
fastcgi_buffers 8 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/asep/www$fastcgi_script_name;
}
location ~ /.htaccess { deny all; log_not_found off; access_log off; }
location ~ /.htpasswd { deny all; log_not_found off; access_log off; }
location = /favicon.ico { allow all; log_not_found off; access_log off; }
location = /robots.txt { allow all; log_not_found off; access_log off; }
}

Step 7: Fix CGI Path Info

# nano /etc/php5/fpm/php.ini

find cgi.fix_pathinfo and Uncomment the command by removing the ; in front and replace the defauly 1 with a 0!

Step 8: Configuring PHP5-FPM

# mv /etc/php5/fpm/php-fpm.conf /etc/php5/fpm/php-fpm.conf.bak
# nano /etc/php5/fpm/php-fpm.conf

Insert this code into /etc/php5/fpm/php-fpm.conf

[global]
pid = /var/run/php5-fpm.pid
error_log = /var/log/php5-fpm.log
log_level = notice
emergency_restart_threshold = 5
emergency_restart_interval = 2
process_control_timeout = 2
daemonize = yes
include=/etc/php5/fpm/pool.d/*.conf
# mv /etc/php5/fpm/pool.d/www.conf /etc/php5/fpm/pool.d/www.conf.bak
# nano /etc/php5/fpm/pool.d/www.conf

Insert this code into /etc/php5/fpm/pool.d/www.conf

[www]
;prefix = /path/to/pools/$pool
listen = 127.0.0.1:9000
listen.backlog = -1
listen.allowed_clients = 127.0.0.1
listen.owner = [USER]
listen.group = [USER]
listen.mode = 0666
user = [USER]
group = [USER]
pm = dynamic
pm.max_children = 15
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 0
pm.status_path = /fpmstatus
ping.path = /ping
ping.response = pong
request_terminate_timeout = 10
request_slowlog_timeout = 10
slowlog = /var/log/$pool.log.slow
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
;chdir = /
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f [email protected]
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 32M
php_admin_value[date.timezone] = Asia/Jakarta
php_value[upload_max_filesize] = 10M
php_value[max_execution_time] = 120

Step 9: Configuring Varnish

# mv /etc/default/varnish /etc/varnish/default.bak
# nano /etc/default/varnish

Insert this code into /etc/default/varnish

START=yes
NFILES=131072
MEMLOCK=82000
DAEMON_OPTS="-a :80 
-T localhost:6082 
-f /etc/varnish/default.vcl 
-S /etc/varnish/secret 
-s malloc,1G"
mv /etc/varnish/default.vcl /etc/varnish/default.vcl.bak
nano /etc/varnish/default.vcl

Insert this code into /etc/varnish/default.vcl

backend default { .host = "localhost"; .port = "8080"; }
acl purge { "localhost"; }
sub vcl_recv { if (req.request == "PURGE") { if (!client.ip ~ purge) { error 405 "Not allowed."; } return(lookup); }
if (req.url ~ "^/$") { unset req.http.cookie; } }
sub vcl_hit { if (req.request == "PURGE") { set obj.ttl = 0s; error 200 "Purged."; } }
sub vcl_miss { if (req.request == "PURGE") { error 404 "Not in cache."; }
if (!(req.url ~ "wp-(login|admin)")) { unset req.http.cookie; }
if (req.url ~ "^/[^?]+.(jpeg|jpg|png|gif|ico|js|css|txt|gz|zip|lzma|bz2|tgz|tbz|html|htm)(?.|)$") {
unset req.http.cookie;
set req.url = regsub(req.url, "?.$", "");
}
if (req.url ~ "^/$") { unset req.http.cookie; } }
sub vcl_fetch { if (req.url ~ "^/$") { unset beresp.http.set-cookie; }
if (!(req.url ~ "wp-(login|admin)")) { unset beresp.http.set-cookie; }}

step 10: Securing VPS

Securing SSH

# nano /etc/ssh/sshd_config

–find: PermitRootLogin yes
–change to: PermitRootLogin no

–find: X11Forwarding yes
–change to: X11Forwarding no

–find: Port 22
–change to: 7766

–add to end: UseDNS no

Restart SSH

# servive sshd restart

Installing IPTABLES

# apt-get install iptables

// Show what is loaded in our firewall

# iptables -L

// Flush everything we have in our firewall

# iptables -F

// Allow established sessions

# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

// Allow services

iptables -A INPUT -p tcp --dport 7766 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
iptables -A INPUT -p tcp --dport 9000 -j ACCEPT
iptables -A INPUT -p tcp --dport 12000:12100 -j ACCEPT

// End iptables by blocking everything that we didn’t explicitly allowed earlier

iptables -A INPUT -j DROP

// backup iptables to a file

iptables-save > /home/[USER]/iptables.rules

Making sure iptables rules are reloaded on reboot

# nano /etc/network/if-up.d/iptables

Insert this code

#!/bin/sh
iptables-restore < /home/[USER]/iptables.rules
# chmod +x /etc/network/if-up.d/iptables

Restartig our services

# service mysql restart
# service memcached restart
# service varnish restart
# service nginx restart
# /etc/init.d/php5-fpm restart

Reference:
http://www.axelsegebrecht.com/how-to/install-nginx-apc-varnish-wordpress-and-w3-cache-128mb-vps/
http://library.linode.com/lemp-guides/debian-6-squeeze
http://www.farinspace.com/wordpress-nginx-rewrite-rules/

{ 0 comments… add one }

Leave a Comment

CAPTCHA
*