HowTo: Nginx mit PHP5-FPM, MySQL und PostgreSQL unter Debian Squeeze installieren
Bei einem vServer kann man selbst wählen, welchen Webserver und welche Datenbanksysteme man verwenden möchte. Ich habe mich daher für Nginx mit PHP5-FPM, sowie MySQL und PostgreSQL entschieden. Um dies alles auf einem Server mit 256 MB RAM zu betreiben sind dann allerdings noch ein paar Modifikationen notwendig.
Bild von Aleks Dorohovich auf Unsplash, lizenziert unter CC0 1.0
Zuerst werden die nicht benötigten Pakete entfernt:
sudo /etc/init.d/apache2 stop
sudo /etc/init.d/samba stop
sudo apt-get -y purge apache2 apache2-doc apache2-mpm-prefork apache2-utils apache2.2-bin apache2.2-common samba-common samba
Dann kann man die Paketquellen und Schlüssel hinzufügen:
sudo echo "deb http://packages.dotdeb.org squeeze all" > /etc/apt/sources.list.d/squeeze-dotdeb.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys --recv-key 89DF5277
Anschließend werden die Pakete installiert, wobei PHP-Module natürlich auch später noch nachinstalliert werden können:
sudo apt-get update
sudo apt-get -y install nginx mysql-server postgresql php5 php5-fpm php-pear php-apc php5-common php5-mcrypt php5-mysql php5-pgsql php5-cli php5-gd php5-curl php5-xmlrpc
PostgreSQL einrichten
Als erstes meldet man sich mit dem Systembenutzer postgres
bei PostgreSQL an und vergibt für den Datenbankbenutzer postgres
ein Passwort:
sudo -u postgres psql
\password postgres
Die PostgreSQL-Umgebung kann man anschließend mit \q
verlassen. Nun wird festgelegt, wer auf die Datenbanken zugreifen kann:
sudo vi /etc/postgresql/8.4/main/pg_hba.conf
# "local" is for Unix domain socket connections only
local all all md5
Zudem empfiehlt es sich, die Puffer-Größe anzupassen, sodass sie zum verfügbaren Arbeitsspeicher passt:
sudo vi /etc/postgresql/8.4/main/postgresql.conf
shared_buffers = 16MB
MySQL einrichten
Während der Installation des MySQL-Servers hat man bereits das Passwort des root-Nutzers festgelegt. Es empfiehlt sich außerdem, die Sicherheit der Installation zu verbessern, in dem man mysql_secure_installation ausführt:
sudo /usr/bin/mysql_secure_installation
sudo /etc/init.d/mysql stop
sudo rm /etc/mysql/my.cnf
sudo vi /etc/mysql/my.cnf
Basierend auf /usr/share/doc/mysql-server-5.5/examples/my-medium.cnf.gz
habe ich für MySQL dann die folgende Konfiguration eingerichtet:
my.cnf
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock
[mysqld]
port = 3306
socket = /var/run/mysqld/mysqld.sock
pid-file = /var/run/mysqld/mysqld.pid
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
log-bin=mysql-bin
binlog_format=mixed
server-id = 1
character-set-server=utf8
collation-server=utf8_general_ci
skip-innodb
default-storage-engine=MyISAM
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
no-auto-rehash
[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M
[mysqlhotcopy]
interactive-timeout
Um Arbeitsspeicher zu sparen, habe ich dabei unter anderem die Storage-Engine InnoDB deaktiviert. Anschließend kann man alle Dienste neustarten, an denen man Modifikationen durchgeführt hat:
sudo /etc/init.d/mysql restart
sudo /etc/init.d/postgresql restart
sudo /etc/init.d/php5-fpm restart
sudo /etc/init.d/nginx restart
PHP-FPM einrichten
Ist anschließend die RAM Nutzung zu hoch, so kann man die Voreinstellungen von PHP-FPM noch modifizieren:
sudo vi /etc/php5/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 3
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 32
Danach ist es wieder notwendig, den Dienst neuzustarten:
sudo /etc/init.d/php5-fpm restart
Nginx einrichten
Die Einstellungen in der Server-Konfigurationsdatei nginx.conf
sind die Grundlage für die namensbasierten Virtual Hosts. Ich habe mich hierbei an diesem Artikel von Sergej Müller orientiert:
sudo rm /etc/nginx/nginx.conf
sudo vi /etc/nginx/nginx.conf
nginx.conf
user www-data;
worker_processes 4;
worker_rlimit_nofile 4096;
pid /var/run/nginx.pid;
# [ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log;
#google_perftools_profiles /tmp/profile/;
events {
worker_connections 2048;
# use [ kqueue | rtsig | epoll | /dev/poll | select | poll ] ;
use epoll;
multi_accept on;
}
http {
server_names_hash_bucket_size 64;
## Size Limits
client_max_body_size 10M;
client_header_buffer_size 32k;
client_body_buffer_size 128k;
large_client_header_buffers 64 8k;
## Timeouts
client_body_timeout 3m;
client_header_timeout 3m;
send_timeout 3m;
#expires 24h;
keepalive_timeout 120 120;
## General Options
ignore_invalid_headers on;
keepalive_requests 100;
limit_conn_zone $binary_remote_addr zone=one:5m;
recursive_error_pages on;
sendfile on;
server_name_in_redirect off;
server_names_hash_max_size 512;
server_tokens off;
set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
## TCP options
#tcp_nopush on;
tcp_nodelay off;
#send_lowat 12000;
include mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
log_format main '$remote_addr - $remote_user [$time_local] $status '
'\"$request\" $body_bytes_sent \"$http_referer\" '
'\"$http_user_agent\" \"http_x_forwarded_for\"';
## Cache
open_file_cache max=1000 inactive=24h;
open_file_cache_valid 24h;
open_file_cache_min_uses 2;
open_file_cache_errors on;
## SSL Settings
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
## Compression
gzip on;
gzip_static on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_buffers 16 8k;
gzip_comp_level 6;
gzip_http_version 1.1;
gzip_min_length 0;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/json application/xml application/xml+rss application/x-javascript image/x-icon application/x-perl application/x-httpd-cgi application/javascript text/x-js font/ttf font/opentype application/vnd.ms-fontobject image/svg+xml;
gzip_vary on;
output_buffers 10 128k;
postpone_output 1500;
include /etc/nginx/sites-enabled/*.conf;
}
Anschließend kann man pro Domain oder Subdomain jeweils einen eigenen Document-Root-Ordner und eigene Konfigurationen anlegen:
sudo mkdir -p /var/www/domain.de/web
sudo mkdir -p /var/www/domain.de/log
sudo chown -R www-data:www-data /var/www
Im Verzeichnis sites-available
legt man die Virtual-Host-Konfigurationen an und legt dann einen symbolischen Link darauf in sites-enabled
an:
sudo mkdir -p /etc/nginx/sites-available/
sudo mkdir -p /etc/nginx/sites-enabled/
Dies hat den Vorteil, dass man einzelne Konfigurationen aktivieren und deaktivieren kann:
sudo vi /etc/nginx/sites-available/domain.de.conf
sudo ln -s /etc/nginx/sites-available/domain.de.conf /etc/nginx/sites-enabled/
Exemplarische Virtual-Host-Konfigurationen
Im folgenden ein paar einfache Konfigurationsbeispiele:
simple-domain.de.conf
server {
listen 80 default_server;
#listen [::]:80 default_server;
server_name domain.de *.domain.de;
root /var/www/domain.de/web;
access_log /var/www/domain.de/log/access.log;
error_log /var/www/domain.de/log/error.log;
location / {
index index.php index.html index.htm;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
simple-domain.de-ssl.conf
server {
listen 443 default_server ssl spdy;
#listen [::]:443 default_server ssl spdy;
server_name domain.de *.domain.de;
ssl on;
ssl_certificate /etc/ssl/certs/domain.de.pem;
ssl_certificate_key /etc/ssl/private/domain.de.key;
ssl_session_timeout 5m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # not possible to do exclusive
ssl_ciphers 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA';
add_header Strict-Transport-Security max-age=31556926;
# use this only if all subdomains support HTTPS!
# add_header Strict-Transport-Security "max-age=31556926; includeSubDomains";
ssl_ecdh_curve secp384r1;
ssl_dhparam /etc/nginx/ssl/group16.pem;
ssl_session_cache shared:SSL:10m;
root /var/www/domain.de/web;
access_log /var/www/domain.de/log/access.log;
error_log /var/www/domain.de/log/error.log;
location / {
index index.php index.html index.htm;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
redirect-domain.de.conf
server {
listen 80;
#listen [::]:80;
server_name otherdomain.de *.otherdomain.de;
#return 301 $scheme://domain.de$request_uri;
return 302 $scheme://domain.de$request_uri;
}
download.domain.de.conf
server {
listen 80;
#listen [::]:80;
server_name download.domain.de;
## ROOT
root /var/www/download.domain.de/web;
## LOGS
access_log /var/www/download.domain.de/log/access.log;
error_log /var/www/download.domain.de/log/error.log;
# DEFAULT INDEX
index index.php index.html index.htm;
## RESSOURCES
location ~ \.(?:ico|png|jpe?g|css|js)$ {
expires 31d;
add_header Pragma "public";
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
#location ~ \.(?!ico).+$ {
# valid_referers none blocked server_names ~(wpseo.|google.|yahoo.|bing.|facebook.|fbcdn.|pinterest.);
# if ( $invalid_referer ) {
# return 444;
# }
#}
}
## PHP FILES
#location ~ \.php$ {
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# include fastcgi_params;
#}
## GZIP
gzip_static on;
## CHARSET
charset utf-8;
## INDEX LOCATION
location / {
## Browser-Cache
expires 1h;
try_files $uri @rewrite;
}
## REWRITE LOCATION
location @rewrite {
#return 301 $scheme://domain.de$request_uri;
return 302 $scheme://domain.de$request_uri;
}
}
download.domain.de-ssl.conf
server {
listen 443 ssl spdy;
#listen [::]:443 ssl spdy;
server_name download.domain.de;
## SSL
ssl on;
ssl_certificate /etc/ssl/certs/domain.de.pem;
ssl_certificate_key /etc/ssl/private/domain.de.key;
ssl_session_timeout 5m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # not possible to do exclusive
ssl_ciphers 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA';
add_header Strict-Transport-Security max-age=31556926;
# use this only if all subdomains support HTTPS!
# add_header Strict-Transport-Security "max-age=31556926; includeSubDomains";
ssl_ecdh_curve secp384r1;
ssl_dhparam /etc/nginx/ssl/group16.pem;
ssl_session_cache shared:SSL:10m;
## ROOT
root /var/www/download.domain.de/web;
## LOGS
access_log /var/www/download.domain.de/log/access.log;
error_log /var/www/download.domain.de/log/error.log;
# DEFAULT INDEX
index index.php index.html index.htm;
## RESSOURCES
location ~ \.(?:ico|png|jpe?g|css|js)$ {
expires 31d;
add_header Pragma "public";
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
#location ~ \.(?!ico).+$ {
# valid_referers none blocked server_names ~(wpseo.|google.|yahoo.|bing.|facebook.|fbcdn.|pinterest.);
# if ( $invalid_referer ) {
# return 444;
# }
#}
}
## PHP FILES
#location ~ \.php$ {
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# include fastcgi_params;
#}
## GZIP
gzip_static on;
## CHARSET
charset utf-8;
## INDEX LOCATION
location / {
## Browser-Cache
expires 1h;
try_files $uri @rewrite;
}
## REWRITE LOCATION
location @rewrite {
#return 301 $scheme://domain.de$request_uri;
return 302 $scheme://domain.de$request_uri;
}
}
wordpress-domain.de.conf
server {
## INIT
listen 80 default_server;
#listen [::]:80 default_server;
server_name domain.de *.domain.de;
## ROOT
root /var/www/domain.de/web;
## LOGS
access_log /var/www/domain.de/log/access.log;
error_log /var/www/domain.de/log/error.log;
# DEFAULT INDEX
index index.php;
## SECURITY
if ( $request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
location ~ /(\.|wp-config.php|liesmich.html|readme.html) {
return 444;
}
## REWRITES
#location ~ ^/(\d+)/$ {
# return 301 /?p=$1;
#}
## RESSOURCES
location ~ \.(?:ico|png|jpe?g|css|js)$ {
expires 31d;
add_header Pragma "public";
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
#location ~ \.(?!ico).+$ {
# valid_referers none blocked server_names ~(wpseo.|google.|yahoo.|bing.|facebook.|fbcdn.|pinterest.);
# if ( $invalid_referer ) {
# return 444;
# }
#}
}
## REDIRECT INDEX
location / {
try_files $uri $uri/ /index.php;
}
## PHP FILES
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
## AUTH ADMIN
location = /wp-login.php {
#auth_basic "Restricted";
#auth_basic_user_file /var/www/domain.de/.htpasswd;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
wordpress-cachify-hdd-domain.de.conf
server {
## INIT
listen 80 default_server;
#listen [::]:80 default_server;
server_name domain.de *.domain.de;
## ROOT
root /var/www/domain.de/web;
## LOGS
access_log /var/www/domain.de/log/access.log;
error_log /var/www/domain.de/log/error.log;
# DEFAULT INDEX
index index.php;
## SECURITY
if ( $request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
location ~ /(\.|wp-config.php|liesmich.html|readme.html) {
return 444;
}
## REWRITES
#location ~ ^/(\d+)/$ {
# return 301 /?p=$1;
#}
## RESSOURCES
location ~ \.(?:ico|png|jpe?g|css|js)$ {
expires 31d;
add_header Pragma "public";
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
#location ~ \.(?!ico).+$ {
# valid_referers none blocked server_names ~(wpseo.|google.|yahoo.|bing.|facebook.|fbcdn.|pinterest.);
# if ( $invalid_referer ) {
# return 444;
# }
#}
}
## PHP FILES
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
## AUTH ADMIN
location = /wp-login.php {
#auth_basic "Restricted";
#auth_basic_user_file /var/www/domain.de/.htpasswd;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
## GZIP
gzip_static on;
## CHARSET
charset utf-8;
## INDEX LOCATION
location / {
if ( $query_string ) {
return 405;
}
if ( $request_method = POST ) {
return 405;
}
if ( $request_uri ~ /wp-admin/ ) {
return 405;
}
if ( $http_cookie ~ (wp-postpass|wordpress_logged_in|comment_author)_ ) {
return 405;
}
error_page 405 = @nocache;
## Browser-Cache
expires 1h;
try_files /wp-content/cache/cachify/${host}${uri}index.html @nocache;
}
## NOCACHE LOCATION
location @nocache {
try_files $uri $uri/ /index.php?$args;
}
}
Zur Aktivierung von IPv6 kommentiert man einfach die Listen-Direktiven mit ipv6only=on
ein. Nach einem Neustart von Nginx sollte die Seite dann unter der entsprechenden Domain verfügbar sein:
sudo /etc/init.d/nginx restart
Hat man die Virtual-Host-Konfiguration abgeschlossen, so kann man sofort loslegen und Dateien in den Document-Root-Ordner legen oder beispielsweise eine Datenbank in MySQL anlegen und WordPress konfigurieren.
Links
- HowtoForge: Installing Nginx With PHP5 (And PHP-FPM) And MySQL Support (LEMP) On Ubuntu 12.04 LTS
- Web Hosting Talk: nginx + php-fpm + debian squeeze tutorial - the fastest way to host php
- ubuntuusers.de Wiki: MySQL
- ubuntuusers.de Wiki: PostgreSQL
- Reducing MySQL Memory Usage for Low End Boxes
- Nginx and PHP-FPM Configuration and Optimizing Tips and Tricks
- MySQL 5.5 Reference Manual: mysql_secure_installation - Improve MySQL Installation Security
- WordPress auf Nginx: Performance und Sicherheit optimieren
- Nginx Webserver und MySQL für WordPress konfigurieren
- SUCKUP.de: Nginx + PHP5-fpm auf Debian/Ubuntu
- BetterCrypto.org: Applied Crypto Hardening [PDF]