AWS EC2 + Debian + Nginx + WordPress + Varnish

One AWS EC2 micro instance + Debian + Nginx + WordPress + Varnish = one year free hosting, sort of.

First launch a fresh instance of Debian (or Ubuntu)

Most of our work today will be run as root user, so let’s switch over into the root user’s privileges.:

sudo -i 

In case your image is a bit out of date, now is a good time to update the apt repositories.

apt-get update

Who are these amazing people who volunteer late nights keeping all these packages up to date?. Whoever you are, thank you.

Step One – Install Software


apt-get install mysql-server mysql-client 

You will be prompted to set and confirm your ‘root’ SQL password – remember this.


apt-get install nginx 

Start it up:

/etc/init.d/nginx start 

Make sure you are now seeing your ‘Welcome to nginx!’ page by visiting your Public DNS URL:

AWS firewalls If you can’t load that page above and it simply disconnects after a few minutes, you probably haven’t set up an AWS EC2 security group that allows incoming traffic over port 80. Add that incoming traffic in your AWS console. Probably a good idea to ensure only port 22 (ssh) and 80 (http) are open from your box.


nginx pipes php to a php-fpm backend rather than loading it and processing it in its own memory. So we’ll need not just php, but php-fpm as well.

apt-get install php5-fpm php5-mysql 

Step Two – Configure nginx & php-fpm

On debian/ubuntu, nginx hosts are configured here:


Edit your configuration files in the ‘sites-available’ directory, and when you want to enable them, just symlink them in the ‘sites-enabled’ directory.

Lets take a look at the default configuration. Run this command to open it up:

vi /etc/nginx/sites-available/default 

Add ‘index.php’ to the ‘index’ line, so a php index page will get picked up as an index.

index index.html index.htm index.php; 

And then uncomment the following so the lines below are active, like so:

