Does Aegir Hosting System work with Nginx?

omega8cc's picture

The shortest answer is: YES.

But, you need a few important ingredients to enjoy your Aegir Hosting System with Nginx as a webserver:

* Latest 0.8.x Nginx installed - try_files required - introduced in 0.7.27 http://nginx.net/CHANGES
* Nginx catch-all configuration with Boost integration (in case Varnish is too powerful for you)
* Simple patch for provision_apache.drush.inc - see http://omega8.cc/dev/provision.patch.txt
* Easy to understand and use Aegir directories tree.

If you prefer standard locations explained in Aegir Documentation then just change them also in the above patch and in the attached below Nginx configuration.

Here it is - sample Nginx configuration to run Aegir Hosting System without the need to restart/reload your fast, small and powerful webserver (please remember to use /bin/true for Apache restart command during Aegir installation).

Please note that I only added some required details for Aegir setup and credit for this Nginx example configuration goes to @brianmercer. See also where it all started.

In below example Nginx is listening on port 88 since port 80 is used by Pound/Nginx load balancer or Varnish/Ncache fast proxy.

#######################################################
###  nginx.conf  BEGIN
#######################################################
#
pid                   /var/run/nginx.pid;
user                  www www;
worker_processes      4;
worker_rlimit_nofile  8192;

events {
    worker_connections  4096;
    use epoll;
}

