Jump to content

Setting up IPB, NGiNX, MariaDB and PHP-FPM


Recommended Posts

Posted

Discussions on using Invision Power Board with NGiNX have sprouted up here and there, but I haven't ever seen a dedicated thread for setting up and configuring NGiNX to work efficiently and securely with IP.Board.

So, I figured I might try and contribute something myself.

I'm currently running a forum receiving an average of over three million page views a month. After some fine tuning, I've got my forum operating on a single server using NGiNX without much issue. However, I am no expert. The advice and examples posted here are just taken largely from trial and error, as well as some advice from other more proficient NGiNX users.

My server specifications (for reference):

  • Processor: Intel Xeon E3-1230 (3.20GHz, 3.60GHz Max Turbo)
  • Memory: 16GB DDR3
  • HDD: 2x2TB Enterprise Grade Sata II (Software RAID 1)
  • OS: Debian 64bit (Squeeze)

I do not recommend trying to follow these steps on a production server until you are sure you know what you are doing. This guide is meant for new site's and people wishing to test their websites in a development environment with NGiNX before deciding to make the switch. I am not responsible for any downtime or loss of data that occurs.



Installation

I'll try and cover the basics of setting up NGINX and PHP-FPM in this tutorial. Though this is really something you should research and learn how to do yourself, there isn't much to the installation process on Debian anyways. However, please note that the installation instructions are for Debian Squeeze only.

NGiNX
Edit /etc/apt/sources.list and add the following to the end of the file:

# NGiNX repository list - created 2012-08-14
deb http://nginx.org/packages/debian/ squeeze nginx
deb-src http://nginx.org/packages/debian/ squeeze nginx


As root, run the following commands in order:


PHP-FPM
Edit /etc/apt/sources.list and add the following to the end of the file:

# DotDeb repository list - created 2012-12-02
deb http://packages.dotdeb.org squeeze all
deb-src http://packages.dotdeb.org squeeze all


As root, run the following commands in order:


MariaDB (Optional)
MariaDB is what I use on my server. It is simply a drop-in replacement for MySQL. You do not in any way need to use it over MySQL. You can read about the differences and decide whether or not you want to opt for this route here.

Edit /etc/apt/sources.list and add the following to the end of the file:

# MariaDB repository list - created 2012-08-14 02:01 UTC
# http://downloads.mariadb.org/mariadb/repositories/
deb http://ftp.osuosl.org/pub/mariadb/repo/5.5/debian squeeze main
deb-src http://ftp.osuosl.org/pub/mariadb/repo/5.5/debian squeeze main


As root, run the following commands in order:

  • apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db
  • apt-get update
  • apt-get install mariadb-server mariadb-client



Setting up NGiNX

Notes:
This guide uses example.com as a placeholder for your domain, your.server.ip.here as a placeholder for your servers primary IP address, and /srv/http/example.com as a placeholder for your site's root path.

Create the file /etc/nginx/conf.d/main.conf and use this as a reference for your configuration:

server {
    # Bind to a specific IP or just use "80" to bind to all available IP's
    # http://nginx.org/en/docs/http/ngx_http_core_module.html
    listen       your.server.ip.here:80 default_server;
    server_name  example.com www.example.com;
    root         /srv/http/example.com/root;
 
    # Basic web server configuration.
    # http://wiki.nginx.org/HttpCoreModule
    client_max_body_size  750M;
    send_timeout          60s;
 
    # (Optional) I keep logging off unless I explicitly need it. It's wasted I/O and wasted disk space otherwise. (My opinion)
    # http://wiki.nginx.org/HttpLogModule
    access_log   off;
    error_log    /var/log/nginx/error.log  error;
 
    # Enabling gzip for files not processed by IPB.
    gzip  on;
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript application/xml application/xml+rss text/javascript application/javascript text/x-js;
    gzip_buffers 16 8k;
    gzip_disable "MSIE [1-6].(?!.*SV1)";
 
    # Remove the second line if you don't use rewrites.
    location / {
        index  index.php index.html index.htm;
        try_files  $uri $uri/ /index.php;
    }
 
    # PHP execution security directives. Prevents bad people from executing naughty PHP scripts.
    location ~ ^(/uploads/).*(.php)$ {
        deny     all;
    }
    location ~ ^(/hooks/).*(.php)$ {
        deny     all;
    }
    location ~ ^(/cache/).*(.php)$ {
        deny     all;
    }
    location ~ ^(/public/style_).*(.php)$ {
        deny     all;
    }
 
    # (Optional) Caching and other directives for static files.
    # http://wiki.nginx.org/HttpHeadersModule
    # First block is for avatars, second is for all other static files.
    location ~* ^(/uploads/profile/).*.(jpg|jpeg|gif|png)$ {
        expires    1d;
    }
    location ~* ^.+.(jpg|jpeg|gif|css|png|js|xml|ico|swf|cur)$ {
        expires    1w;
    }
 
    # NGiNX defaults for dealing with 500 errors.
    # http://wiki.nginx.org/HttpCoreModule#error_page
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
 
    # Passing PHP scripts to PHP-FPM
    # http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html
    location ~ .php$ {
        fastcgi_pass   unix:/srv/http/.socks/main.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_read_timeout  60s;
        fastcgi_send_timeout  60s;
        include        /etc/nginx/fastcgi_params;
    }
}