location ~ .php$ {
  fastcgi_split_path_info ^(.+.php)(/.+)$;
  fastcgi_pass unix:/var/run/php5-fpm.sock;  
  fastcgi_index index.php; include fastcgi_params;

Confirm that PHP-FPM is set up to use the Unix Socket as specified in our default configuration.

vi /etc/php5/fpm/pool.d/www.conf 

And then find the line that starts with listen = . Make sure it looks like this.

listen = /var/run/php5-fpm.sock 

There’s a lot of debate out there whether to use the unix socket or I don’t know who’s right.

Time to restart php5-fpm and nginx:

/etc/init.d/php5-fpm restart
/etc/init.d/nginx restart 

We can test if php is working now by adding the following snippet of php to a file in the server’s document root:

vi /usr/share/nginx/www/info.php 

P.H.P this.

<?php phpinfo(); ?> 

Now you should be able to see the PHP info page by going to: 

It looks like nginx is working with PHP. That’s good.

Uh, there’s a lot of precious information in that info.php file. Throw it away once you’ve confirmed php is working.

Setup your host or virtual host

If you’re certain you only will run web site on this box, just edit the default nginx configuration to reflect what you need. If you have any doubts and might want to run more than one site, you might as well start laying the files out correctly creating virtual hosts. In this example, you just need to change ‘’ to match your domain, and make sure the root path to the public files is pointing where you are planning on serving http documents from. Let’s config!

vi /etc/nginx/sites-available/ 

And then copy this configuration into it, after changing the domain:

server {
   listen 80;
   root /var/www/;

   index index.php index.html;
   location = /favicon.ico {
            log_not_found off;
            access_log off;
   location = /robots.txt {
            allow all;
            log_not_found off;
            access_log off;
   # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
   location ~ /\. {
            deny all;
            access_log off;
            log_not_found off;
   location / {
            try_files $uri $uri/ /index.php?$args;
   # Add trailing slash to */wp-admin requests.
   rewrite /wp-admin$ $scheme://$host$uri/ permanent;
   location ~*  \.(jpg|jpeg|png|gif|css|js|ico)$ {
            expires max;
            log_not_found off;
   location ~ \.php$ {
            try_files $uri =404;
            include /etc/nginx/fastcgi_params;
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Enable the host

Now lets sym-link the site to enable it, and we’ll also remove the default configuration:

cd /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/
rm default 

We don’t need to restart nginx. Just reload it to capture your new configuration.

/etc/init.d/nginx reload 

Make a place for the site’s application code.

We configured our virtual host to serve and process files from a root directory. But we don’t have that directory yet. Let’s fix that.

mkdir -p /var/www/ 

Step Three – Install your apps (WordPress)

Download it. If it downloads too fast, you can use various proxy tools to slow down your network data delivery.

cd /tmp
tar xvfz latest.tar.gz
cd wordpress/
mv * /var/www/ 

Set up MySQL for your WordPress installation

Before we can actually set up our wordpress app, we need to make MySQL behave. Since this is an example we’ll call the database ‘wp_database’ the username for the user connecting to this database will be ‘wp_user’ and the password ‘wp_password’ – They say you should replace these with your own values, but what do they know?

Creating the database:

mysqladmin -u root -p create wp_database 

Adding the User

Connect to the mysql server as the mysql root user like this:

mysql -u root -p 

Run these queries to add your wordpress user and allow the user to actually work with the database:

GRANT ALL PRIVILEGES ON wp_database.* TO 'wp_user'@'localhost' IDENTIFIED BY 'wp_password';
GRANT ALL PRIVILEGES ON wp_database.* TO 'wp_user'@'localhost.localdomain' IDENTIFIED BY 'wp_password'; FLUSH PRIVILEGES; quit; 

That last line about flushing privileges is not a political statement. If you neglect it, all the privileges you just granted user wp_user will not take effect and you’ll be posting to a lot of forums.

WordPress requires that the web server be able to write to some of the files in the wordpress codebase. So we need to make sure nginx & php-fpm own the files. (On debian, php-fpm runs as user www-data, as does nginx:

chown -R www-data:www-data /var/www/ 

Move the WordPress configuration file, we’ll use it, but we just need to move it into place with:

mv /var/www/ /var/www/ 

Now let’s add our sql setting:

vi /var/www/ 

Now add your credentials:

/** The name of the database for WordPress */
define('DB_NAME', 'wp_database');
/** MySQL database username */
define('DB_USER', 'wp_user');
/** MySQL database password */
define('DB_PASSWORD', 'wp_password'); 

Launch WordPress

To run the first time setup on WordPress visit: 

Use the configuration screens to set up your site name and first administrator user. You should now be able to log onto WordPress. Nota bene: You can’t change the username of that user once you’ve set it up, so put a tiny bit of thought into it.

Step Four: Install Varnish

We want the site to be faster! Let’s get Varnish installed. By default nginx will be listening to port 80, but Varnish needs to handle those requests and pass them back to nginx. So now we change our nginx configuration to listen to a different port:

vi /etc/nginx/sites-available/ 

Change the listen configuration so it looks like this:


This now means that we can run Varnish on port 80.

Time to install varnish. First we have to add the repo for varnish to our apt configuration.

curl | apt-key add -
echo "deb lucid varnish-3.0" >> /etc/apt/sources.list

Then, install it.

apt-get update
apt-get install varnish

Configure Varnish

Remember how nginx used to listen on port 80 and we changed it to listen to 8080 instead? Now we need varnish to take over the http port 80 traffic, like this:

vi /etc/default/varnish 

Then change this line so it reads like so:

DAEMON_OPTS="-a :80  

Now, edit the varnish configuration file:

vi /etc/varnish/default.vcl 

Edit the file so it looks like so:

backend default {
  .host = "";
  .port = "8080";
# Drop any cookies sent to WordPress.
sub vcl_recv {
    if (!(req.url ~ "wp-(login|admin)")) {
        unset req.http.cookie;

# Drop any cookies WordPress tries to send back to the client.
sub vcl_fetch {
    if (!(req.url ~ "wp-(login|admin)")) {
        unset beresp.http.set-cookie;

Restart varnish and nginx.

/etc/init.d/nginx restart
/etc/init.d/varnish restart

Finishing up

You’ll notice that when you make a change to a post, you won’t see it, since varnish is caching it. I recommend the W3 Total Cache WordPress plugin. It purges the varnish cache when you publish a post. Enable these things:

  • Page Cache: Enabled – Opcode: Alternative PHP Cache (APC)
  • Object Cache: Enabled – Opcode: Alternative PHP Cache (APC)
  • Browser Cache: Enabled
  • Varnish: Enabled – Varnish Servers: localhost

A lot of people put in a ton of unpaid work to make these things function so well. And yet the people on the cover of so called tech magazines are only the venture capitalists and start-uppers.

2 Comments to “AWS EC2 + Debian + Nginx + WordPress + Varnish”  

  1. 1 Marko

    Thanks! port=8080 in default.vcl was crucial to me! i’ve been setting it at 80.. spent whole day :’-)

  2. 2 Alexis

    I have checked geearetnd .conf files sometime back and I tried to cover everything in above configuration.Does Browser Caching option is responsible for adding some rules into .conf file only?I will check .conf file on my end again.By the way, I just verified browser caching for this site by using following command:curl -I As expected, Nginx returned correct Expires header in its output.Expires: Thu, 31 Dec 2037 23:55:55 GMTPlease correct me if I am missing anything here.

    [ I am not sure about configuring browser caching beyond the defaults that came with the installation procedure listed above. Sorry I can’t be of more help. -Ed.]