http {
## MIME types
  include            /etc/nginx/fastcgi.conf;
  include            /etc/nginx/mime.types;
  default_type       application/octet-stream;

## Size Limits
  client_body_buffer_size         1k;
  client_header_buffer_size       1k;
  client_max_body_size           10m;
  large_client_header_buffers   3 3k;
  connection_pool_size           256;
  request_pool_size               4k;
  server_names_hash_bucket_size  128;

## Timeouts
  client_body_timeout             60;
  client_header_timeout           60;
  keepalive_timeout            75 20;
  send_timeout                    60;

## General Options
  ignore_invalid_headers          on;
  limit_zone gulag $binary_remote_addr 1m;
  recursive_error_pages           on;
  sendfile                        on;
  set_real_ip_from        0.0.0.0/32;
  real_ip_header     X-Forwarded-For;

## TCP options 
  tcp_nodelay on;
  tcp_nopush  on;

## Compression
  gzip              on;
  gzip_buffers      16 8k;
  gzip_comp_level   9;
  gzip_http_version 1.1;
  gzip_min_length   10;
  gzip_types        text/plain text/css image/png image/gif image/jpeg application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon;
  gzip_vary         on;
  gzip_static       on;
  gzip_proxied      any;
  gzip_disable      "MSIE [1-6]\.";

## Log Format
  log_format        main '"$http_x_forwarded_for" $host [$time_local] '
                         '"$request" $status $body_bytes_sent '
                         '$request_length $bytes_sent "$http_referer" '
                         '"$http_user_agent" $request_time "$gzip_ratio"';

  client_body_temp_path /var/cache/nginx/client_body_temp 1 2;
  access_log                   /var/log/nginx/access.log main;
  error_log                     /var/log/nginx/error.log crit;
     
#######################################################
###  nginx.conf catch-all
#######################################################

  server {
        limit_conn   gulag 10;
        listen       127.0.0.1:88;
        server_name  _;
        root         /data/u/$host/;
        index        index.php index.html;

    ## Deny some crawlers
    if ($http_user_agent ~* (HTTrack|HTMLParser|libwww) ) {
         return 444;
    }
    ## Deny certain Referers (case insensitive)
      if ($http_referer ~* (poker|sex|girl) ) {
        return 444;
    }
    ## www. redirect
    if ($host ~* ^(www\.)(.+)) {
        set $rawdomain $2;
        rewrite ^/(.*)$  http://$rawdomain/$1 permanent;
    }

    ## 6.x starts
    location / {
       #rewrite ^/(.*)/$ /$1 permanent; # remove trailing slashes - disabled
        try_files $uri @cache;
    }

    location @cache {
        if ( $request_method !~ ^(GET|HEAD)$ ) {
            return 405;
        }
        if ($http_cookie ~ "DRUPAL_UID") {
            return 405;
        }
        error_page 405 = @drupal;
        add_header Expires "Tue, 24 Jan 1984 08:00:00 GMT";       
        add_header Cache-Control "must-revalidate, post-check=0, pre-check=0";
        add_header X-Header "Boost Citrus 1.9";              
        charset utf-8;
        try_files /cache/$host${uri}_$args.html /cache/normal/$host${uri}_$args.html @drupal;
    }

    location @drupal {
        rewrite ^/\?q=([^.]+)$                    /index.php?q=$1 last;
        rewrite ^/(.*)\?token=([^.]+)$            /index.php?q=$1&token=$2 last;
        rewrite ^/([^.]+)\?t=([^.]+)$             /index.php?q=$1&t=$2 last;
        rewrite ^/([^.]+)\?string=([^.]+)$        /index.php?q=$1&string=$2 last;
        rewrite ^/([^.]+)\?destination=([^.]+)$   /index.php?q=$1&destination=$2 last;
        rewrite ^/([^.]+)\?page=([^.]+)$          /index.php?q=$1&page=$2 last;
        rewrite ^/([^.]+)\?filter0=([^\/]+)$      /index.php?q=$1&filter0=$2 last;
        rewrite ^/([^.]+)\?sort([^.]+)$           /index.php?q=$1&sort$2 last;
        rewrite ^/([^.]+)\?slide=([^.]+)$         /index.php?q=$1&slide=$2 last;
        rewrite ^/([^.]+)\?quote([^.]+)$          /index.php?q=$1&quote$2 last;
        rewrite ^/(.+)$                           /index.php?q=$1 last;
    }

    location ~* (/\..*|settings\.php$|\.(htaccess|engine|inc|info|install|module|profile|pl|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(Entries.*|Repository|Root|Tag|Template))$ {
        deny all;
    }

    location ~* /files/.*\.php$ {
        return 444;
    }
    location ~* /themes/.*\.php$ {
        return 444;
    }
      
    location ~ \.php$ {
          try_files $uri @drupal;       #check for existence of php file
          fastcgi_pass 127.0.0.1:9000;  #php-fpm listening on port 9000
          fastcgi_index index.php;
    }

    location ~ \.css$ {
        if ( $request_method !~ ^(GET|HEAD)$ ) {
            return 405;
        }
        if ($http_cookie ~ "DRUPAL_UID") {
            return 405;
        }
        error_page 405 = @uncached;
        access_log  off;
        expires  max; #if using aggregator
        add_header X-Header "Boost Citrus 2.1";
        try_files /cache/$host${uri}_.css /cache/perm/$host${uri}_.css $uri =404;
    }
   
    location ~ \.js$ {
        if ( $request_method !~ ^(GET|HEAD)$ ) {
            return 405;
        }
        if ($http_cookie ~ "DRUPAL_UID") {
            return 405;
        }
        error_page 405 = @uncached;
        access_log  off;
        expires  max; # if using aggregator
        add_header X-Header "Boost Citrus 2.2";              
        try_files /cache/$host${uri}_.js /cache/perm/$host${uri}_.js $uri =404;
    }

    location ~ \.json$ {
        if ( $request_method !~ ^(GET|HEAD)$ ) {
            return 405;
        }
        if ($http_cookie ~ "DRUPAL_UID") {
            return 405;
        }
        error_page 405 = @uncached;
        access_log  off;
        expires  max; # if using aggregator
        add_header X-Header "Boost Citrus 2.3";              
        try_files /cache/$host${uri}_.json /cache/normal/$host${uri}_.json $uri =404;
    }

    location @uncached {
        access_log  off;
        expires  max; # max if using aggregator, otherwise sane expire time
    }

    location ~* ^.+\.(jpg|jpeg|gif|png|ico)$ {
        access_log      off;
        expires         30d;
        try_files $uri =404;
    }

    location ^~ /files/imagecache/ {
        access_log         off;
        expires            30d;
        try_files $uri @drupal;  #imagecache support
    }

    location ~* \.xml$ {
        if ( $request_method !~ ^(GET|HEAD)$ ) {
            return 405;
        }
        if ($http_cookie ~ "DRUPAL_UID") {
            return 405;
        }
        error_page 405 = @drupal;
        add_header Expires "Tue, 24 Jan 1984 08:00:00 GMT";
        add_header Cache-Control "must-revalidate, post-check=0, pre-check=0";
        add_header X-Header "Boost Citrus 2.4";              
        charset utf-8;
        types { }
        default_type application/rss+xml;
        try_files /cache/$host${uri}_.xml /cache/normal/$host${uri}_.xml /cache/normal/$host${uri}_.html $uri @drupal;
    }

    location ~* /feed$ {
        if ( $request_method !~ ^(GET|HEAD)$ ) {
            return 405;
        }
        if ($http_cookie ~ "DRUPAL_UID") {
            return 405;
        }
        error_page 405 = @drupal;
        add_header Expires "Tue, 24 Jan 1984 08:00:00 GMT";
        add_header Cache-Control "must-revalidate, post-check=0, pre-check=0";
        add_header X-Header "Boost Citrus 2.5";              
        charset utf-8;
        types { }
        default_type application/rss+xml;
        try_files /cache/$host${uri}_.xml /cache/normal/$host${uri}_.xml /cache/normal/$host${uri}_.html $uri @drupal;
    }

  } # end of server

#######################################################
###  nginx.conf catch-all
#######################################################

}

And /etc/nginx/fastcgi.conf looks similar to:

# fastcgi.conf
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
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_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    ApacheSolaris/$nginx_version;
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

fastcgi_index  index.php;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

Example of custom nginx build:

cd /var/opt
wget http://sysoev.ru/nginx/nginx-0.8.29.tar.gz &&
tar -xzf nginx-0.8.29.tar.gz &&
cd nginx-0.8.29 &&
./configure --prefix=/usr \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--user=www \
--group=www \
--with-http_realip_module \
--with-rtsig_module \
--with-http_gzip_static_module \
--with-debug \
--with-http_stub_status_module \
--with-http_ssl_module \
--without-mail_pop3_module \
--without-mail_imap_module \
--without-mail_smtp_module \
--without-http_ssi_module \
--without-http_auth_basic_module \
--without-http_geo_module \
--http-client-body-temp-path=/var/cache/nginx/client_body_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp &&
make &&
make install
cd ./../
service nginx stop
killall -9 nginx
service nginx start

Example of custom php-fpm build:

cd /var/opt
tar -xzf php-5.2.11.tar.gz &&
tar -xzf php-fpm-0.6-5.2.11.tar.gz &&
sh php-fpm-0.6-5.2.11/generate-fpm-patch &&
gzip -d suhosin-patch-5.2.11-0.9.7.patch.gz
cd /var/opt/php-5.2.11
patch -p 1 -i ../suhosin-patch-5.2.11-0.9.7.patch
patch -p1 < ../fpm.patch

./buildconf --force
mkdir fpm-build && cd fpm-build

../configure --with-fpm \
--enable-fastcgi \
--with-mysql \
--with-mysqli \
--enable-force-cgi-redirect \
--enable-discard-path \
--with-zlib \
--with-curl \
--with-curlwrappers \
--with-gd \
--with-jpeg-dir=/usr/lib \
--with-pear \
--with-imap \
--with-imap-ssl \
--with-openssl \
--with-pdo-mysql \
--enable-soap \
--enable-ftp \
--enable-mbstring \
--enable-pcntl \
--enable-bcmath \
--with-kerberos \
--with-xsl \
--with-mcrypt &&
make &&
make install
cd ./../

~Grace

Groups:
Login to post comments

Thanks for config, its work

dicreat - Wed, 2009-09-23 07:53

Thanks for config, its work fine for me, expect one little problem - when I click "Administer" (link with URL /admin) I'm getting the content of /index.php (in site root directory). But all others links work fine - admin/build and etc. I' really don't know why...


@dicreat

omega8cc's picture
omega8cc - Wed, 2009-09-23 13:14

Are you using php-fpm for PHP/FastCGI ?

~Grace -- Turnkey Drupal Hosting on Steroids -- http://omega8.cc


omega8cc, my problem gone

dicreat - Sun, 2009-10-04 13:18

omega8cc, my problem gone away and now all work fine.
Thanks!


how to know if my php was built with --enable-force-cgi-redirect

superxain - Fri, 2009-10-09 08:57

I want to know whether I shall use "fastcgi_param REDIRECT_STATUS 200"


I've wondered about that line

brianmercer - Fri, 2009-10-09 14:11

I've wondered about that line in fastcgi_conf also.

My understanding is that it was a security feature for php-cgi that prevented .php files from being run directly from a URL (i.e. http:example.com/cgi-bin/example.php) by requiring that the "REDIRECT_STATUS 200" parameter be passed from the web server, typically in an apache rewrite.

I've commented it out and .php files still run fine, so I don't think the feature is commonly enabled if it even works with fastcgi these days.


I've wondered about that line

brianmercer - Fri, 2009-10-09 14:23

NT


I didn't tried PHP 5.3 on any

omega8cc's picture
omega8cc - Fri, 2009-10-09 14:46

I didn't tried PHP 5.3 on any production server, but if this comment is true, then

fastcgi_param  REDIRECT_STATUS    200;

can be still valid.

"It appears that as of 5.3.0, --enable-force-cgi-redirect is not a valid configure option. A quick review of the 5.3.0 code indicates that it the logic previously enabled by specifying the --enable-force-cgi-redirect configure option is being built into php by default."

~Grace


Update

omega8cc's picture
omega8cc - Sat, 2009-11-07 21:14

Added examples for custom nginx and php-fpm build.

~Grace -- Drupal on Steroids -- http://omega8.cc


Note: nginx/0.7.62 comes with

Carl Johan's picture
Carl Johan - Tue, 2009-11-10 00:08

Note: nginx/0.7.62 comes with ubuntu 9.10 karmic koala and has support for try_files


Thx for the configs,

brianmercer - Tue, 2009-11-10 00:45

Thx for the configs, omega8cc. I've been using the jeff waugh repos compiled for hardy.
I just installed fcgiwrap to let me mess with awstats and nagios using just nginx and php5-cgi. I'm going to work on collectd next.


Configuration Update

omega8cc's picture
omega8cc - Tue, 2009-12-01 16:19

Added/Fixed:

  1. latest Boost compatibility for /cache/normal & /cache/perm
  2. json cache for Boost
  3. fix for xml/feed Boost cache files with .html extension
  4. fix for xml/feed Boost cache correct mime type

~Grace -- Drupal on Steroids -- http://omega8.cc


Update for Aegir/provision patch

omega8cc's picture
omega8cc - Tue, 2009-12-01 16:47

To revert Apache-only latest Aegir changes, so it is still possible to use Nginx with latest Aegir 0.4 alpha3.

http://omega8.cc/dev/provision.patch.txt

~Grace -- Drupal on Steroids -- http://omega8.cc


Nginx as reverse proxy for Apache

manogolf - Fri, 2009-12-11 03:40

If I had the courage I would setup Aegir on Nginx following your instructions but that's for another day. For now I would like to use Nginx as a proxy for the usual static content but am unable to make it work with Aegir created sites.

Am I starting off rudely and inappropriate? This was the only post I could find that was remotely related to Aegir and Nginx. Bounce me along without hesitation if so.

If not out of line perhaps you might help me understand a way to make these two play together.
I see examples for user as www www and/or www data. Which is correct?
Root is really throwing me. Do you proceed identifying the path through the platform down to domain.com or stop at platform, /var/aegir/platform/6.14 or /var/aegir/platform/6.14/sites/example.com
I can't get either to work but knowing which is correct would help. Or use /var/aegir or ???

Any help is appreciated.