Nginx is my preferable webserver, known for its small memory footprint and great performance. Serendipity is a reasonably complete blog server which I picked over WordPress because it works with PostgreSQL database. I failed to find instructons how to run those two together, so here is my description. I use Ubuntu, so some details (like package naming and the way they are installed) are Debian/Ubuntu-like, but it should not be too difficult to follow on another distribution.
Before we start
Let's assume that Nginx and PHP are already installed, for example after
$ apt-get install nginx php5-cgi php5
(and usual review of /etc/nginx
and /etc/php5
).
Configuring Fast CGI processess
Nginx does not handle spawning Fast CGI processess, therefore they must be run separately. There are a few suitable apps, the one people usually recommend for PHP is spawn-fcgi
program available as part of lighttpd package. So:
$ apt-get install lighttpd
As I do not want to have lighttpd running, followed by:
$ update-rc.d -f lighttpd remove
(this just removes all those /etc/rc*.d/
links)
Now I must ensure spawn-fcgi
will be started by my system.
As I use runit, I created /etc/sv/spawnfcgi
directory and the file /etc/sv/spawnfcgi/run
with the following content:
#!/bin/sh CHILD_COUNT=5 PORT=53217 USER=www-data GROUP=www-data exec /usr/bin/spawn-fcgi -f /usr/bin/php-cgi \ -a 127.0.0.1 -p $PORT \ -P /var/run/fastcgi-php.pid \ -u $USER -g USER \ -C $CHILD_COUNT \ -n
Here, CHILD_COUNT
is a number of FastCGI processess which will run (tune it to your needs), PORT
is some spare TCP port which will be used by nginx, and USER
and GROUP
specify under whose account are PHP processess to run.
To actually start them:
$ chmod a+x /etc/sv/spawnfcgi/run $ ln -s /etc/sv/spawnfcgi /var/service/
In at most few seconds php-cgi
processess started (the way runit works), as ps waux | grep php
confirmed.
If you prefer standard /etc/init.d
, just write appropriate script using the command like above to start spawn-fcgi
. Omit -n
(stay in foreground) in such case.
Actual Nginx configuration
As I run two blogs on the same machine, my configuration is split into some parts. Let's start from
/etc/nginx/sites-available/plblog
:
server { listen 80; server_name notatnik.mekk.waw.pl; access_log /var/log/nginx/notatnik.mekk.access.log combined; root /var/blog/serendipity_plblog; # This is a directory where I unpacked serendipity # distribution for this blog include /etc/nginx/blog_common.nginx; } server { listen 443; server_name notatnik.mekk.waw.pl; access_log /var/log/nginx/ssl.notatnik.mekk.access.log combined; ssl on; ssl_certificate /etc/nginx/mekk_cert.pem; ssl_certificate_key /etc/nginx/mekk_key.pem; root /var/blog/serendipity_plblog; include /etc/nginx/blog_common.nginx; }
Second blog is almost identical (/etc/nginx/sites-available/enblog
), the only changed parts are server_name
, access_log
and (what is most important) root
(which now points to the directory where serendipity for the second blog is unpacked).
Both those files are included by the main /etc/nginx/nginx.conf
. The Debian way is to
$ ln -s /etc/nginx/sites-available/enblog /etc/nginx/sites-enabled $ ln -s /etc/nginx/sites-available/plblog /etc/nginx/sites-enabled
The core configuration - /etc/nginx/blog-common.nginx
looks so:
index index.php; # Forbidding rules created in sync with serendipity-provided .htaccess location ~ (\.inc\.php|\.tpl|\.sql|\.tpl\.php|\.db)$ { deny all; } location ~ \.htaccess { deny all; } # PHP scripts will be forwarded to fastcgi processess. # Remember that the `fastcgi_pass` directive must specify the same # port on which `spawn-fcgi` runs. location ~ \.php$ { include /etc/nginx/fastcgi_params; # Match the port below to the one used for spawn-fcgi fastcgi_pass 127.0.0.1:53217; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } # Optional optimisation - let browsers cache static images location ~ \.(png|jpg|gif)$ { expires 30d; } # Permalink rewriting rules, also created according to .htaccess rewrite ^/archives([/A-Za-z0-9]+)\.html /index.php?url=/archives/$1.html; rewrite ^/([0-9]+)[_\-][0-9a-z_\-]*\.html /index.php?url=$1-article.html; rewrite ^/feeds/(.*) /index.php?url=/feeds/$1; rewrite ^/unsubscribe/(.*)/([0-9]+) /index.php?url=/unsubscribe/$1/$2; rewrite ^/approve/(.*)/(.*)/([0-9]+) /index.php?url=approve/$1/$2/$3; rewrite ^/delete/(.*)/(.*)/([0-9]+) /index.php?url=delete/$1/$2/$3; rewrite ^/(admin|entries)(/.+)? /index.php?url=admin/; rewrite ^/archive$ /index.php?url=/archive; rewrite ^/categories/([0-9]+) /index.php?url=/categories/$1; rewrite ^/plugin/(.*) /index.php?url=plugin/$1; rewrite ^/search/(.*) /index.php?url=/search/$1; rewrite ^/authors/([0-9]+) /index.php?url=/authors/$1; rewrite ^/(.*\.html?)$ /index.php?url=/$1; rewrite ^/(serendipity\.css|serendipity_admin\.css)$ /index.php?url=/$1; rewrite ^/(index|atom|rss|b2rss|b2rdf).(rss|rdf|rss2|xml)$ /rss.php?file=$1&ext=$2; # Usual Nginx error handlers error_page 500 502 503 504 /50x.html; location = /50x.html { root /var/www/nginx-default; }
Note: during serendipity configuration, serendipity dumps .htaccess
file contaning piece of Apache configuration. This file is not handled by nginx, most of the file above was written as nginx port of the Apache config.
If non-trusted users may happen to have upload rights (it is not so in my case), consider improving \.php
regexp so it omits upload directory.
Installing and configuring Serendipity
I tried apt-get install serendipity
first, but found that packaged version is not only obsolete
(1.2), but also fails to work (path mismatch while loading Smarty library, some mysterious errors when I worked around it). In the net end I removed this package and manually installed serendipity from source.
Everything works here as in serendipity manual, there are just two things to remember:
-
files and directories which serendipity writes to (
.htaccess
,templates_c
,upload
,archives
, alsoplugins
andtemplates
in case spartacus is used, alsositemap.xml.gz
if sitemap plugin is on) must be writable by the userwww-data
(the one under whose account php-cgi processes are running) -
once serendipity finishes its configuration, one should review
.htaccess
it dumped (which contains just some deny rules and some rewrite rules) and compare it with the file above.
Blog on sub-url
The configuration above works smoothly for blog running on the root url of virtual host (under http://my.blog.site/). When it is to run below it (say, under http://my.blog.site/blog), things change a bit.
The situation is simple if you can match the directory name with the url path (for example, unpack serendipity to /var/blogdata/blog
if you want to run it under /blog
). Then such configuration
should work:
location /blog { root /var/blogdata; # ... here previous blog-common.nginx }
Inside, add the prefix to all rewrite rules, for example:
rewrite ^/blog/feeds/(.*) /blog/index.php?url=/feeds/$1;
If url differs from the directory name, it is a bit more difficult. I tried using alias
directive instead of root
, but had problems with getting correct SCRIPT_FILENAME
. The easiest method I found was to add one more rewrite rule. For example if blog is to run as http://mysite.net/blog but is installed as /var/mydata/serendipity
, one could try:
rewrite ^/blog/(.*)$ /serendipity/$1; location /serendipity { internal; root /var/mydata; # ... here previous blog-common.nginx }