You may also wish to edit /etc/nginx/nginx.conf and up the worker_processes to 2-4 if you use a multi-core processor.



Setting up PHP-FPM

Notes:
This guide uses example.com as a placeholder for your domain and /srv/http/example.com as a placeholder for your site's root path.

Create the file /etc/php5/fpm/pool.d/main.conf and use this as a reference for your configuration:

; Start a new pool named 'main'.
[main]

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = www-data
group = www-data

; The address on which to accept FastCGI requests.
; You will need to create the ".socks/" directory yourself.
; Be sure PHP-FPM can read and write to this file.
listen = /srv/http/.socks/main.sock

; Set listen(2) backlog. A value of '-1' means unlimited.
; Default Value: 128 (-1 on FreeBSD and OpenBSD)
listen.backlog = 4096

; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions. 
; Default Values: user and group are set as the running user
;                 mode is set to 0666
listen.owner = www-data
listen.group = www-data
listen.mode = 0666

; Choose how the process manager will control the number of child processes.
; Possible Values:
;   static  - a fixed number (pm.max_children) of child processes;
;   dynamic - the number of child processes are set dynamically based on the
;             following directives. With this process management, there will be
;             always at least 1 children.
;             pm.max_children      - the maximum number of children that can
;                                    be alive at the same time.
;             pm.start_servers     - the number of children created on startup.
;             pm.min_spare_servers - the minimum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is less than this
;                                    number then some children will be created.
;             pm.max_spare_servers - the maximum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is greater than this
;                                    number then some children will be killed.
;  ondemand - no children are created at startup. Children will be forked when
;             new requests will connect. The following parameter are used:
;             pm.max_children           - the maximum number of children that
;                                         can be alive at the same time.
;             pm.process_idle_timeout   - The number of seconds after which
;                                         an idle process will be killed.
; Note: This value is mandatory.
; Kirito Note: I use static. You really don't need to use dynamic on a dedicated server. Just ensure you configure this sanely. You can crash your server if you try and handle more processes than you have the memory for.
pm = static

; Set the number of processes you wish to spawn for PHP-FPM.
; The best way to figure out the highest value you can set this as is to measure how much memory your site uses from experimentation.
; IP.Board reports using as low as ~49MB of RAM per process on a "clean" forum to as high as ~82MB per process on a forum with many hooks and applications installed for me. Your mileage may vary, hence why it's good to experiment with this yourself.
; Divide the average (or highest) amount of memory per process your site uses with the amount of RAM you have to spare on your server. That's NOT the total amount of RAM your server has, but how much you KNOW you can spare to the web server alone. Don't forget about MySQL and any other services you run!
; (As an example, if you have 4GB of RAM to spare and IP.Board uses around 52MB of RAM per process on your site, a sane value to put here would be 75. On my forum, I use 120, but I have 16GB total RAM on my box.)
pm.max_children = 15
 
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
pm.max_requests = 300
 
; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
; Kirito Note: This can be useful for logging pages that take a long time to execute on your site.
; slowlog = 
 
; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
; request_slowlog_timeout = 15s
 
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_terminate_timeout = 0
 
; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024
 
; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0
 
; Chroot to this directory at the start. This value must be defined as an
; absolute path. When this value is not set, chroot is not used.
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
; of its subdirectories. If the pool prefix is not set, the global prefix
; will be used instead.
; Note: chrooting is a great security feature and should be used whenever 
;       possible. However, all PHP paths will be relative to the chroot
;       (error_log, sessions.save_path, ...).
; Default Value: not set
;chroot = 
 
; Chdir to this directory at the start.
; Note: relative path can be used.
; Default Value: current directory or / when chroot
chdir = /
 
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environement, this can cause some delay in the page
; process time (several ms).
; Default Value: no
;catch_workers_output = yes

; Limits the extensions of the main script FPM will allow to parse. This can
; prevent configuration mistakes on the web server side. You should only limit
; FPM to .php extensions to prevent malicious users to use other extensions to
; exectute php code.
; Note: set an empty value to allow all extensions.
; Default Value: .php
security.limit_extensions = .php .php5 .phtml
 
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
env[DOCUMENT_ROOT] = /srv/http/example.com/root

; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
;   php_value/php_flag             - you can set classic ini defines which can
;                                    be overwritten from PHP call 'ini_set'. 
;   php_admin_value/php_admin_flag - these directives won't be overwritten by
;                                     PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.

; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.

; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr)

; Default Value: nothing is defined by default except the values in php.ini and
;                specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;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

You'll also probably want to delete the /etc/php5/fpm/pool.d/www.conf example configuration.

Once you have everything configured, just restart NGiNX and PHP-FPM to apply the new configurations.
/etc/init.d/nginx restart
/etc/init.d/php-fpm restart



This is the basic configuration and set-up I've built from personal experimentation and research. I also have some helpful individuals from IRC (#nginx and ##php on Freenode) to thank for helping me ensure everything is set up to acceptable standards.

However, as I mentioned above, I am no expert. This is just a hobby for me, so don't expect this to be the perfect universal configuration. But I hope it can at least serve as a decent starting point for some.

Feel free to suggest changes to this set up, offer your own advice or discuss anything related to setting up NGiNX and PHP-FPM with IP.Board here.
Posted

Thanks!! It does help!

Any good tutorial for installing sphinx on debian squeeze and settings for IPB related?

There used to be an article for this here, but it was in the site's old documentation which seems to no longer be accessible.

I'll see about writing up a tutorial for setting up Sphinx soon!

  • 2 months later...
Posted

I'm sorry for my lack of activity here. I was partially waiting to see if IPB would revive the Sphinx Article with the new documentation system, since it's pretty much as complete as you'd need it to be.

You can view the new doc page here:

http://www.invisionpower.com/support/guides/_/maintenance-and-server-configurations/searching-r21

My only suggestion is to ensure you set Sphinx up so that you are not running it as root. Sphinx does not need root privileges.

On my install, I have a /srv/search directory where I store Sphinx related configuration.

/srv/search/sphinx.conf for the Sphinx configuration file.

/srv/search/log for Sphinx logging files.

/srv/search/index for Sphinx index files.

All of this is owned and run by the non-privileged user searchd.

Setting up a similar configuration will require that you modify the sphinx.conf IP.Board generates for you. You will need to search and replace the path and log configurations. I could probably write up a simple script to do this for you actually, it would make things slightly easier for me as well. I'll see about posting that here later if it would be of use to anyone.

  • 2 weeks later...
Posted

hi

thank you very much for sharing the nginx config. The FURLs etc work perfectly.

I only have issues on Admin. When I upload my logo I get a not found error.

My config is almost exactly the same as yours. Minor difference is in my fastcgi part. I am pasting below.

location ~ .php$ {
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_intercept_errors on;
         fastcgi_buffer_size 128k;
                fastcgi_buffers 256 256k;
                fastcgi_busy_buffers_size 256k;
                fastcgi_read_timeout 300s;


        fastcgi_pass 127.0.0.1:9003;
}
 

I use php-fpm pool with port 9003. each site uses different fpm config and different ports for the pool. the permissions are correct as each pool runs under the specific user who owns the web files. I have other sites hosted that work perfectly but none are IPboard or forums.
If you or anyone can please advise on why I am getting this problem I'd appreciate it very much.
thanks.
Posted

Mi config has this:

        ## Admin
        location /admin/ {
            access_log on;
            log_not_found off;

            try_files $uri $uri/ /admin/index.php;

            location ~ .php$ {
                include php5_params;
            }
        }

This config its not mine, but its the one i use and works great :)

The main thing here for you is the try_files i have under location

Posted

Thank you Luis. May I know why you have the .php handler there?

I am going to give this a try now

Ok I used this as my last rule before the fastcgi block.

## Admin
location /admin/ {
access_log on;
log_not_found off;

try_files $uri $uri/ /admin/index.php;

}
 

but same problem.

Posted

hi

thank you very much for sharing the nginx config. The FURLs etc work perfectly.

I only have issues on Admin. When I upload my logo I get a not found error.

You get a 404 error page? Any chance you could send me a PM and allow me to look at it?

It's possible you're actually getting a 403 error, but your 403 error page is returning a 404. That seems most likely if it happens when you're uploading a template logo.

Try commenting out the ";php_flag[display_errors] = off" line above and change the setting to "on". Then restart or reload php5-fpm and try again.

The configuration I have posted above shouldn't be giving you any issues.

Posted

Thank you Luis. May I know why you have the .php handler there?

I am going to give this a try now

Ok I used this as my last rule before the fastcgi block.

but same problem.

        ## Admin
        location /admin/ {
            access_log on;
            log_not_found off;

            try_files $uri $uri/ /admin/index.php;

            location ~ .php$ {
                include php5_params;
            }
        }

php5_params:

# PHP
access_log off;
log_not_found on;
try_files $uri =403;
fastcgi_split_path_info ^((?U).+.php)(/.+)$;
include fastcgi_params;
fastcgi_param  PATH_INFO        $fastcgi_path_info;
fastcgi_index index.php;
fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
fastcgi_pass php5-fpm;
Posted

As I said, the configuration posted here should work without issue.

If you want me to investigate the problem for you, I'd be happy to. Just send me a PM.

That, or you'll have to provide more information. Enable error logging and post the output here.

Posted

this is the error I am getting when I try to upload the logo

[11-Mar-2013 14:36:14 UTC] PHP Warning: File upload error - unable to create a temporary file in Unknown on line 0
[11-Mar-2013 14:36:14 UTC] PHP Fatal error: Call to undefined method admin_core_templates_easylogo::start() in /home/vguiol3/admin/applications/core/modules_admin/templates/easylogo.php on line 184
I tried to upload an avatar as a user and also get this error.

All my folders have permission of 0755 atleast. The fpm is also running as the user who owns the files/dirs so I know its not permission issue. What bugged me was that I am pretty good with nginx+fpm setup or nginx+uwsgi setups. I assumed IPB was being the ahole and the nginx conf I had copy pasted (which I have never done before)... but read on.

I realized from the error that PHP itself is not able to create the temporary files so it has nothing to do with folder permissions under the root folder. Other fpm pools and their sites are ok, invluding file uploads.

From the error messages I went to check my configs again not nginx but FPM. All this time I was narrowing down nginx config being the problem.

I usually set a specific tmp path for my pools. This directory did not exist.

I updated the following config values in fpm to paths that exist and have write permissions.

env[TMP] = /mnt/ramdisk0
env[TMPDIR] = /mnt/ramdisk0
env[TEMP] = /mnt/ramdisk0
Restarted fpm and everything is normal.
Thanks for all the help. It was my logging that wasnt showing the errors which I had to fix first. ( i never use display errors, just log errors). Once I realized from the posts above that I was getting my error messages things worked out.
Thought i'd share my stupidity here so others may benefit. the configs work perfectly.

p.s. I store all sessions and temp files in a separate ramdisk. Not sure if this is a security/bad practice issue but I get awesome performance on my high traffic sites that run php. any tips?

Posted

How do you set your open_basedir? One of the things in the IPB security guide.

For me, I added this to the bottom of my /etc/php5/fpm/pool.d/main.conf ( or /etc/php-fpm.d/main.conf for CentOS) file:

php_admin_value[open_basedir] = /var/www/html:/usr/share/php:/tmp:/usr/share/phpmyadmin:/etc/phpmyadmin:/var/lib/phpmyadmin

Remembering that I am running CentOS, so change accordingly.

You may also want to add other paths/folders, as you can see 'phpmyadmin' was included in the open_basedir directive.

Posted

How do you set up htaccess and all? I mean before with apache i used to htaccess to protect some directories including admin panel. Can u please provide some how to on it?

Also how do you add another website? I mean what i need to change in the config to add another website called example2.net

Thanks!

Posted

You will need to convert your .htaccess directives over to NGiNX configuration directives and place them in your server block configuration.

NGiNX supports web auth protection and it's fairly easy to set up, it's similar to how you set it up on Apache at least. You can even still use your old htpasswd file with NGiNX.

http://wiki.nginx.org/HttpAuthBasicModule

So really, all you have to do is tell NGiNX where your htpasswd file is and set up a location block for the directories you want to protect. Eg.

location ^~ /admin/  {
    auth_basic            "This is a restricted area";
    auth_basic_user_file  /srv/http/example.com/root/admin/.htpasswd;
}
Posted

Well, the new editor just cut off 80% of my post submitting making it, and it's 2AM in the morning, so I can't be bothered to re-type all of that again.

It did cut off an important bit of information though. If you store your .htpasswd file in a web accessible directory, you will need to ensure you add the following to your configuration as well. If you don't, your .htpasswd file will not be protected and anyone can download it. So don't forget. (Hopefully) hashed password or not, you don't want that.

# Deny access to hidden files
location ~ /. {
    deny  all;
}

As for setting up a new website, you just have to create a new configuration file. You can use the above configuration as a reference for your new site. If you're setting up another IPB forum, it's essentially the same thing. Just update your paths and server name. You don't have to set up another PHP-FPM pool unless you want the added control. You can set it up to use your existing one.
Posted

Can I add some kind of security level? Example:

Different user for different websites? So if one website gets hacked hacker can't go through another website public folder? If yes can u please help on that?

Thanks!

Posted

Yep, you can! That's what I do for all my current websites.

I've actually considered writing up an article explaining how to do that as well. I'll do a little more research on some things first, then I'll try write up a guide on how to do this shortly. I can't promise to have it done by tonight, but I'll at least try and start on something.

Posted

I think I'll write up a much more extensive guide in general sometime soon.

I plan on covering taking advantage of some new experimental technologies, for those that like living on the edge. Primarily including Google's SPDY and PageSpeed modules for NGiNX.

I've been using SPDY on my production server for a while now, since the early NGiNX patches, and I'm in love with it. It's just recently been integrated into NGiNX's code and is available from the latest development release without needing to patch. PageSpeed is something I've just been toying with on one of my development boards. If you're curious, you can check out one of them here, it's using both Google SPDY and PageSpeed. You can inspect the pages source and compare it with another IPB board installation to see some of the differences. Notably cleaning up white space (which, realistically, probably doesn't do much anything when you're using GZIP compression), deferring Javascript, lazyloading images, moving persistent inline CSS to external CSS files for cachability (on my production board, my template outputs ~105KB worth of inline CSS on the index page. I haven't yet fully tested whether the extra lookup(s) are justifiable for this, but it seems to be beneficial.)

I'd also like to include in the guide how to properly set up your forum using SSL/HTTPS. (Which is needed if you want to take advantage of SPDY). I've seen people complaining about the difficulties of this before, but all it really takes is updating a few settings to get everything working right. I ended up giving on it the first time I tried as well. But I'm glad I took the time and effort to properly set it up on all my boards now.

And of course, as I mentioned previously, I'll include instructions on how to properly secure your setup. I'll probably even try and write up a simple bash script to ease the process.

I also recently pre-ordered NGiNX's sponsored "Mastering NGiNX" book for my Kindle and want to finish studying it first before taking a stab at rewriting everything. :tongue:

Guest Ae9803
Posted

Kirito,

Any chance you could create a guide for Centos in the future? :D

Posted

With a server like yours in the OP, and that amount of traffic, you shouldn't really notice much difference between Apache and nginx as you shouldn't have any memory problems etc. with Apache. It is good that you have written a guide though for others who may be worried about setting it up. :)

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...