sidebar hamburger menu

CloudLinux OS components

General information and requirements

LVE-Stats 2

General information and requirements

Why is it needed?

  • Old LVE-statistics store averages as integer numbers, as % of CPU usage. If user used 100% of CPU for 1 second within an hour, it is only 1-2% for a minute, and 0 for 5 minutes. Data in old LVE-statistics is aggregated to 1-hour intervals. So, such peak load will noExat be recorded and we need to store data with much higher precision.
  • 100% CPU usage in old lve statistics means “all cores”. On 32 core servers usage is not visible for most users (as they are limited to 1 core).
  • Old LVE-statistics does not provide a way to determine a cause of LVE faults, i.e. what processes are running when user hits LVE limits.Example
  • Notifications in old LVE-statistics are not accurate because they are based on average values for CPU, IO, IOPS.
  • Old LVE-statistics functionality is hard to extend.

Major improvements and features

  • increased precision of statistics;
  • CPU usage is calculated  in terms of % of a single core (100% usage means one core);
  • lvestats-server emulates and tracks faults for CPU, IO, IOPS;
  • lvestats-server saves “snapshots” of user’s processes and queries for each “incident” - added new lve-read-snapshot utility;
  • improved notifications about hitting LVE limits (more informative and without false positives);
  • implemented ability to add custom plugins;
  • MySQL and PostGreSQL support;
  • more pretty, scalable, interactive charts;
  • snapshots include HTTP-requests.

Note

mod_proctitle has to be enabled for HTTP request collection to be available

What features will be implemented in the future?

  • Notifications for control panels other than CPanel.
  • Burstable Limits/server health: We are monitoring server health ( LA , memory , idle CPU ) and automatically decreasing/increasing limits based on server health.
  • Reseller Limits: plugin would analyze usage per group of users (reseller’s usage), and do actions.
  • Suspend/notify plugin: would detect that user is being throttled for 10 minutes, and suspend him (just because), or notify, or increase limits.

Installation and update

To install, please execute:

yum install lve-stats

To update:

yum update lve-stats

Settings of old lve-stats (ver. 0.x) are imported automatically on the first install/update of a new lve-stats package.

SQLite database file is located in /var/lve/lvestats2.db, data from old lve-stats (ver. 0.x) are being migrated automatically in the background. Migrating process can last 2-8 hours (during this time lags are possible when admin is trying to check statistics, at the same time users will not be affected). Migrating the latest 30 days, SQLite DB stable migration is provided.

Currently, the new lve-stats supports all databases available in CloudLinux OS Shared.

Note

You can also use LVE-stats 2 CLI

Downgrade

If you have any problems after update, downgrade lve-stats2 to the previous stable version by running:

yum downgrade lve-stats

and contact CloudLinux OS Shared support at https://cloudlinux.zendesk.com/hc/requests/new

Note

You may need to rename *.rpmsave files to original ones in order to restore settings for old lve-stats (/etc/sysconfig/lvestats, /etc/sysconfig/cloudlinux-notify).

Note

You can also use LVE-stats 2 CLI

Configuration

The main configuration file /etc/sysconfig/lvestats2 contains the following options:

  • db_type - selects appropriate database type to use;

  • connect-string - connection string for PostGreSQL and MySQL database, has the following form:

    connect_string = USER:PASSWORD@HOST[:PORT]/DATABASE
    

    Default port is used for specific database, if port is not specified (typical port is 3306 for MySQL and 5432 for PostGreSQL). Connection string is not used for sqlite database.

  • server_id - sets the name of the server (at most 10 characters). This option is to use with centralized database ( PostGreSQL or MySQL). For use with sqlite database, value of this option should be "localhost" (without quotes).

  • plugins – path to directory containing custom plugins for lve-stats (default path /usr/share/lve-stats/plugins).

  • db_timeout - period of time to write data to database (in seconds); default value is 60 seconds.

  • timeout - timeout for custom plugins (seconds). If plugin execution does not finish within this period, plugin is terminated. Default value is 5 seconds.

  • interval - duration of one cycle of lvestats-server (seconds). This should be less than total duration of execution of all plugins. Default value is 5 seconds. Increasing this parameter makes precision of statistics worse.

  • keep_history_days - period of time (in days) to keep history in th database. Old data is removed from the database automatically. Default value is 30 days.

  • mode – sets compatibility output mode (compatibility with older lveinfo version

    • Value v1 enables compatibility with old version of lveinfo.
    • Value v2 enables extended output mode, but can break LVE plugins for control panels (statistics in LVE Manager, Resource Usage, etc). Support of v2 mode will be added to LVE plugins in the recent future. When mode parameter is absent, later version of lveinfo is implied.
  • disable_snapshots - disable snapshots and incidents. Possible values:

    • true
    • false
  • hide_lve_more_than_maxuid - disable displaying of lve ids more than max uid in resource usage. Possible values:

    • true
    • false
  • use_big_uids - the option is available from lvestats version 3.0.14-1. It allows using the user uids more than 109 and up to 231-2 (2 147 483 646). Top border is the biggest value which kmodlve can use as LVE ID. You should recreate lvestats database by command lve-create-db --recreate if you change the option's value from false to true. Possible values:

    • true
    • false

Configuration files for plugins are located in /etc/sysconfig/lvestats.config directory.

/etc/sysconfig/lvestats.config/SnapshotSaver.cfg contains the following options:

  • period_between_incidents - the minimal interval of time between incidents (in seconds). If minimal interval of time between LVE faults is greater than value specified, than new "incident" will begin and new snapshots will be saved. Default value is 300 seconds.
  • snapshots_per_minute - the maximum number of snapshots saved per minute for specific LVE id (default is 2).
  • max_snapshots_per_incident - the maximum number of snapshots saved for one "incident". Default is 10.
  • litespeed - enable or disable data import from LiteSpeed, default is auto (autodetect); On|on|1 - force use litespeed; Off|off|0 - force use apache

/etc/sysconfig/lvestats.config/StatsNotifier.cfg contains the following options:

  • NOTIFY_ADMIN – enables notification for admin (Y/N, default N);
  • NOTIFY_RESELLER – enables notification for reseller (Y/N, default N);
  • NOTIFY_CUSTOMER - enables notification for customers (Y/N, default N);
  • NOTIFY_INCLUDE_RESELLER_CUSTOMERY=notify all users, N=notify only hoster's users (whos reseller is root), default = N;
  • NOTIFY_CPU – notify about CPU faults when customer hits 100% of his CPU limit (Y/N, default N);
  • NOTIFY_IO - notify about IO faults when customer hits 100% of his IO limit (Y/N, default N);
  • NOTIFY_IOPS - notify about IOPS faults when customer hits 100% of his IOPS limit (Y/N, default N);
  • NOTIFY_MEMORY - notify about memory faults (Y/N, default N);
  • NOTIFY_EP – notify about entry processes faults (Y/N, default N);
  • NOTIFY_NPROC – notify about number of processes faults (Y/N, default N);
  • NOTIFY_MIN_FAULTS_ADMIN – minimum number of faults to notify admin (default 1);
  • NOTIFY_MIN_FAULTS_USER – minimum number of faults to notify customer (default 1);
  • NOTIFY_INTERVAL_ADMIN – period of time to notify admin (default 12h);
  • NOTIFY_INTERVAL_USER – period of time to notify customer (default 12h);
  • NOTIFY_FROM_EMAIL - sender email address. For example: NOTIFY_FROM_EMAIL=main_admin@host.com;
  • NOTIFY_FROM_SUBJECT - email message subject. For example: NOTIFY_FROM_SUBJECT=Message from notifier
  • REPORT_ADMIN_EMAIL - custom email for admin reporting. For example: REPORT_ADMIN_EMAIL=report_email@host.com
  • NOTIFY_CHARSET_EMAIL – charset type for email. Available for lve-stats-2.9.4-1 and later. Default is us-ascii. For example: NOTIFY_CHARSET_EMAIL=utf-8. If your email templates include non-Latin letters, it is recommended to use the UTF-8 encoding.

These values can also be set using cloudlinux-config CLI utility

Templates of notifications are located here:

  • /usr/share/lve/emails/en_US/admin_notify.txt
  • /usr/share/lve/emails/en_US/reseller_notify.txt
  • /usr/share/lve/emails/en_US/user_notify.txt
  • /usr/share/lve/emails/en_US/admin_notify.html
  • /usr/share/lve/emails/en_US/reseller_notify.html

Note

Notifications about LVE faults are implemented for CPanel, Plesk, and DirectAdmin.

After changing any options above, please restart lvestats service:

service lvestats restart

/etc/logrotate.d/lvestats - configuration file for /var/log/lve-stats.log rotation

Note

You can also use LVE-stats 2 CLI

LVE-stats2 and DB servers compatible work setup

LVE-stats2 and MySQL DB server compatible work setup

Note

Run all the commands below under root.

1. MySQL server setup

If MySQL Server is not installed, then install it according to control panel documentation.

For non-panel system:

  • CloudLinux OS Shared 6

    yum install mysql mysql-server
    service mysqld start
    chkconfig mysqld on
    
  • CloudLinux OS Shared 7

    yum install mariadb mariadb-server
    systemctl start mariadb.service
    systemctl enable mariadb.service
    

2. Database setup

  • Run MySQL administrative utility: mysql.

  • In utility run the commands:

    • creating server DB. Also, check Note below.
    CREATE DATABASE db_lvestats2;
    
    • creating a user for LVE Stats 2 server to work under. Also, check Note below.
    CREATE USER 'lvestats2'@'localhost' IDENTIFIED BY 'lvestats2_passwd';
    
    • granting all the privileges for all DB tables to the user. Use the username and DB name from the points above.
    GRANT ALL PRIVILEGES ON db_lvestats2.* TO 'lvestats2'@'localhost';
    
    • refreshing privileges information.
    FLUSH PRIVILEGES;
    
    • Exit administrative utility (Ctrl+d).

Note

DB name, username and their passwords above are given for an example - you can use any of your choices. Using old DB from LVE Stats version 1 is also acceptable as LVE Stats2 uses different tables and the old information will not be corrupted.

3. LVE-stats2 setup

  • Stop LVE Stats 2 server by running the command:
service lvestats stop
  • In server configuration file /etc/sysconfig/lvestats2, edit the following options:
    • db_type = mysql
    • connect_string = lvestats2:lvestats2_passwd@localhost/db_lvestats2

Note

connect_string option value is used in format: user:pass@host/database. Username, password and DB name must be the same as in point 2 of Database Setup above.

  • After making changes in configuration files run:
/usr/sbin/lve-create-db 

For DB primary initialization (creating tables, indexes, etc). There is no need to create anything in the DB manually.

  • When done, restart server by running:
service lvestats restart

4. Additional security settings

If you need to provide access to LVE Stats information utilities (lveinfo, lvechart, lve-read-snapshot) for different users, then we recommend creating one more DB user with read-only privilege to guarantee information security. It can be done by running the following commands in administrative utility:

  • creating a user (check Note above)
CREATE USER 'lvestats2_read'@'localhost' IDENTIFIED BY 'lvestats2_read_passwd';
  • granting read-only privilege to the user
GRANT SELECT ON db_lvestats2.* TO 'lvestats2_read'@'localhost';
  • refreshing privileges information
FLUSH PRIVILEGES;

If LVE Stats2 server is set correctly (see information below), the information utilities will work under this user.

If you need to provide access to information utilities to other users, then in order to guarantee information security you should do the following:

  • Assign permission 600 to the main configuration file (/etc/sysconfig/lvestats2), so that it could be read only by LVE Stats 2 server and by utilities that run under root.
  • Copy /etc/sysconfig/lvestats2 to /etc/sysconfig/lvestats2.readonly, assign permission 644 to the new file, so that it could be read by any user but could only be changed by root.
  • In /etc/sysconfig/lvestats2.readonly file, in the line connect_string, specify DB user with read-only permission, created above.

These steps allow hiding main DB user username/password from other system users.

If there is no need in such access differentiation, then /etc/sysconfig/lvestats2 file access permission should be 644, so that it could be read by all users and could be changed only by root.

5. Using special characters in database password

Since scheme ://user:password@host[:port]/database_name URI is used in connect_string config option, then usage of special characters in user DB password is not allowed.

To use special symbols in the password, it must be converted to escape-sequence. You can convert a password to escape-sequence in a console as follows:

echo -n '[You_P@$$]:' | perl -MURI::Escape -ne 'print uri_escape($_)."\n"'
%5BYou_P%40%24%24%5D%3A

Or replace the symbols manually:

!    #    $    &    '    (    )    *    +    ,    /    :    ;    =    ?    @   [    ]
%21  %23  %24  %26  %27  %28  %29  %2A  %2B  %2C  %2F  %3A  %3B  %3D  %3F  %40  %5B %5D

After that сonnect_string will look as follows:

сonnect_string=lvestats2:%5BYou_P%40%24%24%5D%3A@localhost/db_lvestats2

LVE-stats2 and PostgreSQL DB server compatible work setup

Note

Run all the commands below under root.

1. PostgreSQL server installation and setup

  • PostgreSQL installation and initialization

    For control panels use proper documentation for installation on the links: сPanel, Plesk.

    For non-panel CloudLinux OS Shared, run the following commands:

    • CloudLinux OS Shared 6
    yum install postgresql-server postgresql
    service postgresql initdb
    service postgresql start
    chkconfig postgresql on
    
    • CloudLinux OS Shared 7
    yum install postgresql-server postgresql
    postgresql-setup initdb
    systemctl start postgresql
    systemctl enable postgresql
    
  • Setup

    • In /var/lib/pgsql/data/pg_hba.conf config file change user authentication mode. Add the following lines (place before all other authentication parameters):

      # IPv4 local connections for lve-stats-2.x
      host dblvestat all 127.0.0.1/32 password
      # IPv6 local connections for lve-stats-2.x
      host dblvestat all ::1/128 password
      

      These lines enable user authentication by the password for IP4/IP6 connections. You can set other modes if needed.

    • Apply config changes by running:

      service postgresql restart
      

2. DB for LVE-stats-2.x - creating and setup

  • Run standard PostgreSQL psql administrative utility:
sudo -u postgres psql postgres 

OR for сPanel

psql -w -U postgres
  • In utility run:

    • creating server DB. Also, check Note below.

      CREATE DATABASE dblvestat;
      
    • creating a user for LVE Stats 2 server to work under. Also, check Note below.

      CREATE USER lvestat WITH password 'passw';
      
    • granting lvestat user all privileges for work with dblvestat DB.

      GRANT ALL privileges ON DATABASE dblvestat TO lvestat;
      
    • exit psql utility:

      \q
      

      OR alternatively:

      Ctrl+d
      

Note

DB name, username and their passwords above are given for an example - you can use any of your choices. Using old DB from LVE Stats version 1 is also acceptable as LVE Stats 2 uses different tables and the old information will not be corrupted

3. LVE-stats-2.x setup

  • Stop lve-stats2 server by running:
service lvestats stop
  • In server config file /etc/sysconfig/lvestats2 edit options for connecting to DB:
db_type = postgresql
connect_string=lvestat:passw@localhost/dblvestat
If DB is going to be used as centralized for multiple hosts then collect_usernames parameter must be changed:
collect_usernames=true

Note

connect_string option value is of the format: user:pass@host/database. Username, password and DB name must be the same as in Database Setup section above.

  • After making changes in configuration files, for DB primary initialization (creating tables, indexes, etc), run:
/usr/sbin/lve-create-db 
  • There is no need to create anything in the DB manually. When done, restart server by running:
service lvestats restart

4. Additional security settings

If you need to provide access to LVE Stats information utilities (lveinfo, lve-read-snapshot ) for other users (or if CageFS is disabled), then in order to guarantee DB security the following steps are required:

  • Create a DB user with read-only permission:
CREATE USER lvestat_read WITH password 'passw';
GRANT CONNECT ON DATABASE dblvestat to lvestat_read;
\connect dblvestat;
GRANT SELECT ON lve_stats2_history, lve_stats2_history_gov, lve_stats2_history_x60, lve_stats2_incident, lve_stats2_servers, lve_stats2_snapshot, lve_stats2_user TO lvestat_read;
  • Assign root ownership and permission 600 to the main configuration file (/etc/sysconfig/lvestats2), so that it could be read only by LVE Stats 2 server and by utilities that run under root.

  • Copy /etc/sysconfig/lvestats2 to /etc/sysconfig/lvestats2.readonly, assign permission 644 to the new file, so that it could be read by any user but could be changed only by root.

  • In /etc/sysconfig/lvestats2.readonly file, in the line connect_string, specify DB user with read-only permission, created above.

    These steps allow hiding main DB user username/password from other system users.

    If there is no need in such access differentiation, then /etc/sysconfig/lvestats2 file access permission should be 644, so that it could be read by all users and could be changed only by root.

  • When done, restart server by running:

service lvestats restart

5. Using special characters in database password

Since scheme ://user:password@host[:port]/database_name URI is used in connect_string config option, then usage of special characters in user DB password is not allowed.

To use special symbols in the password, it must be converted to escape-sequence. You can convert a password to escape-sequence in a console as follows:

echo -n '[You_P@$$]:' | perl -MURI::Escape -ne 'print uri_escape($_)."\n"'
%5BYou_P%40%24%24%5D%3A

OR replace the symbols manually:

!    #    $    &    '    (    )    *    +    ,    /    :    ;    =    ?    @    [    ]
%21  %23  %24  %26  %27  %28  %29  %2A  %2B  %2C  %2F  %3A  %3B  %3D  %3F  %40  %5B  %5D

After that сonnect_string will look as follows:

сonnect_string=lvestats2:%5BYou_P%40%24%24%5D%3A@localhost/db_lvestats2

Customize LVE-stats2 notifications

Jinja2 is used as a template engine for the notifications.

The templates for notifications are located in /usr/share/lve/emails/LOCALE, where LOCALE is the directory with localization name (language codes are formed according to ISO 639-1 and ISO 639-2).

By default the templates for English are set: /usr/share/lve/emails/en_US..

/usr/share/lve/emails/en_US contains the following templates:

  • admin_notify.html admin_notify.txt for administrator;
  • reseller_notify.html reseller_notify.txt for reseller;
  • user_notify.txt for user.

Starting from lve-stats-4.1.2, it allows server administrators to use their own lvestats notifier email notification templates.

To use the custom templates, place them and the locales.json file to the /etc/cl.emails.d/LOCALE directory. File names are the same as in general /etc/cl.emails.d/LOCALE file.

If the /etc/cl.emails.d/LOCALE doesn't exist or doesn't contain the templates, you can use the /usr/share/lve/emails/LOCALE directory, as it was in previous versions of lve-stats.

Please do not edit the templates in the the /usr/share/lve/emails and place the new updated templates in the /etc/cl.emails.d directory.

The notification is formed as Multipart content type RFC1341(MIME).

The plain text is taken from the .txt files, html version - from the .html template.

In case when only one template is present (.txt or .html) the notification is sent as a Non-multipart content type notification.

It is better to use Multipart content type notifications because when a mail client can not display an html-format message, then it will be displayed as plain text version.

To localize notifications, copy standard templates into directory with the proper locale name and translate the template. Also you can customize the main template making proper changes into it.

The list of variables that can be used in the template:

VariableExampleDescription
TONAMECustomerNotification receiver user name. Taken from profile in the control panel, by default - Customer for user, Administrator for administrator, Reseller for reseller.
TOMAILsupport@cloudlinux.comNotification receiver email address.
DOMAINwordpress.test247.cloudlinux.comMain domain. Available only for user.
LOCALEen_USLocale in which the notification is sent. Available only for user.
RESELLERrootUser reseller. Available only for user.
PERIOD12 hoursVerification and notification sending period.
LOGINwordpressUser login in the system.
ID500User ID in the system.
lPMem lEP PMemF lVMem anyF IOf VMemF lCPU aIOPS aEP aPMem IOPSf lIO lIOPS aIO EPf aCPU aVMem NprocF aNproc lNproc CPUfSee description in lveinfo --help output. Available only for users
STATS_HTMLhtml table with the list of users that exceeded limits. Available for administrator and reseller.
STATSASCII - table with the list of users that exceeded limits. Available only for admins and resellers.

Sender’s email address by default is administrator email address from control panel settings (root@{hostn_name} if there is no email in the control panel).

It can be changed with NOTIFY_FROM_EMAIL option in the config /etc/sysconfig/lvestats.config/StatsNotifier.cfg

For example:

NOTIFY_FROM_EMAIL=support@hostername.com

To apply changes restart lve-stats service:

service lvestats restart

for CloudLinux OS Shared 7

systemctl restart lvestats.service

Default subject is Hosting account resources exceeded.  It can be changed for each template (and for localized templates as well). To change subject, in the very beginning of the file (no blank lines allowed in the beginning of the template) add the field Subject:, leave two blank lines after it and add template body.

Customized subjects can be taken only from the templates with the resolution *.txt (admin_notify.txt, reseller_notify.txt, user_notify.txt). Changes apply without lvestats restart.

For backward compatibility the subject can be also changed with the key NOTIFY_FROM_SUBJECT in the config /etc/sysconfig/lvestats.config/StatsNotifier.cfg.

Customized subjects have the higher priority than the key NOTIFY_FROM_SUBJECT.

Example for the file user_notify.txt

Subject: Customized subject example
Dear {{TONAME}},
 
Your {{DOMAIN}} web hosting account exceeded one or more of its resources within the last {{PERIOD}}.
{% if epf %}Exceeded the maximum of {{lep}} concurrent website connections. Your website was not available {{epf}} times because of this problem.
{% endif %}{% if pmemf %}Exceeded the physical memory limit of {{lpmem}}KB. Your website was not available {{pmemf}} times because of this problem.
{% endif %}{% if vmemf %}Exceeded the virtual memory limit of {{lvmem}}KB. Your website was not available {{vmemf}} times because of this problem.
{% endif %}{% if nprocf %}Exceeded the number of processes limit of {{lnproc}}. Your website was not available {{nprocf}} times because of this problem.
{% endif %}{% if cpuf %}You reached limit of {{lcpu}} of total server CPU usage {{cpuf}} times. Your website was forced to load slower to reduce its CPU usage.
{% endif %}{% if iof %}You reached limit of {{lio}}KB/s disk io rate {{iof}} times. The disk io speed for your account was slowed as a result of this problem.
{% endif %}{% if iopsf %}You reached limit of {{liops}} I/O operations {{iopsf}} times. The disk io speed for your account was slowed as a result of this problem.
{% endif %}
 
To view full details about your web hosting account's resource usage, including the time of each incident listed above, please click the link below and log into your cpanel hosting control panel, then click the "Resource Usage" link under the "Logs and Statistics" section.
http://{{DOMAIN}}:2083
 
If your account is regularly exceeding it's available resources, please consider upgrading to a higher level hosting plan that includes more resources. If you have any questions or need help with anything, just reply to this email and let us know.
 
Sincerely,
 
Your Friendly Web Hosting Support Team

Note

The email template should be in the UTF-8 encoding. The other encodings are not supported.

Starting from lve-stats v. 3.0.15-1, it allows to localize the words that before were imported directly from the lve-stats program code and couldn't be changed.

The email template looks like follows:

Dear {{TONAME}},
Following accounts experienced issues in the past {{PERIOD}}{% if HOSTNAME is defined %} on host {{HOSTNAME}}{% endif %}

{{STATS}}

Sincerely,
Your Friendly Web Hosting Support Team

Before, the {TONAME} was replaced with “Administrator”/”Reseller”/”Customer” according to the type of the addressee. The {PERIOD} was replaced with the duration of the period during which the limits faults were detected. The period duration includes the words "days"/"hour"/"minutes"/"seconds".

In the new version, in order for the lve-stats notifier to include these words in the letter in the language corresponding to the addressee locale, you should create a file for the required locale /usr/share/lve/emails/<encoding_name>/locale_defines.json with the following content:

{
 "NOTIFY_FROM_SUBJECT": "Your server has lve-stats faults",
 "PERIOD": {
   "days": "days", "hours": "hour(s)", "minutes": "minutes", "seconds": "seconds"
 },
 "TONAME": {
   "admin": "Administrator", "reseller": "Reseller", "customer": "Customer"
 }
}

The file format should be JSON, and the file encoding should be UTF-8. If this file is found and successfully read, the words from it will be used in emails. In case of any file reading/parsing error, a corresponding message will be written in the lve-stats log, and the contents of the file will be completely ignored. If a key in the JSON file content is missing, then lve-stats notifier uses the word contained in the body of the program (just like in previous lve-stats versions). Also, this file allows you to override/localize the subject of the email.

It should be noted that this override of the subject line is the highest priority. I.e., if you want to use the subject from the configuration file /etc/sysconfig/lvestats.config/StatsNotifier.cfg, then do not specify the NOTIFY_FROM_SUBJECT key in the locale_defines.json.

Plugins

LVE-stats2 comes with a set of generic plugins:

Plugin NameOrderDefaultPeriod (seconds)Description
LVECollector1000Y5Collects usage/limits data from /proc/lve/list
CPUInfoCollector2000Y5collents info about CPU - /proc/cpuinfo
LVEUsernamesCollector3000Y3600collects usernames & user ids to match uid <-> lve id later on
LVEUsageAnalyzer4000Y5analyzes usage of LVE
LveUsageAggregator5000Y60aggregates data by time periods
DBGovSaver6000Y5Saves data about database governor
FileSaver7000Y5Saves LVE data into /var/lve/info
CloudLinuxTopFileSaver8000Y60saves data used by cloudlinux-top to /var/lve/cloudlinux-top.json
DBSaver9000Y60save LVE data to database
DbUsernamesSaver10000Y3600saves users name to database
DBSaverX6011000Y3600saves aggregated hourly data into database
SnapshotSaver12000Y30collects & saves snapshots data
StatsNotifier13000Yvariednotify user/admin based on usage
HistoryCleaner14000Y3600removes old usage
ResMEMCollector1500N30collects physical memory usage from processes RES field instead of /proc/lve/list
LVEDestroyer-N5destroys LVEs that weren't active for X iterations. Number of iterations is passed from config using iterations variable. iterations=0 means plugin disabled

To enable non-default plugin, copy or link it to /usr/share/lve-stats/plugins directory.

For example to enable ResMEMCollector plugin, do:

ln -s /usr/share/lve-stats/plugins.other/res_mem_collector.py /usr/share/lve-stats/plugins/
service lvestats restart

Creating a plugin for LVE-stats2

Introduction

LVE Stats 2 complex has scalable architecture, which allows to connect custom plugins.

LVE Stats server searches for plugins in the directory which is specified with plugins parameter of server’s /etc/sysconfig/lvestats2 configuration file. Default directory is /usr/share/lve-stats/plugins.

Each plugin must be of a Python class, must be written in Python language and its file must have .py extension. Files with all other extensions will be ignored. For normal server work access permission 400 is enough; owner – root .

Plugin classes can be of the same name, but better not, because classes' names can affect the set of parameters in set_config method. You can find detailed plugins' configuring information below, in appropriate chapter.

Plugin class must contain execute() method, which is invoked by the server every 5 seconds (by default, can be changed by interval parameter of configuration file). Also set_config method (configuration settings) can be available. You can find more info in Plugins Configuration chapter.

Additionally the following attributes can be set (plugin class instance variable):

  • order (integer) - defines plugin's position in the server's plugin list, (more info in Servers Plugin Arrangement ).
  • timeout (integer or float ) – the longest allowable duration of one launch of the plugin (execute method). Default value of timeout parameter is 5 seconds.
  • period (integer) – sets the interval between two launches of execute plugin method in seconds. If not defined, then plugin runs every 5 seconds ( interval parameter in configuration file).

When execute() method of the plugin is invoked, the server creates an attribute now in it, where launch time is recorded. This value is equal to what a standard Python function time.time() returns. All the plugins launched one after another receive the same  value of now attribute from the server. now is overwritten before execute() method is invoked.

The previous value of now attribute is not saved by the server. If plugin needs it, it has to save it by itself.

Plugin class can be inherited from LveStatsPlugin class, which is the part of the server itself. This is not obligatory, but inheritance can help to avoid different errors in servers work, particularly if a plugin doesn't contain required execute method.

LveStatsPlugin class is defined in the file: /opt/alt/python27/lib/python2.7/site-packages/lvestats/core/plugin.py .

Server plugin arrangement

When the server starts, it performs the search of plugins in the directory specified in /etc/sysconfig/lvestats2 configuration file. This directory is scanned only when the server starts, therefore if any plugin was added into the directory, the server has to be restarted with the following command:

service lvestats restart.

After successful restart, the plugins are graded and executed ascending by order attribute. If any plugin order attribute is not set, it is considered as a Python language constant sys.maxint (which is usually 9223372036854775807). This in fact means that such plugins will be executed in the last. If any plugins has similar order meanings, their execution order is unpredictable.

The server invokes execute method of all plugins one after another.

When the server invokes execute() method of any plugin, it transmits a data dictionary ( lve_data argument) into plugin. The dictionary is common for all the plugins. Any plugin can read, write and change any data in this dictionary. LVE Stats 2 server doesn't control this area. That is why one must be careful while developing new plugins, in order not to change or corrupt other plugins' data which can break their functionality.

If an exception occurs in execute() method, its text and python stack trace is recorded into server log /var/log/lve-stats and all the changes made to lve_data dictionary before the exception happened are lost.

The keys of the lve_data dictionary are recommended to look like PluginName_Key , in order the plugins do not corrupt other data accidentally.

Server contains some standard plugins which define and use the following keys in the common dictionary lve_data: LVE_VERSION, stats, old_stats, procs and lve_usage . User plugins can use data from these keys, but it is recommended not to change them if there is no special need, because it can break the next plugins in the execution queue.

KeyContent
LVE_VERSIONLVE version. The same as console command lvectl lve-version produces.
statsDictionary, that contains lve id’s as keys and LVEStat class objects as values. Every LVEStat object contains values of usages and limits taken from /proc/lve/list file for every LVE Id . Dictionary keys – integer lve id , including 0 for “ default ” LVE. This dictionary is updated on each iteration of lvestats-server (every 5 seconds by default). LVEStat – is a standard server class, it can be imported with the command from lvestats.core.lvestat import LVEStat. The class is described in the file /opt/alt/python27/lib/python2.7/site-packages/lvestats/core/lvestat.py . Here you can find the whole list of data fields and their functions.
old_statsstats content from the previous iteration. Before the first iteration – empty dictionary.
totalHzWhen LVE_VERSION is 4, real CPU frequency in Hz multiplied by number of cores. When LVE_VERSION > 4, CPU speed is in conventional units and equals to 1000000000 * cores (1 GHz per core).
procsQuantity of CPU cores.
lve_usagesContains accumulated LVE statistics for each 5-seconds interval in current minute. Cleared each minute.
lve_usageContains aggregated LVE Statistics for “previous” minute to store to database. Overwritten each minute.

Each plugin’s instance lifetime is from the moment it was loaded till the server stops working. But if execute() method working time exceeds timeout, the plugin will be terminated and restarted in the next iteration. All changes to the lve_data dictionary will be lost.

During servers graceful shutdown (restart, server shutdown, commands service lvestats stop, service lvestats restart ), each plugin receives SIGTERM signal. This is useful to correctly unload the plugin (terminate all subsidiary processes, save data to files etc.). If a plugin doesn't need to “finalize” its execution before termination, then there's no need to implement this signal handler. Below you can see an example of such handler.

Note

If a plugin implements handler for SIGTERM, then this handler must end with sys.exit(0) command. Otherwise plugin process will not be terminated correctly and will become orphaned.

Plugin configuration

LVE Stats 2 Server allows to configure each plugin separately.

On initialization stage the server invokes set_config() method of the plugin and locates there a dictionary which contains:

  • all parameters from file /etc/sysconfig/lvestats2 (global).
  • plugin's individual configuration file parameters (if one exists). Configuration files must be located in /etc/sysconfig/lvestats.config directory, have .cfg extension and be the same format as /etc/sysconfig/lvestats2 . Files in this directory are matched with the plugins by name. For instance, if plugin class is Plugin1_class , then server will try to find and download /etc/sysconfig/lvestats.config/Plugin1_class.cfg. Names are case sensitive. If any plugin doesn't have an individual configuration file, then it's not an error. In this case plugin will just receive parameters from /etc/sysconfig/lvestats2 .

Note

An individual configuration file of every plugin is loaded after server configuration file. That is why if it contains any parameters with names similar to ones of server config, then plugin will use parameters from its individual config rather than server config parameters.

If a plugin doesn't require any configuration to be done, then set_config method can be skipped.

In addition, plugins can use their own configuration methods.

Types of plugins

According to server architecture, plugins can be of the following types:

  • collectors
  • analyzers
  • persistors
  • notifiers

Collectors are designed to collect information; analyzers – to analyze it and form some other data on its basis; persistors – to save information from the common dictionary into files, databases, etc.; notifiers - to notify system users about any events.

This division is rather arbitrary. There is an opportunity to execute all the actions on collection, analysis and saving the information in one and only plugin. But at the same time the division into functionally independent parts allows to build flexible and easily configurable system for collecting and processing the data.

Also it is possible to implement the systems of lazy-write, planning of collecting/processing tasks and notifying users about different events.

Examples of plugins

Here is a practical example of a user plugin.

Specification:

  1. To trace specified file size changes. The name of file being traced must be specified in configuration file, which allows to change it without modifying the plugin itself. If file size has been changed, it has to be written as a message into our log. The name of log must be specified in configuration file as well.

  2. File size must be checked with default interval (5 seconds), and log notification must be held once a minute (to avoid resource expend for possibly regular write).

  3. System administrator must receive emails with file size at the moment the email was sent. These notifications must be sent even if the file size hasn’t been changed. Notification emails must be read periodicity from configuration file as well as sender/receiver emails .

As file size check, fixing the result and notification sending must be held with different periods, then it’s impossible to realize all the tasks by means of one plugin. The fact that one minute (60 seconds) is multiple to 5 seconds doesn’t matter in this case, because default period can be changed in server’s configuration file, but the condition of fixing the result once a minute is a part of the specification, which can not be violated. In addition, notification email period is known in advance, as it is specified by user in configuration file.

That is why we realize 4 plugins: collector, analyzer, persistor and notifier.

Collector

Collector's aim is to determine the size of a proper file.

# FSize_watcher_collector.py
# Example plugin for monitoring file size.
# Part 1. Collector

import os
from lvestats.core.plugin import LveStatsPlugin 

# Key name
COLLECTOR_KEY = 'FSizeWatcher_fsize'
COLLECTOR_KEY_FILENAME = 'FSizeWatcher_fname'  

class FSize_watcher_collector (LveStatsPlugin):
	# this plugin should be first in chain
	order = 0
	# File to monitoring
	file_to_monitoring = None 
	
	def __init__(self):
		pass 
		
	# Sets configuration to plugin
	def set_config(self, config):
		self.file_to_monitoring = config.get('file_to_monitoring', None)
		pass
	# Work method
	def execute(self, lve_data):
		try:
			# if monitoring file absent, do nothing
			if self.file_to_monitoring is None or not os.path.exists(self.file_to_monitoring):
		return 
		
			# Get file size
			stat_info = os.stat(self.file_to_monitoring)
			fsize = stat_info.st_size 
			
			# Place file name and file size to server data dictionary
			lve_data[COLLECTOR_KEY_FILENAME] = self.file_to_monitoring
			lve_data[COLLECTOR_KEY] = fsize
		except (OSError, IOError):
			# file absent or any other error - remove size from dictionary
			del lve_data[COLLECTOR_KEY]

Plugin algorithm is extremely simple – file size is read and written into data dictionary. Files name is read from set_config method configuration. If the name is not specified, then None is written into appropriate variable. All the errors are completely ignored (e.g. if specified file doesn't exist or there's no way to read any of it's information).

order attribute is specified as 0 to make this plugin go the first among three. Data collector must always be the first in plugins logical chain, because it provides all the necessary information for the analyzer which goes the next. Specific values of order can be of any kind, but what is important is that when the server starts, all the plugins line up in proper sequence: collector – analyzer – persistor .

In order to make plugin work, we have to create configuration file /etc/sysconfig/lvestats.config/FSize_watcher_collector.cfg with the following content:

# Config file for FSize_watcher_collector plugin
# Please define monitoring file here
# file_to_monitoring = /usr/local/cpanel/logs/error_log
file_to_monitoring = /usr/local/cpanel/logs/access_log

Note that file name FSize_watcher_collector without .cfg extension matches plugin class name.

file_to_monitoring option is read by plugin in set_config method and contains file’s full name for monitoring.

Files for monitoring, suggested in the actual example - /usr/local/cpanel/logs/error_log and /usr/local/cpanel/logs/access_log - are real, these are cPanel control panel logs.

The first file is errors log; the second is appeal log, is refreshed during common work with panel (e.g. if user email address is changed).

Errors log tracking is more important, but appeal log monitoring allows to illustrate plugins work more in details, because it is refreshed more often.

Note that plugin can monitor one file only.

Analyzer

Analyzer decides if the file's size has changed and gives a command to persistor to refresh log.

# FSize_watcher_analyzer.py
# Example plugin for monitoring file size.
# Part 2. Analyzer 

from lvestats.core.plugin import LveStatsPlugin 

# Key name from collector plugin
COLLECTOR_KEY = 'FSizeWatcher_fsize' 

# Key name 1 for saver plugin
SAVER_KEY = 'FSizeWatcher_fsize_to_store'
# Key name 2 for saver plugin
SAVER_DATA_PRESENCE = 'FSizeWatcher_fsize_present'  

class FSize_watcher_analyzer (LveStatsPlugin):
	# this plugin should be second in chain
	order = 1
	# Last file size
	file_last_size = 0
	# Plugin run period in secondsperiod = 60 
	
	def __init__(self):
		pass 
		
	# work method
	def execute(self, lve_data):
		# Default setting for saver
		lve_data[SAVER_DATA_PRESENCE] = 0
		# Check presence data
		if COLLECTOR_KEY not in lve_data:
		return 
		
		# Get file size from server data dictionary
		fsize = lve_data[COLLECTOR_KEY] 
		
		# Check, if file size changed, store it for saver plugin
		if fsize == self.file_last_size:
			return 
			
		# Put new size for saver plugin
		lve_data[SAVER_KEY] = fsize
		self.file_last_size = fsize
		lve_data[SAVER_DATA_PRESENCE] = 1

This plugin is extremely simple as well. It starts after collector (order=1) , searches for file size in the dictionary and compares it with the previous index. If it has changed, then it writes a sign of presence of a new size into the dictionary. If no changes seen, then sign resets. The previous file size is stored in the plugin itself in file_last_size variable. Note that they are stored during the whole server lve-stats lifetime.

If file size is not found in data dictionary, then plugin just ends.

All the errors are completely ignored.

Analyzer is unconfigurable, that is why it doesn’t require any configuration file and it doesn’t contain set_config method.

Plugin starts every 60 seconds (1 minute), because we need data fixation to be performed one time in a minute.

Persistor

Persistor saves information from the common dictionary into files, databases, etc.

# FSize_watcher_saver.py
# Example plugin for monitoring file size and last modification date-time.
# Part 3. Data saver 

import signal
import sys
import time
from lvestats.core.plugin import LveStatsPlugin 

# Key name 1 for saver plugin
SAVER_KEY = 'FSizeWatcher_fsize_to_store'
# Key name 2 for saver plugin
SAVER_DATA_PRESENCE = 'FSizeWatcher_fsize_present'
# Monitoring file name
COLLECTOR_KEY_FILENAME = 'FSizeWatcher_fname'  

class FSize_watcher_saver (LveStatsPlugin):
	# this plugin should be third in chain
	order = 2
	# Plugin run period in seconds
	period = 60
	# Log filename
	log_file_name = None
	# First run flag
	is_first_run = True 
	
	def __init__(self):
		signal.signal(signal.SIGTERM, self.sigterm_handler) 
		
	# Sets configuration to plugin
	def set_config(self, config):
		# Get log filename
		self.log_file_name = config.get('log_filename', None) 
		
	# work method
	def execute(self, lve_data):
		# do nothing, if log file not defined
		if not self.log_file_name:
			return
		try:
			# Check presence data
			if SAVER_DATA_PRESENCE not in lve_data or lve_data[SAVER_DATA_PRESENCE] == 0:
				# No data
				return
			# Get file size from server data dictionary
			fsize = lve_data[SAVER_KEY]
			
			# Store data to log
			f = open(self.log_file_name, 'a')
			if self.is_first_run:
				f.write('%s - FSize_watcher started. Monitoring file: %s, saving data period=%d sec\n' % (time.asctime(time.localtime()), lve_data[COLLECTOR_KEY_FILENAME], self.period))
				self.is_first_run = False
			f.write('%s - FSize_watcher: file size is %d bytes\n' % (time.asctime(time.localtime()), fsize))
			f.close()
		except:
			# Ignore all errors
			pass 
			
	# Terminate handler
	def sigterm_handler(self, signum, frame):
		if self.log_file_name:
			try:
				# Store data to log file
				f = open(self.log_file_name, 'a')
				f.write('%s - File watcher saver plugin: TERMINATE\n' % time.asctime(time.localtime()))
				f.close()
				pass
			except:
				# Ignore all errors
				pass
		# Terminate process
		sys.exit(0)

Configuration file /etc/sysconfig/lvestats.config/FSize_watcher_saver.cfg:

# Config file for FSize_watcher_saver.py plugin
# Please define log filename here
log_filename = /var/log/FSize_watcher.log

This plugin starts after analyzer (order=2) , checks new file size presence flag, and if positive – writes it into log. If the flag is cleared (which means the size hasn't changed), then plugin simply ends.

Starts once in a minute (period=60).

Also this plugin shows the work of signal handler.

Plugin constructor registers handler-function of a proper signal: signal.signal(signal.SIGTERM, self.sigterm_handler) . This means, that when the server finishes its work, then sigterm_handler method of plugin class will be invoked. In the actual example the function just writes a notification into log, tracing the fact of it's invocation.

Pay attention on sys.exit(0) command in the end of the handler. Find the information on it in Server Plugin Arrangement section.

In addition see into examples of file log /var/log/FSize_watcher.log formed by the plugins above:

Tue Feb  3 13:06:24 2015 - FSize_watcher started. Monitoring file: /usr/local/cpanel/logs/access_log, saving data period=60 sec
Tue Feb  3 13:06:24 2015 - FSize_watcher: file size is 122972890 bytes
Tue Feb  3 13:07:25 2015 - FSize_watcher: file size is 122975507 bytes
Tue Feb  3 13:08:25 2015 - FSize_watcher: file size is 122978124 bytes
Tue Feb  3 13:09:25 2015 - FSize_watcher: file size is 122978997 bytes
Tue Feb  3 13:10:25 2015 - FSize_watcher: file size is 122981033 bytes
Tue Feb  3 13:11:25 2015 - FSize_watcher: file size is 122982052 bytes
Tue Feb  3 13:13:25 2015 - FSize_watcher: file size is 122983798 bytes
Tue Feb  3 13:20:15 2015 - File watcher saver plugin: TERMINATE

and

Thu Feb  5 13:07:27 2015 - FSize_watcher started. Monitoring file: /usr/local/cpanel/logs/error_log, saving data period=60 sec
Thu Feb  5 13:07:27 2015 - FSize_watcher: file size is 14771849 bytes
Thu Feb  5 14:03:32 2015 - FSize_watcher: file size is 14771995 bytes
Thu Feb  5 15:01:36 2015 - FSize_watcher: file size is 14772434 bytes
Thu Feb  5 17:15:47 2015 - FSize_watcher: file size is 14772873 bytes
Thu Feb  5 18:47:54 2015 - FSize_watcher: file size is 14775213 bytes
Thu Feb  5 19:11:56 2015 - FSize_watcher: file size is 14775652 bytes
Thu Feb  5 21:09:05 2015 - FSize_watcher: file size is 14776091 bytes
Thu Feb  5 23:06:14 2015 - FSize_watcher: file size is 14776530 bytes
Fri Feb  6 00:47:23 2015 - FSize_watcher: file size is 14778870 bytes
Fri Feb  6 01:02:24 2015 - FSize_watcher: file size is 14779309 bytes
Fri Feb  6 02:00:28 2015 - FSize_watcher: file size is 14779434 bytes
Fri Feb  6 03:16:34 2015 - FSize_watcher: file size is 14779873 bytes
Fri Feb  6 05:04:42 2015 - FSize_watcher: file size is 14779998 bytes
Fri Feb  6 05:12:43 2015 - FSize_watcher: file size is 14780437 bytes
Fri Feb  6 05:56:50 2015 - FSize_watcher: file size is 14780551 bytes
Fri Feb  6 06:01:50 2015 - FSize_watcher: file size is 14780975 bytes
Fri Feb  6 06:03:51 2015 - FSize_watcher: file size is 14782183 bytes
Fri Feb  6 06:04:51 2015 - FSize_watcher: file size is 14782575 bytes
Fri Feb  6 06:18:52 2015 - FSize_watcher: file size is 14782647 bytes
Fri Feb  6 06:21:52 2015 - FSize_watcher: file size is 14782898 bytes
Fri Feb  6 06:48:54 2015 - FSize_watcher: file size is 14785238 bytes
Fri Feb  6 07:09:56 2015 - FSize_watcher: file size is 14785677 bytes
Tue Feb  6 08:03:15 2015 - File watcher saver plugin: TERMINATE

You can see that log record is being held once a minute (what we actually need), new file size is written.

Also we can notice that handler SIG_TERM was executed, signaling that plugin received the notification about server shut-down.

Notifier

Notifier informs system users about any events.

# FSize_watcher_saver.py
# Example plugin for monitoring file size and last modification date-time.
# Part 4. Notifier 

import time
import smtplib 

from lvestats.lib.commons import dateutil
from lvestats.core.plugin import LveStatsPlugin  


# Key name
COLLECTOR_KEY_FSIZE = 'FSizeWatcher_fsize'
COLLECTOR_KEY_FILENAME = 'FSizeWatcher_fname' 

# email message pattern
EMAIL_MESSAGE_PATTERN = """Hello, administrator!
Size of the file '%s' is %d bytes.
"""  


class FSize_watcher_notifier (LveStatsPlugin):
	# Default period
	DEFAULT_PERIOD_STR = '12h'
	# this plugin should be third in chainorder = 3
	# Timeout
	timeout = 20
	# Notifier Log filename
	log_file_name = '/var/log/FSize_watcher_notifier.log'
	# Email from address
	email_from = None
	# Email to address
	email_to = None
	# Email subject
	email_subject = None
	# Sets configuration to plugin
	def set_config(self, config):
		# Email settings
		self.email_from = config.get('notify_from_email', None)
		self.email_to = config.get('notify_to_email', None)
		self.email_subject = config.get('notify_from_subject', 'Message from FSize_watcher_notifier plugin')
		# Notify period
		s_period = config.get('notify_period', None)
		if s_period:
			self.period = dateutil.parse_period2(s_period)
		else:
			self.period = dateutil.parse_period2(FSize_watcher_notifier.DEFAULT_PERIOD_STR)
		f = open(self.log_file_name, 'a')
		f.write('%s - FSize_watcher_notifier plugin: configure\n' % time.asctime(time.localtime()))
		f.write('       - Period: %s\n' % self.period)
		f.write('       - From: %s\n' % self.email_from)
		f.write('       - To: %s\n' % self.email_to)
		f.write('       - Subject: \'%s\'\n' % self.email_subject)
		f.close() 
		
	# work method
	def execute(self, lve_data):
		if COLLECTOR_KEY_FSIZE not in lve_data or COLLECTOR_KEY_FILENAME not in lve_data:
			return
		if not self.email_from or not self.email_to:
			f = open(self.log_file_name, 'a')
			f.write('%s - FSize_watcher_notifier plugin error: email_from or email_to not set\n')
			f.close()
			return
		try:
			from email.mime.text import MIMEText
			# Send email
			msg = MIMEText(EMAIL_MESSAGE_PATTERN % (lve_data[COLLECTOR_KEY_FILENAME], lve_data[COLLECTOR_KEY_FSIZE]))
		msg['Subject'] = self.email_subject
		msg['From'] = self.email_from
		msg['To'] = self.email_to 
		
		s = smtplib.SMTP('localhost')
		s.sendmail(self.email_from, [self.email_to], msg.as_string())
			s.quit() 
			
		f = open(self.log_file_name, 'a')
			f.write('%s - FSize_watcher_notifier plugin: email message was successfully sent\n' % time.asctime(time.localtime()))
			f.close()
			except Exception as e:
			f = open(self.log_file_name, 'a')
			f.write('%s - FSize_watcher_notifier plugin error:\n%s\n' % (time.asctime(time.localtime()), str(e)))
			f.close()

Configuration file /etc/sysconfig/lvestats.config/FSize_watcher_notifier.cfg :

# Config file for FSize_watcher_notifier.py plugin
# Please define email options here 

NOTIFY_FROM_EMAIL=user@hostname
NOTIFY_FROM_SUBJECT=Message from FSize_watcher_notifier
NOTIFY_TO_EMAIL=admin@hostname
NOTIFY_PERIOD=12h

Plugin’s index number equals 3 ( order=3 ), that is why notifier starts after the rest. But since it uses only data formed by collector , then its order may equal any number bigger that collectors order (>0).

Notifier reads the necessary parameters from the configuration (email address, topic, period) and writes them into its own log as reference.

Plugin’s execute method checks the availability of all the necessary data (email parameters, collectors data) and sends the message. All the notifications are written into the notifier's own log.

If any data is missing, the message is not sent.

Log example:

Thu Feb  5 11:51:34 2015 - FSize_watcher_notifier plugin: configure
       - Period: 60.0
       - From: user@hostname
       - To: admin@hostname
       - Subject: 'Message from FSize_watcher_notifier'
Thu Feb  5 11:51:35 2015 - FSize_watcher_notifier plugin: email message was successfully sent
Thu Feb  5 11:52:35 2015 - FSize_watcher_notifier plugin: email message was successfully sent
Thu Feb  5 11:53:35 2015 - FSize_watcher_notifier plugin: email message was successfully sent
Thu Feb  5 11:54:35 2015 - FSize_watcher_notifier plugin: email message was successfully sent
Thu Feb  5 11:57:00 2015 - FSize_watcher_notifier plugin: configure
       - Period: 43200.0
       - From: user@hostname
       - To: admin@hostname
       - Subject: 'Message from FSize_watcher_notifier'
Thu Feb  5 11:57:00 2015 - FSize_watcher_notifier plugin: email message was successfully sent

File info and format for /var/lve/info file

This file is used by control panels to display to user their 'current' usage. The file is updated every 5 seconds by lve-stats.

When writing to this file we make sure that: average CPU/IOPS/MEM is never greater then LIMIT for that resource.

Example:

0,0,20,0,2500,0,262144,0,0,262144,0,0,100,0,0,0,0,1024,1024,0,0,0,0 600,1,20,2492,2500,70,262144,0,0,262144,33,0,100,1,0,0,0,1024,1024,0,5,0,0 200,0,20,0,2500,0,262144,0,0,262144,0,0,100,0,0,0,0,1024,1024,0,0,0,0 500,0,20,0,2500,0,262144,0,0,262144,0,0,100,0,0,0,0,1024,1024,0,0,0,0

First line of the file is ' default limits '.

Fields:

# 0 - id
# 1 - mep (average entry processes)
# 2 - lep  (limit ...)
# 3 - cpu_usage (average speed)
# 4 - lcpu (limit spped)
# 5 - mem_usage (average virtual memory)
# 6 - lmem (limit ...)
# 7 - mem_fault (number of virtual memory faults)
# 8 - mep_fault (number of entry processes faults)
LVE_VERSION >=6
# 9 - lmemphy (limit physical memory)
# 10 - memphy (average ...)
# 11 - memphy_fault (faults ...)
# 12 - lnproc (limit number of processes)
# 13 - nproc (average ...)
# 14 - nproc_fault (faults ...)
# 15 - lcpuw (CPU weight -- deprecated not used)
# 16 - io_usage (average IO usage)
# 17 - io_limit (limit ...)
LVE_VERSION >=8
#18 - liops  (limit IOPS)
#19 - iops (average IOPS)

Note

You can also use LVE-stats 2 CLI

Troubleshooting

lvestats service and utilities write fatal errors to system log.

There is /var/log/lve-stats.log file with additional information (warnings, tracebacks for errors).

To see which letters were sent via lves-tats notifier in the logs, do the following:

  1. In the /etc/sysconfig/lvestats2, specify logging_level=debug;
  2. Execute the service lvestats restart command.

CageFS

General information and requirements

CageFS is a virtualized file system and a set of tools to contain each user in its own 'cage'. Each customer will have its own fully functional CageFS, with all the system files, tools, etc.

The benefits of CageFS are:

  • Only safe binaries are available to user
  • User will not see any other users, and would have no way to detect presence of other users & their user names on the server
  • User will not be able to see server configuration files, such as Apache config files.
  • User's will have limited view of /proc file system, and will not be able to see other users' processes

At the same time, user's environment will be fully functional, and user should not feel in any way restricted. No adjustments to user's scripts are needed. CageFS will cage any scripts execution done via:

  • Apache (suexec, suPHP, mod_fcgid, mod_fastcgi)
  • LiteSpeed Web Server
  • Cron Jobs
  • SSH
  • Any other PAM enabled service

Note

mod_php is not supported, MPM ITK requires a custom patch

Note

CageFS is not supported for H-Sphere.

See also Compatibility Matrix.

Minimum Requirements:

  • kernel: CL6 with lve1.2.17.1 or later, CL7.
  • 7GB of disk space.

Depending on your setup, and number of users, you might also need:

  • Up to 8MB per customer in /var directory (to store custom /etc directory)
  • 5GB to 20GB in /usr/share directory (to store safe skeleton of a filesystem)

Warning

If at any time you decide to uninstall CageFS, please make sure you follow uninstall instructions

CageFS quirks

Due to the nature of CageFS, some options will not work as before or will require some changes:

  • lastlog will not work (/var/log/lastlog).
  • PHP will load php.ini from /usr/selector/php.ini. That file is actually a link to the real php.ini file from your system. So the same php.ini will be loaded in the end.
  • You have to run cagefsctl --update any time you have modified php.ini, or you want to get new/updated software inside CageFS.
  • CageFS installation changes jailshell to regular bash on cPanel - read why.

Installation and update

To install CageFS:

yum install cagefs
/usr/sbin/cagefsctl --init

That last command will create skeleton directory that might be around 7GB in size. If you don't have enough disk space in /usr/share, use following commands to have cagefs-skeleton being placed in a different location:

mkdir /home/cagefs-skeleton
ln -s /home/cagefs-skeleton /usr/share/cagefs-skeleton

The commands above should be executed before the cagefsctl --init.

Also, it is needed approximately 4Kb of disk space per one user for the /var/cagefs directory. You should place the /var/cagefs directory on partition, which is large enough and has disk quota enabled.

For example, to create the /var/cagefs directory on the /home partition, execute the following commands before the cagefsctl --init:

mkdir /home/cagefs
ln -s /home/cagefs /var/cagefs

IMPORTANT

Please make sure to turn on disk quota for a partition where the /var/cagefs directory is located, or move the /var/cagefs to a partition where disk quota is enabled. This is needed to prevent users from abusing disk quota inside CageFS.

If the /var/cagefs directory is already created, you can move it. How to move the /var/cagefs directory: https://docs.cloudlinux.com./#moving-var-cagefs-directory

IMPORTANT

If you are placing skeleton in /home directory on cPanel servers, you must configure the following option in cPanel WHM: WHM -> Server Configuration -> Basic cPanel/WHM Setup -> Basic Config -> Additional home directories
Change the value to blank (not default Home ). Without changing this option, cPanel will create new accounts in incorrect places.

CageFS will automatically detect and configure all necessary files for:

  • cPanel
  • Plesk
  • DirectAdmin
  • ISPmanager
  • Interworx
  • MySQL
  • PostgreSQL
  • LiteSpeed

Web interface to manage CageFS is available for cPanel, Plesk 10+, DirectAdmin, ISPmanager & Interworx. Command line tool would need to be used for other control panels.

Once you initialized the template you can start enabling users. By default CageFS is disabled for all users.

Starting from cagefs-6.1-27 fs.proc_can_see_other_uid will be migrated (one time) from /etc/sysctl.conf into /etc/sysctl.d/90-cloudlinux.conf . If this variable is not set in either file, it will default to 0.

It is strongly advised against setting this variable in 90-cloudlinux.conf. Define it in /etc/sysctl.conf or in some other config file with an index number greater than 90-cloudlinux.conf, e.g. /etc/sysctl.d/95-custom.conf.

You can find more information on fs.proc_can_see_other_uid automatic migration in Kernel Config Variables.

Note

You can also use CageFS CLI

Uninstalling

To uninstall CageFS, start by disabling and removing all directories:

/usr/sbin/cagefsctl --remove-all

That command will: disable CageFS for all customers, unmount CageFS for all users, removes /usr/share/cagefs-skeleton & /var/cagefs directories. It will not remove /etc/cagefs directory.

Remove CageFS RPM:

yum remove cagefs

Managing users

CageFS provides for two modes of operations:

  1. Enabled for all, except those that are disabled.
  2. Disabled for all, except those that are enabled.

Mode #1 is convenient for production operation, where you want all new users to automatically be added to CageFS. Mode #2 is convenient while you test CageFS, as it allows you to enable it on one by one for your customers.

To start using CageFS you have to select one of the mode of operations:

/usr/sbin/cagefsctl --enable-all
or
/usr/sbin/cagefsctl --disable-all
or
/usr/sbin/cagefsctl --toggle-mode
That will switch the operation mode, preserving current disabled/enabled users.

To enable individual user do:

/usr/sbin/cagefsctl --enable [username]
To disable individual user:
/usr/sbin/cagefsctl --disable [username]
To list all enabled users:
/usr/sbin/cagefsctl --list-enabled
To list all disabled users:
/usr/sbin/cagefsctl --list-disabled
To see current mode of operation:
/usr/sbin/cagefsctl --display-user-mode

Note

You can also use CageFS CLI

Configuration

File system templates

Warning

Please do not modify any existing files in the /etc/cagefs/conf.d directory because they may be overwritten while updating CageFS package. You should create a new file with a unique name instead.

CageFS creates a filesystem template in /usr/share/cagefs-skeleton directory. CageFS template will be mounted for each customer. The template is created by running:

/usr/sbin/cagefsctl --init

To update the template, you should run:

/usr/sbin/cagefsctl --update

The behavior of the commands (and the files copied into /usr/share/cagefs-skeleton directory) depends on the configuration files in /etc/cagefs/conf.d
You can add additional files, users, groups and devices into CageFS template by adding .cfg file, and running:

/usr/sbin/cagefsctl --update

To delete files from CageFS template, remove corresponding .cfg file, and run:

/usr/sbin/cagefsctl --update

Here is an example openssh-clients.cfg file:

[openssh-clients]

comment=OpenSSH Clients

paths=/etc/ssh/ssh_config, /bin/hostname, /usr/bin/scp, /usr/bin/sftp, /usr/bin/slogin, /usr/bin/ssh, /usr/bin/ssh-add, /usr/bin/ssh-agent, /usr/bin/ssh-copy-id, /usr/bin/.ssh.hmac, /usr/bin/ssh-keyscan, /usr/libexec/openssh/sftp-server, /etc/environment, /etc/security/pam_env.conf

devices=/dev/ptmx

Example mail.cfg file:

[mail]

comment=Mail tools

paths=/bin/mail, /etc/aliases.db, /etc/mail, /etc/mailcap, /etc/mail.rc, /etc/mime.types, /etc/pam.d/smtp.sendmail, /etc/rc.d/init.d/sendmail, /etc/smrsh, /etc/sysconfig/sendmail, /usr/bin/hoststat, /usr/bin/Mail, /usr/bin/mailq.sendmail, /usr/bin/makemap, /usr/bin/newaliases.sendmail, /usr/bin/purgestat, /usr/bin/rmail.sendmail, /usr/lib64/sasl2/Sendmail.conf, /usr/lib/mail.help, /usr/lib/mail.tildehelp, /usr/lib/sendmail.sendmail, /usr/sbin/mailstats, /usr/sbin/makemap, /usr/sbin/praliases, /usr/sbin/sendmail.sendmail, /usr/sbin/smrsh, /var/log/mail, /var/spool/clientmqueue, /var/spool/mqueue

users=smmsp

groups=smmsp

There is an easy way to add/delete files from particular RPMs into CageFS. That can be done by using --addrpm and --delrpm options in cagefsctl . Like:

cagefsctl --addrpm ffmpeg
cagefsctl --update

Note

ffmpeg RPM should be installed on the system already.

Excluding files

To exclude files and directories from CageFS, edit file:
/etc/cagefs/custom.black.list
And add files or directories in there, one per line.

Execute the following command to apply changes:

cagefsctl --force-update

Please do not edit /etc/cagefs/black.list file because it is replaced during the update of CageFS package.

Excluding users

To exclude users from CageFS, create a file (any name would work) inside /etc/cagefs/exclude folder, and list users that you would like to exclude from CageFS in that file (each user in separate line).

Then execute the following command to apply changes:

cagefsctl --user-status USER

And check that the command shows Disabled.

Mount points

Note

Please do not mount the entire /var directory into CageFS (i.e. do not add the /var to the cagefs.mp). This will break CageFS.

CageFS creates individual namespace for each user, making it impossible for users to see each other's files and creating high level of isolation. The way namespace is organized:

  1. /usr/share/cagefs-skeleton with safe files is created
  2. Any directory from the server that needs to be shared across all users is mounted into /usr/share/cagefs-skeleton (a list of such directories is defined in /etc/cagefs/cagefs.mp)
  3. /var/cagefs/[prefix]/username directory for each user. Prefix is defined as last two digits of user id. User id is taken from /etc/passwd file.
  4. Separate /etc directory is created and populated for each user inside /var/cagefs/[prefix]/username
  5. /tmp directory is mounted for each user separately into ~username/.cagefs/tmp directory
  6. Additional custom directories can be mounted for each user by defining them in /etc/cagefs/cagefs.mp
  7. You can define custom directories per user using virt.mp files [CageFS 5.1 and higher]

To define individual custom directories in /etc/cagefs/cagefs.mp following format is used:

@/full/path/to/directory,permission notation

This is useful when you need to give each user its own copy of a particular system directory, like:

@/var/run/screen,777

Such entry would create separate /var/run/screen for each user, with permissions set to 777

To modify mount points, edit /etc/cagefs/cagefs.mp. Here is an example of cagefs.mp:

/var/lib/mysql
/var/lib/dav
/var/www/cgi-bin
/var/spool
/dev/pts
/usr/local/apache/domlogs
/proc
/opt
@/var/spool/cron,700
@/var/run/screen,777

If you want to change mount points, make sure you re-initialize mount points for all customers:

cagefsctl --remount-all
This command will kill all current processes and reset mount points.

Per user virtual mount points

[CageFS 5.1 and higher]

  • Please, see Split by username feature, as it might be simpler to implement in some cases.

Starting with CageFS 5.1 you can specify additional directories to be mounted inside user's CageFS. This can be specified for each user. To specify virtual mount points for a user, create a file:

/var/cagefs/[prefix]/[user]/virt.mp

Inside that file, you can specify mount points in the following format:

virtdir1,mask
@subdir1,mask
@subdir2,mask
virdir2,mask
@subdir3,mask
@subdir4,mask
>virtdir3,mask
@subdir5,mask
@subdir6,mask
# comments
  • mask is always optional, if missing 0755 is used
  • Create virtual directory subdir/virtdir , mount it to:
    • skeleton jaildir/virtdir
    • inside virtual directory, create directories subdir1, subdir2
    • mount virtdir1/subdir1 to subdir/virtdir/subdir1
    • if virtdir is started with >, create directory subdir/virtdir , but don't mount it into jaildir . This is needed for cases when virtdir is inside home base dir.
  • if file /var/cagefs/[prefix]/[user]/virt.mp is missing -- no virt directories are loaded for that user.

Note that CageFS will automatically create those files for Plesk 10 & higher.

For example if we have Plesk 11.5 with two users cltest1, and cltest2:

cltest1 uid 10000 has domains: cltest1.com, cltest1-addon.com and sub1.cltest1.com
cltest2 uid 10001 has domains: cltest2.com, cltest2-addon.com

In such case we would have file /var/cagefs/00/cltest1/virt.mp :

>/var/www/vhosts/system,0755
@cltest1-addon.com,0755
@cltest1.com,0755
@sub1.cltest1.com,0755

and file: /var/cagefs/01/cltest2/virt.mp:

>/var/www/vhosts/system
@cltest2-addon.com
@cltest2.com

Split by username

[CageFS 5.3.1+]

Sometimes you might need to make sure that directory containing all users would show up as containing just that user inside CageFS. For example, if you have directory structure like:

/home/httpd/fcgi-bin/user1
/home/httpd/fcgi-bin/user2

Then we can add the following line to /etc/cagefs/cagefs.mp file:

%/home/httpd/fcgi-bin

and execute:

cagefsctl --remount-all

After that each subdirectory of /home/httpd/fcgi-bin will be mounted for appropriate user in CageFS: /home/httpd/fcgi-bin/user1 will be mounted for user1 and /home/httpd/fcgi-bin/user2 will be mounted for user2 .

Splitted by user’s UID mounts

Note

Requires CageFS v.7.4.2-1 and later

In addition to splitted by username mounts, there is an ability to “split” mounts by user’s UID. This feature is useful for systems with non-unique UIDs, i.e. when multiple users have the same UID.

Using splitted by UID mounts is more preferable over splitted by username mounts, because you can mount the same directory for all users with the same UID. Also, splitted by UID mounts work fine with renaming of the users (when username changes but UID remains the same).

For example if you have a directory structure like the following:

/data/uids/1000/data.db
/data/uids/1001/data.db

Then you can add the following line to the /etc/cagefs/cagefs.mp file:

*/data/uids
and execute
cagefsctl --remount-all

After that each subdirectory of the /data/uids will be mounted for the appropriate user in CageFS. The /data/uids/1000 directory will be mounted for the user with UID 1000 and the /data/uids/1001 directory will be mounted for the user with UID 1001.

Mounting users home directory inside CageFS

CageFS 6.1-1 (and later) has improved mounting user’s home directory that is applied for users with home directories like /home/user or /homeN/user (where N = 0,1,..9).

In such case, earlier versions of CageFS always mount user’s home directory to /home/user and create symlink /homeN -> /home when needed, so user’s home directory can be accessed both via /home/user and /homeN/user . This quirk leads to some rare incompatibilities between CageFS and other software (for example OpenCart), because real path of user’s home directory in CageFS and in real file system can differ.

New CageFS mounts user’s home directory in a way that its real path in CageFS is always the same as in real file system. Additionally, CageFS searches for symlinks like /homeX -> /homeY and /homeX/user -> /homeY/user in real system and creates such symlinks in user’s CageFS when found.

This new mounting mode is enabled by default. You can switch to old mounting mode by executing the following commands:

touch /etc/cagefs/disable.home.dirs.search
cagefsctl --force-update
cagefsctl --remount-all

Note

New mounting mode will be disabled automatically when "mounting base home directory" mode is enabled (mount_basedir=1 setting in /etc/cagefs/cagefs.base.home.dirs file).

How to hide directory inside mount point

Note

For files outside a mount point, use blacklisting, see: Excluding files

To hide directories inside a mount point, create a file in the /etc/cagefs/empty.dirs directory (you can use any name) with a list of directories to be hidden (these directories will look like empty for users in CageFS).

Note

Please do not edit the supplied config file (/etc/cagefs/empty.dirs/emptied_dirs.default).

Example

Let’s take a /var/www directory which contains the following folders: icons and html. To hide the /var/www directory content from users inside CageFS, we will do the following:

  • Create the custom.empty file with a single record: /var/www
  • Place the custom.empty file to the /etc/cagefs/empty.dirs directory
  • Run the cagefsctl --remount-all command

Now, all users inside CageFS will see the /var/www directory as an empty directory even if there is /var/www/html in the /etc/cagefs/cagefs.mp.

Base home directory

If you have a custom setup where home directories are in a special format, like: /home/$USERNAME/data , you can specify it using regular expressions. This is needed by CageFS to create safe home space for end user, where no other users are visible.

We will create empty: /var/cagefs/[prefix]/$USERNAME/home , and then mount /home/$USERNAME in that directory

To do that, create a file: /etc/cagefs/cagefs.base.home.dirs

With content like:

^/home/
^/var/www/users/

If there is no such file, the home directory without last component will be considered as a base dir, like with /home/$USERNAME we would create /var/cagefs/[prefix]/$USERNAME/home , and then mount /home/$USERNAME in there

With /home/$USERNAME/data as a home dir, we would assume that /home/$USERNAME is the base directory, and we would create /var/cagefs/[prefix]/$USERNAME/home/$USERNAME/data and then we would mount /home/$USERNAME/data -- which would cause each user to see empty base directories for other users, exposing user names.

Sharing home directory structure among users

When you want to share directory structure among multiple users, you can add following line at the top of the /etc/cagefs/cagefs.base.home.dirs file. This is useful on the systems that support sites with multiple users, with different home directories inside the main 'site' directory.

mount_basedir=1

For example:

user1 has home directory /var/www/vhosts/sitename.com/web_users/user1 user2 has home directory /var/www/vhosts/sitename.com/web_users/user2 site admin has home directory /var/www/vhosts/sitename.com

So, content of /etc/cagefs/cagefs.base.home.dirs should be the following:

mount_basedir=1
^/var/www/vhosts/[^/]+

Directory structure in /var/www/vhosts/sitename.com will be mounted in CageFS for appropriate users.
Each user will have access to whole directory structure in /var/www/vhosts/sitename.com (according to their permissions).

Note

You should execute cagefsctl --remount-all in order to apply changes to CageFS (i.e. remount home directories).

PostgreSQL support

CloudLinux OS Shared 7:

CageFS works with any PostgreSQL version installed from CloudLinux OS Shared or CentOS repositories. PostgreSQL packages for CloudLinux OS Shared 7 come from the upstream (CentOS) unmodified. PostgreSQL’s socket is located in /var/run/postgresql directory. This directory is mounted to CageFS by default (in cagefs-5.5-6.34 or later).

When PostgreSQL has been installed after CageFS install, please add line:

/var/run/postgresql

/etc/cagefs/cagefs.mp file and then execute:

cagefsctl --remount-all 

The steps above are enough to configure CageFS to work with PostgreSQL.

CloudLinux OS Shared 6:

CageFS provides separate /tmp directory for each end user. Yet, PostgreSQL keeps its Unix domain socket inside server's main /tmp directory. In addition to that, the location is hard coded inside PostgreSQL libraries.

To resolve the issue, CloudLinux OS Shared provides a version of PostgreSQL with modified start up script that can store PostgreSQL's socket in /var/run/postgres. The script automatically creates link from /tmp to that socket to prevent PostgreSQL dependent applications from breaking.

In addition to that, CageFS knows how to correctly link this socket inside end user's /tmp directory.

To enable PostgreSQL support in CageFS:

  1. Make sure you have updated to latest version of PostgreSQL.

  2. Edit file /etc/sysconfig/postgres, and uncomment SOCK_DIR line.

  3. Update CageFS configuration by running:

cagefsctl  --reconfigure-cagefs
  1. Restart PostgreSQL by running:
service postgresql restart 

If you are using cPanel, you would also need to modify file: /etc/cron.daily/tmpwatch

And update line:

flags=-umc 

to:

flags=-umcl

to prevent symlink from being removed.

PAM configuration

CageFS depends on pam_lve module for PAM enabled services. When installed, the module is automatically installed for following services:

  • sshd
  • crond
  • su

The following line is added to corresponding file in /etc/pam.d/:

``` session required pam_lve.so 100 1 ```

Where 100 stands for minimum UID to put into CageFS & LVE , and 1 stands for CageFS enabled.

Filtering options for commands executed by proxyexec

You can disallow a user in CageFS to execute specific commands with some specific dangerous options via proxyexec.

To do so, you should create <command>.json file in the /etc/cagefs/filters directory and specify the names of options you want to disable.

For example, to disable some options of sendmail command, /etc/cagefs/filters/sendmail.json is created with the following content:

{
  "default": {
    "deny": [
      "-be",
      "-bem"
    ],
    "restrict_path": [
      "-C",
      "-D"
    ]
  },
  "/usr/sbin/sendmail": {
    "deny": [
      "-be",
      "-bem"
    ],
    "restrict_path": [
      "-C",
      "-D"
    ]
  },
  "/var/qmail/bin/sendmail": {
    "deny": [
      "-be",
      "-bem"
    ],
    "restrict_path": [
      "-C",
      "-D"
    ]
  },
  "/usr/sbin/sendmail.sendmail": {
    "deny": [
      "-be",
      "-bem"
    ],
    "restrict_path": [
      "-C",
      "-D"
    ]
  },
  "/usr/local/cpanel/bin/sendmail": {
    "deny": [
      "-be",
      "-bem"
    ],
    "restrict_path": [
      "-C",
      "-D"
    ]
  }
}

You can specify options for different paths separately (for example, /usr/sbin/sendmail or /var/qmail/bin/sendmail).

If the path to the program being executed does not match any path specified in the config file, then default parameters are used.

  • deny list should contain options that should be disallowed for use by users (the black list of options, all other options will be allowed).
  • You can specify the white list of options in the allow list (all other options will be denied).
  • You cannot specify both white and black list (allow and deny).

It is possible to verify that a path specified as a parameter for an option does not refer outside of the user’s home directory. This check is performed for options specified in the restrict_path list. All issues are reported in /var/log/secure log file.

NOTE: By default, option filters only compare the starting parts of arguments with entries specified by lists. For instance, if option "-f" is forbidden, "-f /etc/list" will be forbidden, but "-vf /etc/list" will not.

Starting from cagefs v. #7.4.12-1, specifying a "strict_options": true switch inside a filter file entry enables an extended parsing mechanism where each short option from a cluster is parsed separately.

It is recommended to enable this option, unless it causes issues with the restricted command's functionality.

{
  "default": {
    "deny": [
      "-be",
      "-bem"
    ],
    "restrict_path": [
      "-C",
      "-D"
    ],
    "strict_options": true
  }
}

Executing by proxy

Some software has to run outside CageFS to be able to complete its job. It includes such programs as passwd, sendmail, etc. CageFS provides proxyexec technology to accomplish this goal: you can define any program to run outside CageFS by specifying it in any file located in the /etc/cagefs/ which ends with .proxy.commands. In the examples below we use custom.proxy.commands, but you can use any other name, e.g. mysuperfile.proxy.commands.

Warning

Do not edit the existing /etc/cagefs/proxy.commands file as it will be overwritten with the next CageFS update.

Also you should create a config file in /etc/cagefs/conf.d/ and define there a path to the binary or to one of it's parent directories.

Warning

Binary shouldn't be located in one of the directories specified in /etc/cagefs/cagefs.mp. Binaries from directories in /etc/cagefs/cagefs.mp are always executed inside cagefs.

The syntax of the /etc/cagefs/[*.]proxy.commands file is as follows:

ALIAS[:wrapper_name]=[username:]path_to_executable
ParameterDescription
ALIASAny name unique within all /etc/cagefs/\[*.\]proxy.commands files. Used as an identifier.
wrapper_nameOptional field. The name of the wrapper file which is used as a replacement for an executable file (set by path_to_executable) inside CageFS.

Possible values:
- a name of a wrapper you place into the /usr/share/cagefs/safeprograms directory;
- noproceed - a reserved word which means that the wrapper is not needed, e.g. when it is already installed by other ALIAS. Often used for the commands with several ALIAS as in the example below.
- omit this field - default wrapper cagefs.proxy.program will be used.

Used in cases when you'd like to give access only to the part of binary functions but it is not possible to do that using options filtering.
Note: wrapper works inside CageFS with user rights and executes “real” scripts using proxy daemon.
path_to_executableA path to an executable file that will run via proxyexec.
usernameOptional field. A name of a user on whose behalf path_to_executable will run in the real system. If username is not specified, then path_to_executable will run on behalf of the same user who is inside CageFS.

Once the program is defined, run this command to populate the skeleton:

cagefsctl --force-update

Users with duplicate UIDs

Sometimes hosters may have users with non-unique UIDs. Thus, proxyexec may traverse users' directories to find a specific one. That behavior turns into inappropriate if the user's directory is not cached locally (for example LDAP is used).

To turn this feature off, run:

touch /etc/cagefs/proxy.disable.duid

Or to activate it back, run:

rm /etc/cagefs/proxy.disable.duid

Examples

Let's have a script that must do some stuff outside CageFS and return a result to a user. Let's name it superbinary and place it into the /my/scripts/ directory.

In the examples below, we will use a small script that:

  • checks if it works inside or outside of CageFS
  • prints a number of users in the /etc/passwd file

We use the /etc/passwd file because it is truncated inside the cage by default and we can easily see the difference between CageFS and the “real” system by just counting lines in it.

cat /opt/scripts/superbinary  
............ 

#!/usr/bin/env bash
if [[ -e /var/cagefs ]]; then
  echo "I am running without CageFS"
else
  echo "I am running in CageFS"
fi;
echo "I am running as: `whoami`"
echo "Number or records in /etc/passwd: `cat /etc/passwd | wc -l`"

First, let’s check that CageFS works: create a user and disable the cage:

useradd test
cagefsctl --disable test

Then, run the following command as root and you will see the following output:

[root ~]# su - test -c "/my/scripts/superbinary"
I am running without CageFS
I am running as: test
Number or records in /etc/passwd: 49

Now, enable CageFS for the test user and run the command again:

[root ~]# cagefsctl --enable test
[root ~]# su - test -c "/my/scripts/superbinary"
-bash: /my/scripts/superbinary: No such file or directory

As you can see the access to the file is restricted by CageFS.

Example 1. Make users in CageFS be able to execute a script which must work outside CageFS

Add the following line into the /etc/cagefs/custom.proxy.commands:

MYSUPERBINARY=/my/scripts/superbinary

Then run the cagefsctl --force-update, which will place a special wrapper instead of your script inside CageFS. And run your script again:

[root ~]# su - test -c "/my/scripts/superbinary"
I am running without CageFS
I am running as: test
Number or records in /etc/passwd: 49

To compare, let’s count a number of users in the /etc/passwd directly:

[root ~]# su - test -c "cat /etc/passwd | wc -l"
25

Result: the script escapes from CageFS and has access to all files which a user with disabled CageFS has.

Example 2. Permissions escalation

Let's imagine that you need to give the users the ability to run a script which gets information about their domains from the apache.conf. To do that, you need root permissions. You can achieve that with proxyexec.

First, run the following:

echo "MYSUPERBINARY=root:/my/scripts/superbinary" > /etc/cagefs/custom.proxy.commands

And then, run the example script again:

[root ~]# su - test -c "/my/scripts/superbinary"
I am running without cagefs
I am running as: root
Number or records in /etc/passwd: 49

As you can see, the script now works with root permissions, as set in the custom.proxy.commands file. In order to get information about a user who runs the script, use the following environment variables:

PROXYEXEC_UID
PROXYEXEC_GID

Example:

[root ~]# id test
uid=1226(test) gid=1227(test) groups=1227(test)
[root ~]# su - test -c "/my/scripts/superbinary"                                                
I am running without CageFS                                                                          
I am running as: root
Number or records in /etc/passwd: 49
PROXYEXEC_UID=1226
PROXYEXEC_GID=1227

Result: users can run the script that gains the root permissions and work outside CageFS. Of course, you can set any other user instead of root in the custom.proxy.commands.

Example 3. Custom proxyexec wrapper

Let’s modify the test binary in a next way:


[root ~]# cat /my/scripts/superbinary
#!/usr/bin/env bash
FILE="$1"
if [[ -e /var/cagefs ]]; then
  echo "I am running without CageFS"
else
  echo "I am running in CageFS"
fi;
echo "I am running as: `whoami`"
echo "Number or records in ${FILE}: `cat ${FILE} | wc -l`"
echo "PROXYEXEC_UID=${PROXYEXEC_UID}"
echo "PROXYEXEC_GID=${PROXYEXEC_GID}"

Now users can pass any path to the file as an argument. In order to restrict possible parameters (file paths) that users can pass, you can use the custom proxyexec wrapper.

First, duplicate the default wrapper and give it a name, e.g. cagefs.proxy.mysuperbinary.

[root ~]# cp /usr/share/cagefs/safeprograms/cagefs.proxy.program /usr/share/cagefs/safeprograms/cagefs.proxy.mysuperbinary

The default wrapper already contains a check that does not allow to run it by the root user:

#!/bin/bash
##CageFS proxyexec wrapper - ver 15
    
if [[ $EUID -eq 0 ]]; then
    echo 'Cannot be run as root'
    exit 1
fi
...

Add the new check below:

if [[ $1 == "/etc/passwd" ]]; then                                                    
    echo "it is not allowed for user to view this file!"                  
    exit 1      
fi

Now, set a custom binary name in the custom.proxy.commands:

[root ~]# cat /etc/cagefs/custom.proxy.commands
MYSUPERBINARY:cagefs.proxy.mysuperbinary=root:/my/scripts/superbinary

Run skeleton update and check that everything works as expected:

[root ~]# cagefsctl --force-update
[root ~]# su - test -c "/my/scripts/superbinary /etc/passwd"
it is not allowed for user to view this file!
[root ~]# su - test -c "/my/scripts/superbinary /etc/group"
I am running without CageFS
I am running as: root
Number or records in /etc/group: 76
PROXYEXEC_UID=1226
PROXYEXEC_GID=1227

Notes and warnings

  1. Make sure that a directory with your script is not listed in the /etc/cagefs/cagefs.mp (is not mounted inside the cage). Otherwise, the proxyexec will not work because CageFS will not be able to replace your script with a special wrapper inside the cage.
  2. Use this feature with caution because it gives users the ability to execute specified commands outside CageFS. SUID commands are extremely dangerous because they are executed not as a user, but as an owner of the file (typically root). You should give users the ability to execute safe commands only. These commands should not have known vulnerabilities. You should check that users cannot use these commands to get sensitive information on a server. You can disable specific dangerous options of programs executed via proxyexec using filtering options.
  3. In cPanel, all the scripts located in the /usr/local/cpanel/cgi-sys/, that user might need to execute, should be added to the custom *.proxy.commands file.

Custom /etc files per customer

[4.0-5 and later]

To create a custom file in /etc directory for end user, create a directory:
/etc/cagefs/custom.etc/[username]

Put all custom files, and sub-directories into that direcotry.

For example, if you want to create custom /etc/hosts file for USER1 , create a directory:
/etc/cagefs/custom.etc/USER1

Inside that directory, create a hosts file, with the content for that user.

After that execute:

cagefsctl --update-etc USER1

If you are making changes for multiple users, you can run:

cagefsctl --update-etc

To remove a custom file, remove it from /etc/cagefs/custom.etc/[USER] directory, and re-run:

cagefsctl --update-etc

Moving cagefs-skeleton directory

Sometimes you might need to move cagefs-skeleton from /usr/share to another partition.

There are two ways:

  1. If /usr/share/cagefs-skeleton is not created yet ( cagefsctl --init wasn't executed), then execute:
mkdir /home/cagefs-skeleton 
ln -s /home/cagefs-skeleton /usr/share/cagefs-skeleton 
cagefsctl --init
  1. If /usr/share/cagefs-skeleton already exists, see this article

Moving /var/cagefs directory

To move /var/cagefs to another location:

cagefsctl --disable-cagefs
cagefsctl --unmount-all

Verify that /var/cagefs.bak directory does not exist (if it exists - change name "cagefs.bak" to something else)

cp -rp /var/cagefs /new/path/cagefs
mv /var/cagefs /var/cagefs.bak
ln -s /new/path/cagefs /var/cagefs
cagefsctl --enable-cagefs
cagefsctl --remount-all

Verify that the following command gives empty output:

cat /proc/mounts | grep cagefs.bak

Then you can safely remove /var/cagefs.bak:

rm -rf /var/cagefs.bak

TMP directories

CageFS makes sure that each user has his own /tmp directory, and that directory is the part of end-user's quota.

The actual location of the directory is $USER_HOME/.cagefs/tmp

Once a day, using cron job, CageFS will clean up user's /tmp directory from all the files that haven't been accessed during 30 days.

This can be changed by running:

cagefsctl --set-tmpwatch='/usr/sbin/tmpwatch -umclq 720'

Where 720 is the number of hours that the file had to be inaccessible to be removed.

By default this is done at 03:37 AM, but you can also force the clean up outdated files that match 'chosen period' of all user's /tmp directories without waiting for a job to be launched by cronjob . Just run:

cagefsctl --tmpwatch

The following paths will be cleaned as well:

  • /var/cache/php-eaccelerator (actual location $USER_HOME/.cagefs/var/cache/php-eaccelerator)
  • /opt/alt/phpNN/var/lib/php/session (actual location $USER_HOME/.cagefs/opt/alt/phpNN/var/lib/php/session), where NN corresponds to Alt-PHP version.

You can configure tmpwatch to clean custom directories inside CageFS.

Create /etc/cagefs/cagefs.ini configuration file and specify tmpwatch_dirs directive as follows:

tmpwatch_dirs=/dir1,/dir2

After that directories /dir1 and /dir2 inside CageFS  will be cleaned automatically.

Note that actual location of those directories in real file system is $USER_HOME/.cagefs/dir1 and $USER_HOME/.cagefs/dir2.

Cleanup PHP sessions in cPanel

For cPanel servers, CageFS version 6.0-42 or higher performs cleaning of PHP sessions based on session.gc_maxlifetime and session.save_path directives specified in proper php.ini files.

session.gc_maxlifetime directive default value is 1440 seconds. Those session files will be deleted, that were created or had metadata (ctime) changes more time ago than it is specified in session.gc_maxlifetime.

For Alt-PHP versions session.save_path value is normally /tmp.

Note

For new installations of Alt-PHP packages, session.save_path will be changed from /tmp to /opt/alt/phpNN/var/lib/php/session, where NN corresponds to Alt-PHP version.

This applies to the following Alt-PHP versions (or later):

  • alt-php44-4.4.9-71;
  • alt-php51-5.1.6-81;
  • alt-php52-5.2.17-107;
  • alt-php53-5.3.29-59;
  • alt-php54-5.4.45-42;
  • alt-php55-5.5.38-24;
  • alt-php56-5.6.31-7;
  • alt-php70-7.0.23-5;
  • alt-php71-7.1.9-5;
  • alt-php72-7.2.0-0.rc.2.2.

When using EasyApache 3, session.save_path value is normally /var/cpanel/php/sessions/ea3 or /tmp . Settings for EasyApache 3 are usualy taken from the file /usr/local/lib/php.ini .

When using EasyApache 4, session.save_path value is normally /var/cpanel/php/sessions/ea-phpXX , where XX corresponds to PHP version.

Cleaning is started by cron /etc/cron.d/cpanel_php_sessions_cron , which starts the script /usr/share/cagefs/clean_user_php_sessions twice within one hour.

Note

Script clean_user_php_sessions deletes only files which names match mask sess_[a-z0-9]*. If php was configured to use non-standart session file prefix in some custom save_handler, these session files wouldn't be managed by clean_user_php_sessions.

The settings for ea-php are located in /opt/cpanel/ea-phpXX/root/etc/php.d/local.ini or in /opt/cpanel/ea-phpXX/root/etc/php.ini , where XX corresponds to the PHP version.

The settings for alt-php are located in /opt/alt/phpXX/etc/php.ini files, where XX corresponds to PHP version.

The cleaning script cleans php sessions for all PHP versions ( ea-php and alt-php ) regardless of whether a version is used or selected via MultiPHP Manager or PHP Selector . When different session.gc_maxlifetime values are specified for the same session.save_path (for different php versions), the cleaning script will use the least value for cleaning session.save_path . So, it is recommended to specify different session.save_path for each PHP version.

Users can define custom value of session.gc_maxlifetime via PHP Selector in order to configure PHP's garbage collector, but that will not affect the script for cleaning PHP sessions. The script cleans PHP sessions based on global values of session.gc_maxlifetime and session.save_path directives taken from files mentioned above. Settings in custom users’ php.ini files are ignored.

Cleanup PHP session files in Plesk

For Plesk servers, CageFS version 6.0-52 or higher is provided with a special cron job for removing obsolete PHP session files. Cleanup script runs once an hour (similar to how it is done in Plesk).

Each time the script runs, it performs the cleanup of the paths:

  1. set by session.save_path directive in /opt/alt/phpXX/etc/php.ini files. If session.save_path is missing, then /tmp is used. Session files lifetime is set by session.gc_maxlifetime directive. If it is not found, then 1440 seconds value is used (24 minutes, as in Plesk). Lifetime set in the file is only taken into consideration if it is longer than 1440 seconds, otherwise 1440 seconds is used. All the installed Alt-PHP versions are processed.

  2. /var/lib/php/session . Files lifetime is only defined by Plesk script /usr/lib64/plesk-9.0/maxlifetime . If the script is missing or returns errors, then this directory is not processed.

The following features are applied during the cleanup:

  • all the users with UID higher than specified in /etc/login.defs are processed. Each user is processed independently from one another.
  • only directories inside CageFS are being cleaned. The paths of the same name in the physical  file system are not processed.
  • in all the detected directories, all the files with the names that correspond to sess_ search mask are removed, the rest of the files are ignored.
  • the files older than specified lifetime are removed.
  • all non-fatal errors (lack of rights, missing directory) are ignored and do not affect the further work of the script.

Disable PHP sessions cleanup on cPanel and Plesk

Here is a possible workaround for PHP session expiration problem (session lives longer than it is possible in a control panel). To use your own custom PHP sessions cleanup scripts - you can turn off built-in sessions cleanup implementation in the following way: add clean_user_php_sessions=false line to /etc/sysconfig/cloudlinux .

Syslog

By default, /dev/log should be available inside end user's CageFS . This is needed so that user's cronjobs and other things that user dev/log would get recorded in the system log files.

This is controlled using file /etc/rsyslog.d/schroot.conf with the following content:

$AddUnixListenSocket /usr/share/cagefs-skeleton/dev/log

To remove presence of dev/log inside CageFS, remove that file, and restart rsyslog service.

Excluding mount points

How to exclude mounts from namespaces for all LVEs

By default, all mounts from the real file system is inherited by namespaces of all LVEs . So, destroying all LVEs may be required in order to unmount some mount in real file system completely. Otherwise, mount point remains busy after unmounting it in the real file system because this mount exists in namespaces of LVEs .

lvectl start command saves all mounts from real file system as “default namespace” for later use in all LVEs . lve_namespaces service executes lvectl start command during startup.

In lve-utils-2.0-26 (and later) there is an ability to exclude specific mounts from namespaces for all LVEs . In order to do so, please create a file /etc/container/exclude_mounts.conf with list of mounts to exclude (one mount per line) as regular expressions, and then execute lvectl start :

cat /etc/container/exclude_mounts.conf     
............ 

^/dir1/
^/dir2$ 
lvectl start

After that, all new created LVEs will be without /dir2 mount and without mounts that start with /dir1/ (like /dir1/x , /dir1/x/y , etc). To apply changes to existing LVEs you should recreate LVEs :

lvectl destroy all   
lvectl apply all

Note

You should recreate all LVEs only once after creating /etc/container/exclude_mounts.conf file. After that the configuration changes will be applied to all new LVEs automatically.

Shared memory (/dev/shm) isolation in CageFS

Note

Requires cagefs-6.2.1-1 or later

The /dev/shm in a real file system directory is “world-writable”. This directory from the real file system is mounted to CageFS by default, so /dev/shm directory is common for all users by default. However, it is possible to improve security and isolate /dev/shm (shared memory) for each user in CageFS.

To enable /dev/shm isolation, do the following steps:

  1. Delete /dev/shm line from the /etc/cagefs/cagefs.mp file
sed -i -e '/^\/dev\/shm/d' /etc/cagefs/cagefs.mp
  1. Create a configuration file with mount options for shared memory
echo 'mode=0777' > /etc/cagefs/dev.shm.options
  1. Remount CageFS to apply changes
cagefsctl --remount-all

You can also specify additional mount options.

For example, you can specify the size of shared memory in megabytes:

echo 'mode=0777,size=1m' > /etc/cagefs/dev.shm.options
cagefsctl --remount-all

Or you can specify the size of user’s physical memory limit (PMEM) in percentage:

echo 'mode=0777,size=50%' > /etc/cagefs/dev.shm.options
cagefsctl --remount-all

To disable /dev/shm isolation, do the following steps:

  1. Delete configuration file
rm -f /etc/cagefs/dev.shm.options
  1. Validate /etc/cagefs/cagefs.mp file
cagefsctl --check-mp
  1. Add /dev/shm line to /etc/cagefs/cagefs.mp file
echo '/dev/shm' >> /etc/cagefs/cagefs.mp
  1. Remount CageFS to apply changes
cagefsctl --remount-all

Note

you should specify mode=0777. It is required for the proper operation of shared memory inside CageFS. This is not a security issue because the /dev/shm directory is isolated for each user and visible inside a user’s CageFS only.

Note

/dev/shm is mounted with nosuid,nodev,noexec mount options always (both in “isolated /dev/shm” mode and not). You cannot change this behavior.

Note

“isolated /dev/shm” mode will become the default in the future CageFS releases.

Note

when the size of the /dev/shm is specified in percentage of user’s physical memory limit (PMEM), you should remount CageFS after changing PMEM limit in order to change the size of shared memory allocated for the /dev/shm in user’s CageFS. To do so, execute cagefsctl --remount-all

Preventing process from entering CageFS on error

Starting from CageFS v.7.2.0-1, you can prevent a process from work if it can't enter to CageFS. The option is disabled by default.

To enable it, run the following commands:

touch /etc/cagefs/fail.on.error
cagefsctl --remount-all (cagefsctl --remount <user>)

To disable it, run the following commands:

rm -f /etc/cagefs/fail.on.error
cagefsctl --remount-all (cagefsctl --remount <user>)

The message “Act like CageFS is disabled” in the /var/log/messages will be displayed regardless the file /etc/cagefs/fail.on.error is available or not.

Integration with control panels

CageFS comes with a plugin for various control panels.

The plugin allows to:

  • Initialize CageFS;
  • Select mode of operation;
  • See and modify the list of enabled/disabled users;
  • Update CageFS skeleton.

cPanel

CageFS Plugin

CageFS plugin for cPanel is located in Plugins section of WHM and called CageFS User Manager .

Note

Take a note that the configuration of CageFS for users is located at the Users tab in the main menu of LVE Manager and the global CageFS settings (Toggle, update skeleton and others) are located at the Options tab in the main menu of LVE Manager inside the section "CageFS".

It allows to initialize CageFS, select users CageFS will be enabled for, as well as update CageFS skeleton.

To enable CageFS for a proper user (users), in CageFS User Manager choose a user from the list on the right ( Disabled users) and click Toggle . The user will move to the list on the left ( Enabled users).

To disable a user (users), choose a user from the list on the left ( Enabled users) and click Disable CageFS . The user will move to the list on the right ( Disabled users).

To update CageFS skeleton, click Update CageFS Skeleton .

CageFS inbuilt in Cloudlinux Manager

To enable or disable CageFS for a proper user (users), in Cloudlinux Manager , go to the Users tab and use the Toggle next to the chosen user(s) from the list under the CageFS column.

To update CageFS skeleton, go to Cloudlinux Manager > Options > CageFS and click on the Update button next to CageFS Skeleton:

Plesk

CageFS is an option inbuilt in Cloudlinux Manager that allows initializing and updating CageFS template, as well as managing users and mode of operation for CageFS.

To enable or disable CageFS for a proper user (users), in Cloudlinux Manager , go to the Users tab and use the Toggle next to the chosen user(s) from the list under the CageFS column.

To update CageFS skeleton, go to Cloudlinux Manager > Options > CageFS and click on the Update button next to CageFS Skeleton:

ISPManager

CageFS comes with plugin for ISP Manager to enable/disable CageFS on per user base. In edit user section chose Permission tab. Mark CageFS User Mode checkbox and click OK to apply.

Or you can manage global CageFS settings via CageFS menu

See also CageFS CLI tools.

MySQL Governor

General information and requirements

Warning

The "All" mode will be deprecated starting from September 1, 2021. You can read more here.

MySQL Governor is software to monitor and restrict MySQL usage in shared hosting environment. The monitoring is done via resource usage statistics per each MySQL thread.

MySQL Governor has two active modes of operations:

  • off - In this mode MySQL Governor will not throttle customer's queries, instead it will let you monitor the MySQL usage.
  • abusers - In this mode, once user goes over the limits specified in the MySQL Governor, all customer's queries will execute inside that user's LVE.

More details of the governor operation modes are described in the Modes of operation section

MySQL Governor allows to restrict customers who use too much resources. It supports following limits:

CPU % CPU speed relative to one core. 150% would mean one and a half cores
READ bytesbytes read. Cached reads are not counted, only those that were actually read from disk will be counted
WRITE bytesbytes written. Cached writes are not counted, only once data is written to disk, it is counted

You can set different limits for different periods: current, short, med, long. By default those periods are defined as 1 second, 5 seconds, 1 minute and 5 minutes. They can be re-defined using configuration file. The idea is to use larger acceptable values for shorter periods. Like you could allow a customer to use two cores (200%) for one second, but only 1 core (on average) for 1 minute, and only 70% within 5 minutes. That would make sure that customer can burst for short periods of time.

Customers will also be limited to a finite number of concurrent connections, this number is 30 by default and can be changed. This is done so they wouldn't use up all the MySQL connections to the server. MySQL Governor can also kill off slow SELECT queries.

MySQL Governor limits interaction with LVE limits

How is interaction between MySQL Governor and LVE organized?

The main purpose of MySQL Governor is to monitor how many common resources are used by each user for working with MySQL/MariaDB and to manage usage restrictions for such resources by LVE containers.

Before any SQL request, MySQL Governor determines which user sent the request and if this user exceed limits, the MySQL Governor pushes the request to appropriate LVE container.

This is how common server resources can be managed.

Why the СPU/IO charts are different for database and LVE usage?

SQL requests are not limited inside LVE, so there are not any calculations for IO usage there. It can be clearly viewed via the lve-stats charts:

Blue chart (database):

This is the user’s real IO Database usage which was calculated by MySQL Governor.

Green chart (LVE):

This is the user’s IO Database usage which was calculated by lve-stats.

Also for different types of database load (for example in case, there is a huge amount of shot requests), CPU usage charts for LVE and database can be different.

Take a look on this chart:

Blue (database) CPU usage:

It is calculated by MYSQL Governor and the value is identical with top/htop values.

Green (LVE) CPU usage:

It is calculated by lve-stats. The values are less on the LVE CPU usage chart because user requests are placed in the LVE only for part of the time.

For what purpose are the IO limits of MySQL Governor used?

MySQL Governor uses its limits as triggers to place user’s requests to the LVE. If user’s requests exceed MySQL Governor limits they are placed to the LVE, which already limits resource usage. After some timeout (which can be configured in the MySQL Governor config file requests will not be placed to the LVE. The next placing of the SQL requests to the LVE will occur after the next limits are exceeded.

How exactly does IO limiting work for MySQL/MariaDB requests?

There is no direct IO limitation for database treads. But in case of exceeding governor IO user limits, their requests will be placed into the LVE and CPU LVE limitation will be applied to the requests. And it’s clear that any IO load causes CPU load. So by CPU limits IO usage will be limited indirectly.

Take a look at the next chart. The I/O load is synchronous with the CPU load.

Installation and update

Installation

IMPORTANT

Please note that MariaDB 10.4 release isn’t supported by cPanel, thus you can’t install this version with MySQL Governor + cPanel. More details are available here: https://features.cpanel.net/topic/maria-10-4-support.

IMPORTANT

Please make full database backup (including system tables) before you upgrade MySQL or switch to MariaDB. This action will prevent data loss in case if something goes wrong.

MySQL Governor is compatible only with MySQL 5.x, 8.0; MariaDB & Percona Server 5.6.

To install MySQL Governor on your server install governor-mysql package at first:

yum remove db-governor db-governor-mysql  # you can ignore errors if you don't have those packages installed
yum install governor-mysql

Then configure MySQL Governor properly.

The installation is currently supported only on cPanel, Plesk, DirectAdmin, ISPmanager, InterWorx , as well as on servers without control panel.

If you are installing CloudLinux OS Shared on a server running MySQL already, set your current MySQL version before calling installation script:

/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=mysqlXX
/usr/share/lve/dbgovernor/mysqlgovernor.py --install

Please make sure to specify your current MySQL version instead of XX as follows:

  • 55 — MySQL v5.5
  • 56 — MySQL v5.6
  • 57 — MySQL v5.7
  • 80 — MySQL v8.0 (requires MySQL Governor 1.2-37+)

If you are installing CloudLinux OS Shared on a server running MariaDB already, do instead:

/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=mariadbXX
/usr/share/lve/dbgovernor/mysqlgovernor.py --install

Please make sure to specify your current MariaDB version instead of XX as follows:

  • 55 — MariaDB v5.5
  • 100 — MariaDB v10.0
  • 101 — MariaDB v10.1
  • 102 — MariaDB v10.2
  • 103 — MariaDB v10.3 [requires MySQL Governor 1.2-36+; for cPanel - MySQL Governor 1.2-41+]
  • 104 – MariaDB v10.4 [requires MySQL Governor 1.2-53+]
  • 105 - MariaDB v10.5 [requires MySQL Governor 1.2-62+]
  • 106 - MariaDB v10.6 [requires MySQL Governor 1.2-76+]

Updated note

MariaDB version 10.4 is available for CloudLinux OS Shared 6.

Installation for Percona Server 5.6 [requires MySQL Governor 1.1-22+ or 1.2-21+]:

/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=percona56
/usr/share/lve/dbgovernor/mysqlgovernor.py --install

Please note that MySQL/MariaDB/Percona will be updated from CloudLinux OS Shared repositories.

If you are installing MySQL Governor on a server without MySQL at all, you have an opportunity to choose desired MySQL version to be installed with MySQL Governor installation script. Use --mysql-version flag before calling the installation script:

/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=MYSQL_VERSION
/usr/share/lve/dbgovernor/mysqlgovernor.py --install

MYSQL_VERSION could be chosen from the list of versions currently supported by MySQL Governor :

mysql51MySQL v5.1
mysql55MySQL v5.5
mysql56MySQL v5.6
mysql57MySQL v5.7
mysql80MySQL v8.0 (requires MySQL Governor 1.2-37+)
mariadb55MariaDB v5.5
mariadb100MariaDB v10.0
mariadb101MariaDB v10.1
mariadb102MariaDB v 10.2
mariadb103MariaDB v 10.3 [requires MySQL Governor 1.2-36+; for cPanel - MySQL Governor 1.2-41+]
mariadb104MariaDB v 10.4 [requires MySQL Governor 1.2-53+]
mariadb105MariaDB v 10.5 [requires MySQL Governor 1.2-62+]
mariadb106MariaDB v 10.6 [requires MySQL Governor 1.2-76+]
percona56 Percona Server v 5.6

Generally, stable and beta channels contain different version of MySQL packages - beta contains newer version than stable or the same one. If you would like to install beta packages, use --install-beta flag instead of --install when calling installation script:

/usr/share/lve/dbgovernor/mysqlgovernor.py --install-beta

Starting with MySQL Governor version 1.2 when installing MySQL/MariaDB MySQL Governor asks for a confirmation of a database version to be installed. To avoid such behavior for the automatic installations, please use --yes flag.

For example:

/usr/share/lve/dbgovernor/mysqlgovernor.py --install --yes

Please note that restore of previous packages in case of failed installation would also be confirmed with --yes flag.

IMPORTANT

Use --yes flag on your own risk, because it confirms installation in any case - even in case if there are troubles during installation (for example, network problems causing incomplete download of packages), everything would be confirmed.

Note

See also MySQL Governor CLI

Upgrading database server

Important

Upgrade from MySQL 8 to MariaDB 10.x isn't supported due to compatibility issues between these database server versions and will break your database server completely. You can find more information about the compatibility issues here

Warning

Please note that DB Governor is capable of upgrading your database to a newer version, including sequential upgrades, but we make no guarantee that such upgrades do not affect the operation of the database, its components, control panel operation and other functions of database. Always create a backup copy of your data.

In order to change MySQL version you should run the following commands:

/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=MYSQL_VERSION
/usr/share/lve/dbgovernor/mysqlgovernor.py --install

where MYSQL_VERSION is the target database server version that should be replaced with the value from the table above.

IMPORTANT

Make sure you have full database backup (including system tables) before you switch. This action will prevent data loss in case if something goes wrong.

Uninstalling

To remove MySQL Governor:

/usr/share/lve/dbgovernor/mysqlgovernor.py --delete

The script will install original MySQL server, and remove MySQL Governor.

Configuration and operation

Configuration

Warning

The "All" mode will be deprecated starting from September 1, 2021. You can read more here.

MySQL Governor configuration is located in /etc/container/mysql-governor.xml

It is best to modify it using dbctl tool.

Once configuration file is updated, please, restart the MySQL Governor using:

service db_governor restart
Example configuration:
<governor> 

<!--  'off' - do not throttle anything, monitoring only -->
<!--  'abusers' - when user reaches the limit, put user's queries into LVE for that user -->
<!--  'all' - user's queries always run inside LVE for that user -->
<!--  'single' - single LVE=3 for all abusers. -->
<!-- 'on' - deprecated (old restriction type) -->
<!-- To change resource usage of restricted user in LVE mode use command /usr/sbin/lvectl set 3 --cpu=<new value> --ncpu=<new value> --io=<new value> --save-all-parameters -->
<lve use="on|single|off|abusers|all"/> 

<!-- connection information -->
<!-- If host, login and password are not present, this information is taken from /etc/my.cnf and ~root/.my.cnf -->
<!-- Use symbol specified in prefix to figure out hosting accounts (mysql username will be split using prefix_separator, and first part will be used as account name). If prefix is not set, or empty -- don’t use prefixes/accounts --> 

<!-- db governor will try to split MySQL user names using prefix separator (if present)and statistics will be aggregated for the prefix (account name) -->
<connector host="..." login="..." password=".." prefix_separator="_"/> 

<!-- Intervals define historical intervals for burstable limits. In seconds -->
<intervals short="5" mid="60" long="300"/> 

<!-- log all errors/debug info into this log -->
<log file=”/var/log/dbgovernor-error.log” mode=”DEBUG|ERROR”/> 

<!-- s -- seconds, m -- minutes, h -- hours, d -- days -->
<!-- on restart, restrict will disappear -->
<!-- log file will contain information about all restrictions that were take -->
<!-- timeout - penalty period when user not restricted, but if he hit his limit during this period he will be restricted with higher level of restrict (for more long time) -->
<!- level1, level2, level3, level4 - period of restriction user for different level of restriction. During this period all user's requests will be placed into LVE container -->

<!-- if user hits any of the limits during period of time specified in timeout, higher level of restrict will be used to restrict user. If user was already on level4, level4 will be applied again -->
<!-- attribute format set an restrict log format:
SHORT -  restrict info only
MEDIUM - restrict info, _all_tracked_values_
LONG - restrict info, _all_tracked_values_, load average and vmstat info
VERYLONG - restrict info, _all_tracked_values_, load average and vmstat info, slow query info -->
<!-- script -- path to script to be triggered when account is restricted -->
<!-- user_max_connections - The number of simultaneous connections of blocked user (in LVE mode) --> 

<!-- restriction levels/format are deprecated -->
<restrict level1="60s" level2="15m" level3="1h" level4="1d" timeout="1h"
log="/var/log/dbgovernor-restrict.log" format="SHORT|MEDIUM|LONG|VERYLONG"
script="/path/to/script"
user_max_connections="30"/> 

<!-- period (deprecated) - period based restriction that has multiple levels (see above) -->
<!-- limit (by default) - when user hits limits, the account will be marked as restricted and if user does not hit  limit again during "unlimit=1m" account will be unrestricted. This mode doesn't have any additional levels/penalties. -->
<restrict_mode use="period|limit" unlimit="1m"/>

<!-- killing slow SELECT queries (no other queries will be killed) -->
<!-- if "log" attribute was set all killed queries will be saved in log file -->
<!-- slow parameter in the <limit name="slow" current="30"/> will no be applied without enabling slow_queries --> 
<slow_queries run="on|off" log="/var/log/dbgovernor-kill.log"/> 
<!-- Enable or disable saving of statistics for lve-stats - On - enabled, Off-disabled -->
<statistic mode="on|off"></statistic>
<!-- Enable logging user queries on restrict, can be On or Off -->
<!-- Files are saved in /var/lve/dbgovernor-store and being kept here during 10 days -->
<logqueries use="on|off"></logqueries>
<default>
<!-- -1 not use limit(by default, current - required) -->
<limit name="cpu" current="150" short="100" mid="90" long="65"/>
<limit name="read" current="100000000" short="90000000" mid="80000000" long="70000000"/>
<limit name="write" current="100000000" short="90000000" mid="80000000" long="70000000"/>
<!-- Time to kill slow SELECT queries for account, can be different for accounts in seconds(but unit can be specified) -->
<!-- enabled only when slow_queries run="on" -->
<!-- s -- seconds, m -- minutes, h -- hours, d -- days -->
<!-- Requests are checked at 15 second intervals, so a request will be canceled after a timeout + 15 seconds maximum -->
<limit name="slow" current="30"/>
</default>
<!-- name will matched account name, as extracted via prefix extraction --> 

<!-- mysql_name will match exact MySQL user name. If both name and mysql_name are present, system will produce error -->
<!-- mode restrict -- default mode, enforcing restrictions -->
<!-- mode norestrict -- track usage, but don’t restrict user --> 
<!-- mode ignore -- don’t track and don’t restrict user -->
<user name=”xxx” mysql_name=”xxx” mode=”restrict|norestrict|ignore”>
<limit...>
</user> 

<!-- debug mode for particular user. The information logged to restrict log. -->
<debug_user name="xxx"/> 

</governor>

These values can also be set using cloudlinux-config CLI utility

Modes of operation

Warning

The "All" mode will be deprecated starting from September 1, 2021. You can read more here.

Note

MySQL Governor 1.0+

Active modes

  • abusers - Use LVE for a user to restrict queries (default mode): In that mode, once user goes over the limits specified in the MySQL Governor , all customer's queries will execute inside that user's LVE. We believe this mode will help with the condition when the site is still fast, but MySQL is slow (restricted) for that user. If someone abuses MySQL, it will cause queries to share LVE with PHP processes, and PHP processes will also be throttled, causing fewer new queries being sent to MySQL. Requires dbuser-map file.
  • off - Monitor Only: In that mode MySQL Governor will not throttle customer's queries, instead it will let you monitor the MySQL usage to see the abusers at any given moment in time (and historically). This mode is good when you are just starting and want to see what is going on.

Deprecated modes

  • all - Always run queries inside user's LVE (will be deprecated on September 1, 2021): This way there is no need for separate limits for MySQL. Depending on overhead we see in the future, we might decide to use it as a primary way of operating MySQL Governor . The benefit of this approach is that limits are applied to both PHP & MySQL at the same time, all the time, preventing any spikes whatsoever. Requires dbuser-map file.
  • single - Single restricted's LVE for all restricted customers (deprecated): In that mode once customer reaches the limits specified in the MySQL Governor , all customer's queries will be running inside LVE with id 3. This means that when you have 5 customers restricted at the same time, all queries for all those 5 customers will be sharing the same LVE. The larger the number of restricted customers - the less resources per restricted customer will be available.
  • on - Synonym for single mode

Note

After the all mode will be deprecated on September 1, 2021:

  • the users, having it, will continue to work with this mode;
  • all new installation will not have the all mode;
  • moving to the all mode will be forbidden.

If the dbuser-map file is absent on the server, the abusers mode emulates the single.

With the single and abusers mode, once user is restricted, the queries for that user will be limited as long as user is using more than limits specified. After a minute that user is using less, we will unrestricted that user.

You can specify modes of operation using dbctl or by changing configuration file.

MySQL Governor limits

MySQL Governor limit setting recommendations

Note

Please note that these recommendations on setting limits for MySQL Governor are general. The exact values of the limits for the effective work of MySQL and the site as a whole depend on many factors.

Here they are:

  • the load of the virtual server with incoming requests
  • the database size
  • SQL queries efficiency
  • absolute values of the LVE limits

MySQL Governor allows setting the burstable limits for accounts. To provide that possibility, four levels of limits are defined: current, short, middle, and long. Correctly set limits can give users more CPU without having a bottleneck on MySQL.

General principles of choosing the limits:

  • current and short can be more than the LVE limit and should not be less
  • setting the current and short limits more than the LVE limit prevents bottlenecks in SQL request processing
  • middle limit can be more or less that the LVE limit
  • long on the contrary, should not be more than the LVE limit
  • setting the middle and long limits less than the LVE limit prevents abuse of other processes in the account (Apache, PHP) by MySQL

Example of choosing MySQL Governor limits

  • With the default LVE SPEED limit is 100, the possible values of the MySQL Governor CPU limits can be 250/150/110/90. I.e., we admit the short-term exceeding of the limits for processing SQL requests.
  • If you face spike CPU consumption with these limits, it is recommended to reduce the excess of the current and short limits over the LVE limit. For example, to the values 150/110/100/90.
  • If the average level of CPU consumption is too high, then it is recommended to reduce the middle and long limits, too. For example, to the values 150/100/80/50.
  • Then MySQL processes will fall into LVE and be limited by LVE limits more often.
  • The same clues are applicable to the IO limits – the current and short IO limits for MySQL Governor can exceed IO LVE limits, but the middle and long cannot.

Starting and stopping

To start:

service db_governor start

To stop:

service db_governor stop

Mapping a user to a database

[ MySQL Governor 1.x]

Traditionally MySQL Governor used prefixes to map user to database. With the latest version, we automatically generate user -> database user mapping for cPanel , Plesk and DirectAdmin control panels.

The mapping file is recreated daily by cron.

Mapping recreation is also triggered by the following events on cPanel servers:

  • creation of user
  • modification of user
  • removal of user

You can also rebuild the mapping file manually on cPanel, Plesk and DirectAdmin control panels by running the following command:

/usr/share/lve/dbgovernor/mysqlgovernor.py --dbupdate

The mapping file is located in: /etc/container/dbuser-map

The format of the file:

[dbuser_name1] [account_name1] [UID1]
...
[dbuser_nameN] [account_nameN] [UIDN]
For example:
pupkinas_u2 pupkinas 502
pupkinas_u1 pupkinas 502
pupkinas_u3 pupkinas 502
pupkin2a_uuu1 pupkin2a 505
pupkin10_p10 pupkin10 513
pupkin5a_u1 pupkin5a 508
pupkin3a_qq1 pupkin3a 506
pupkin3a_test22 pupkin3a 506
pupkin3a_12 pupkin3a 506

This would specify that db users: pupkinas_u2, pupkinas_u1, pupkinas_u3 belong to user pupkinas with uid (lve id) 502 db user pupkin2a_uuu1 belongs to user pupkin2a with uid 505, etc...

db_governor service checks this file for modifications every 5 minutes.

If you need to force reload the mapping file, run:

service db_governor restart

Log files

Error_log

MySQL Governor error log is used to track any problems that MySQL Governor might encounter. Error log is located in /var/log/dbgovernor-error.log

Restrict_log

Restrict log is located in /var/log/dbgovernor-restrict.log

Restrictions:

_timestamp_ _username_ LIMIT_ENFORCED _limit_setting_ __current_value_                         _restrict_level__ SERVER_LOAD TRACKED_VALUES_DUMP
 ... 
  • TRACKED_VALUES_DUMP=busy_time:xx,cpu_time:xx,...
  • SERVER_LOAD = load averages followed by output of vmstat
  • TRACKED_VALUES_DUMP is available with MEDIUM & LONG format
  • SERVER_LOAD is available with LONG format

Kill_log

MySQL Governor kill log is optional log located in the /var/log/dbgovernor-kill.log. Kill log is used to track all killed queries

Change MySQL version

If you would like to change to a different MySQL version, or switch to MariaDB you have to start by backing up existing databases.

Note

For experienced users only. Changing MySQL version is a quite complicated procedure, it causes system table structural changes which can lead to unexpected results. Think twice before proceeding.

IMPORTANT

Please make full database backup (including system tables) before you will do upgrade of MySQL or switch to MariaDB. This action will prevent data losing in case if something goes wrong.

/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version=MYSQL_VERSION
/usr/share/lve/dbgovernor/mysqlgovernor.py --install
  • If you are using cPanel or DirectAdmin - recompile Apache .

To install beta version of MySQL:

/usr/share/lve/dbgovernor/mysqlgovernor.py --install-beta

MYSQL_VERSION can be one of the following:

auto default version of MySQL for given OS release (or cPanel settings)
mysql51MySQL v5.1
mysql55MySQL v5.5
mysql56MySQL v5.6
mysql57MySQL v5.7
mysql80MySQL v8.0 (requires MySQL Governor 1.2-37+)
mariadb55MariaDB v5.5
mariadb100MariaDB v10.0
mariadb101MariaDB v10.1
mariadb102MariaDB v 10.2
mariadb103MariaDB v 10.3 [requires MySQL Governor 1.2-36+; for cPanel - MySQL Governor 1.2-41+ ]
mariadb104MariaDB v 10.4 [requires MySQL Governor 1.2-53+]
percona56Percona v 5.6
  • We don't recommend to downgrade from MySQL v5.6, MariaDB 10.x

Note

cPanel does not officially support MariaDB 10.4, that is why we don’t recommend to use it on cPanel servers. Use on your own risk for Plesk servers, because downgrade can corrupt your databases.

Note

MariaDB version 10.4 is not available for CloudLinux OS Shared 6 yet.

Note

See also MySQL Governor CLI

PrivateDevices mode support

MySQL Governor v. 1.2-66 and later on Cloudlinux OS Shared 7 and 8 supports the PrivateDevices mode for the mysqld service.

To switch on the PrivateDevices mode, follow these steps.

On Cloudlinux OS Shared 7

  • Make sure that the systemd version is at least 219-78.2.cloudlinux.1
  • Add the following instruction into the Service section of the mysqld service file:
    PrivateDevices=true
    
  • Invoke systemctl daemon-reload
  • Restart mysqld service

On Cloudlinux OS Shared 8

  • Add the following instructions into the Service section of the mysqld service file:
    PrivateDevices=true
    DeviceAllow=/dev/lve
    BindPaths=/dev/lve 
    
  • Invoke systemctl daemon-reload
  • Restart mysqld service

Backing up MySQL

On cPanel server disable MySQL service monitoring before doing the job:

whmapi1 configureservice service=mysql enabled=1 monitored=0

Execute as root :

mkdir -p ~/mysqlbkp
service mysql restart --skip-networking --skip-grant-tables
mysql_upgrade
mysqldump --all-databases --routines --triggers > ~/mysqlbkp/dbcopy.sql
service mysql stop
cp -r /var/lib/mysql/mysql ~/mysqlbkp/
service mysql start

On cPanel server enable monitoring back:

whmapi1 configureservice service=mysql enabled=1 monitored=1

Note

This operation may take some time.

See also MySQL Governor CLI tools.

MySQL Governor improvements for CPU calculation

In MySQL Governor version 1.2-81, we provide improvements in the algorithm of calculation user CPU usage. New behavior helps to increase precision of resource distribution between server users. By default the new type of CPU usage calculation is tuned on.

Server administrator can turn on/off the new type of CPU usage calculation by using the following command:

dbctl --lve-improved-accuracy off

What is the impact of improvements?

The calculation of CPU usage has become more accurate, so the dbtop utility provides more correct information to the MySQL Governor and it places user’s requests to the LVE in a proper moment. And as a result, such improvements reduce the possibility of absorbing whole server resources by one user.

One mpre possible outcome of calculation improvements is that some server users will become in need of MySQL Governor limits reconfiguration.

How to view the impact of improvements?

Let’s check the CPU usage charts from CloudLinux statistics (lve-stats).

The new type of  CPU usage calculation is turned off.

In this case, CPU usage by database could be less than LVE average CPU usage (blue chart is lower than green chart):

The new type of  CPU usage calculation is turned on.

In this case, CPU usage by database become more similar to LVE average CPU usage (blue chart and green chart on the sceen):

FAQ

How is interaction between MySQL Governor and LVE organized?

The main purpose of MySQL Governor is to monitor how many common resources are used by each user for working with MySQL/MariaDB and to manage usage restrictions for such resources by LVE containers.

Before any SQL request, MySQL Governor determines which user sent the request and if this user exceed limits, the MySQL Governor pushes the request to appropriate LVE container.

This is how common server resources can be managed.

Why the СPU/IO charts are different for database and LVE usage?

SQL requests are not limited inside LVE, so there are not any calculations for IO usage there. It can be clearly viewed via the lve-stats charts:

Blue chart (database):

This is the user’s real IO Database usage which was calculated by MySQL Governor.

Green chart (LVE):

This is the user’s IO Database usage which was calculated by lve-stats.

Also for different types of database load (for example in case, there is a huge amount of shot requests), CPU usage charts for LVE and database can be different.

Take a look on this chart:

Blue (database) CPU usage:

It is calculated by MYSQL Governor and the value is identical with top/htop values.

Green (LVE) CPU usage:

It is calculated by lve-stats. The values are less on the LVE CPU usage chart because user requests are placed in the LVE only for part of the time.

For what purpose are the IO limits of MySQL Governor used?

MySQL Governor uses its limits as triggers to place user’s requests to the LVE. If user’s requests exceed MySQL Governor limits they are placed to the LVE, which already limits resource usage. After some timeout (which can be configured in the MySQL Governor config file requests will not be placed to the LVE. The next placing of the SQL requests to the LVE will occur after the next limits are exceeded.

How exactly does IO limiting work for MySQL/MariaDB requests?

There is no direct IO limitation for database treads. But in case of exceeding governor IO user limits, their requests will be placed into the LVE and CPU LVE limitation will be applied to the requests. And it’s clear that any IO load causes CPU load. So by CPU limits IO usage will be limited indirectly.

Take a look at the next chart. The I/O load is synchronous with the CPU load.

Troubleshooting

MariaDB 5.5 and MariaDB 10.0: How to set LimitNOFILE correctly for systemd.

MariaDB 5.5 and MariaDB 10.0 have only file for managing the service, but the file has LSB functions, so it is supported by systemd .

For adding extra limits, do the following:

  1. Run:
mkdir /etc/systemd/system/mariadb.service.d/
  1. Run:
touch /etc/systemd/system/mariadb.service.d/limits.conf
  1. Add the following content to the the file /etc/systemd/system/mariadb.service.d/limits.conf :
[Service] 
LimitNOFILE=99999

MySQL Governor lost connection to MySQL - “Can't connect to mysql” messages in /var/log/dbgovernor-error.log (Plesk and DirectAdmin)

This may be caused by changing root/administrator credentials without updating MySQL Governor configuration file.

When you change root or administrator credentials in Plesk or DirectAdmin, you also need to update MySQL Governor configuration file. This could be done with the following command (available since governor-mysql 1.2-38):

/usr/share/lve/dbgovernor/mysqlgovernor.py --update-config-auth

The command updates credentials in MySQL Governor configuration file and restarts db_governor service afterwards.

After applying the command MySQL Governor successfully connects to MySQL.

Handling Missing Libraries in cl-MariaDB-libs for MySQL Governor (libgovernor_stubs.so)

During the update of latest cl-MariaDB-libs, issues related to the addition or removal of certain shared libraries in cl-MySQL/cl-MariaDB packages may arise. The presence or absence of these libraries can disrupt the functioning of clients' systems.

When you encounter errors pointing towards missing or newly added libraries after updating the cl-MySQL/cl-MariaDB packages, it's crucial to adjust and address the configurations to restore normalcy.

For instance, the error might resemble the following message:

ImportError: libexample_stubs.so: cannot open shared object file: No such file or directory

The recommended approach to resolving such issues involves three primary steps:

  1. Verify the Presence of the Library.

Use the command to ascertain the library's presence in the current version:

yum provides '*/libexample_stubs.so'
  1. Recompiling the Affected Package.

Recompile the affected package for each user, especially if using Python's mysql package (mysqlclient):

pip install mysqlclient --force --no-cache-dir

However, this is not the ideal solution since sites that are functional may break after a system update.

  1. Using Utilities to Modify Binary.

If recompilation isn't feasible, patchelf utility can help remove references to the missing library in the binary:

patchelf --remove-needed libexample_stubs.so path/to/affected/binary.so

Applying the aforementioned steps should rectify the issues, allowing MySQL Governor to function correctly with the updated libraries in cl-MySQL/cl-MariaDB packages.

Always remember to back up your data and configurations before making any changes to ensure a safety net in case of any inadvertent errors. If you continue to face challenges, please reach out to our support team for assistance.

Known limitations

I/O LVE limits don't work for user’s SQL queries

In the MySQL Governor default mode, once users go over the limits, all their SQL queries will execute inside that user's LVE.

This technique was provided with the early 1.1.5 version of MySQL Governor.

It turned out that memory limitation for MySQLrequests causes the OOM (Out Of Memory) issues and as a result database corruptions. So, it was decided not to apply LVE memory limits for SQL queries supplied by MySQL Governor. But the internal implementation of LVE is such that I/O and memory limits work together. That’s why the I/O LVE limits do not apply for user’s SQL queries now.

Nevertheless, SQL queries inside LVE are restricted by CPU limit which indirectly limits I/O usage, too.

PHP Selector

General information and requirements

The main requirements:

  • CageFS is installed
  • Alt-PHP packages are installed
  • Mod_suexec is installed. You can find installation instruction here
  • CageFS is initialized without errors
  • CageFS is enabled for a domain user-owner
  • An appropriate PHP handler is selected for PHP version which is system version. PHP Selector is compatible with the following technologies: suPHP, mod_fcgid, CGI (suexec), LiteSpeed. See also Compatibility Matrix.
  • PHP version in the CloudLinux OS Shared PHP selector does not equal to the Native PHP version

Note

PHP Selector is not supported for H-Sphere.

Note

PHP Selector requires native PHP to be installed, otherwise you will get an error message with a proposal to install PHP package. Here are some instructions for different control panels:

  1. for cPanel
  2. for Plesk
  3. for DirectAdmin

After installing native PHP, please run the cloudlinux-selector setup --interpreter=php --json command in order to reconfigure CageFS and LVE Manager.

Supported versions

Installation and update

The installation of PHP Selector presumes that you already have CageFS & LVE Manager installed.

Use compatibility matrix to check if your Web Server/PHP mode is supporting PHP Selector. If not, you need a change to one of the supported models.

Installation of different versions of PHP & modules:

yum groupinstall alt-php

Update CageFS & LVE Manager with support for PHP Alternatives:

$ yum update cagefs lvemanager

cPanel/WHM: Make sure 'Select PHP version' is enabled in Feature Manager .

IMPORTANT

Please, do not use settings like SuPHP_ConfigPath, PHPRC, PHP_INI_SCAN_DIR. Do not redefine path to php.ini and ini-files for PHP modules. Doing that can break PHP Selector functionality.

For example, alternative php5.2 versions should load /opt/alt/php52/etc/php.ini file and scan /opt/alt/php52/etc/php.d directory for modules:

Configuration File (php.ini) Path         /opt/alt/php52/etc
Loaded Configuration File                 /opt/alt/php52/etc/php.ini
Scan this dir for additional .ini files   /opt/alt/php52/etc/php.d
additional .ini files parsed              /opt/alt/php52/etc/php.d/alt_php.ini

Those are default locations for alt-php.

If you need custom PHP settings per user, please change them via "Edit PHP settings" feature of PHP Selector .

If a list of default modules is absent on the server in the /etc/cl.selector/defaults.cfg file for some alt-PHP version and there is nd_mysqli extension in this version, then on installation/update of the LVE Manager the mysqli extension will be disabled and nd_mysqli extension will be enabled automatically.

  • If nd_mysqli module is absent or a list of enabled modules is available, then they won't be changed automatically.
  • If alt-PHP is not installed on LVE Manager installation/update, then they won’t be changed automatically.

To change the modules status (enabled/disabled) manually, run the following command in a console:

/usr/sbin/cloudlinux-selector make-defaults-config --json --interpreter=php

Update

To update PHP Selector, run the following command:

yum groupupdate alt-php

This command allows to install newly released versions in PHP Selector.

Note

See also PHP Selector CLI

Installation instructions for cPanel users

  1. Install CageFS as root via SSH:
yum install cagefs
  1. Install alt-php packages as root:
yum groupinstall alt-php
  1. Install mod_suexec package as root. See installation instructions here.
  2. Verify that CageFS is initialized successfully.
  • via SSH by running the following command:
cagefsctl --check-cagefs-initialized
  • via cPanel admin interface

Go to cPanel → Admin interface → LVE Manager → Dashboard → click Refresh

If there is a problem you can see Not initialized

  1. Initilize CageF (if it is not initialized)
  • Via SSH
cagefsctl --init
  • Via cPanel admin interface

    Go to cPanel → Admin interface → LVE manager → Options → CageFS INIT

If CageFS was initialized after refreshing Dashboard you will see that CageFS is enabled:

  1. Enable CageFS to a user

Go to cPanel → Admin interface → LVE manager → Users

  • For one user by individual slider (for LVE 1001 in the picture above)
  • For a group of user by the CageFS button (for LVE 1002 and 1003 in the picture above)
  1. Check that system PHP version is not alt-php (it should be ea-php)

Go to cPanel → Admin interface → MultiPHP Manager → PHP versions

  1. Check that an appropriate PHP handler is selected for PHP version which is system version

Go to cPanel Admin interface → MultiPHP Manager → PHP Handlers

  1. Check version for domain in MultiPHP Selector. It should be equal to the system default version

Go to cPanel Admin interface → MultiPhp Manager → PHP versions → scroll to Set PHP Version per Domain

  1. Version for domain in User’s interface in PHP Selector should not be equal to the Native version.

LiteSpeed support

Note

LiteSpeed detects CloudLinux OS Shared and applies all settings out-of-the-box.

Note

If your LiteSpeed is installed to a non-standard, custom location path, create a symlink: ln -s /path/to/custom/lsws /usr/local/lsws then run cagefsctl --setup-cl-selector.

If the settings were not applied, you can use the following steps to set up LiteSpeed to use PHP Selector.

  1. Follow PHP Selector installation guide.
  2. Make sure the following settings are set in the LSWS Web Admin console:
  • Configuration ➞ Server ➞ General ➞ CloudLinux OS Shared: CageFS or CageFS without suEXEC
  • Configuration ➞ Server ➞ General ➞ PHP suEXEC: Yes

How to set up LiteSpeed version 5.3+ to use PHP Selector

For Plesk

For other control panels

Additionally, we recommend setting up the following parameters:

  • Configuration ➞ Server ➞ PHP:
  • Click Edit in the PHP Handler Defaults section
  • Set Yes in the Run On Startup
  • Make sure to set Max Idle Time (for example to 140)

How to set up LiteSpeed version lower than 5.3 to use PHP Selector

Go to the External App tab, External Application ➞ Add.

  • The Command line should be /var/www/cgi-bin/cgi_wrapper/cloudlinux_wrapper on Plesk.
  • For other control panels, the Command line should be /usr/local/bin/lsphp.
  • The Run On Start Up line must contain Yes or No.

For Plesk

For other control panels

Settings in text format:

Namelsphp_selector
Addressuds://tmp/lshttpd/lsphp_selector.sock
NotesNot Set
Max Connections35
EnvironmentPHP_LSAPI_MAX_REQUESTS=5000
PHP_LSAPI_CHILDREN=35
Initial Request Timeout (secs)60
Retry Timeout (secs)0
Persistent ConnectionYes
Connection Keepalive TimeoutNot Set
Response BufferingNo
Auto StartThrough CGI Daemon (Async)
Command* For Plesk /var/www/cgi-bin/cgi_wrapper/cloudlinux_wrapper
* For other control panels /usr/local/bin/lsphp
Back Log100
Instances1
suEXEC UserNot Set
suEXEC GroupNot Set
umaskNot Set
Run On Start UpYes
Max Idle Time70
Priority0
Memory Soft Limit (bytes)2047M
Memory Hard Limit (bytes)2047M
Process Soft Limit400
Process Hard Limit500

Go to the Script Handler tab. For required suffixes, change the Handler Name to lsphp_selector.

Note

In order to use PHP Selector and custom php.ini, lsphp5 needs to be in SuEXEC non-daemon mode.

Note

Some PHP configurations require more memory for SuExec to work properly. If you are getting error 500 after switching suEXEC to non-daemon mode, try to increase Memory Soft Limit and Memory Hard Limit for external App to at least 650/800M.

ISPmanager support

As of July 2013, PHP Selector support for ISPmanager is limited to command line utilities. You should still be able to use it.

As always, PHP Selector requires CGI, FCGI or suPHP to work.

You will need to do following modifications:

Create new file /usr/local/bin/php-cgi-etc:

#!/bin/bash
/usr/bin/php-cgi -c /etc/php.ini "$@"
Make that file executable:
chmod +x /usr/local/bin/php-cgi-etc
Edit file /usr/local/ispmgr/etc/ispmgr.conf

Add a line:

path phpcgibinary /usr/local/bin/php-cgi-etc

Make sure there is no other lines with path phpcgibinary defined in the file.

Restart ISPmanager :

killall ispmgr

After that FCGID wrappers (/var/www/[USER]/data/php-bin/php) for new users will be like this:

#!/usr/local/bin/php-cgi-etc

You might need to edit/modify wrappers for existing users if you want them to be able to use PHP Selector. You can leave them as is for users that don't need such functionality.

Uninstalling

It is not possible to remove PHP Selector from the system completely as it is an essential part of LVE Manager and CageFS packages. However, you can make PHP Selector unavailable for cPanel and Plesk users.

To do so, go to LVE Manager → PHP Selector and check Disabled as PHP Selector status. Doing so allows you to disable web-interface of the PHP Selector in the user interface but does not reset custom settings (choosing a version of PHP and modules).

To disable PHP Selector and make it has no effect on a PHP version on the sites, run the following command:

  • this command resets PHP versions to Native:
cagefsctl --cl-selector-reset-versions
  • this command resets PHP modules to Default:
cagefsctl --cl-selector-reset-modules

These commands can affect PHP version of your clients’ web sites. Use them with caution as improper usage might cause your clients’ web sites down.

Note

It is possible to manually uninstall the integral parts of PHP Selector - alt-php packages. However, since CloudLinux OS components heavily rely on them, we do not recommend doing so. Proceed with caution and keep an eye on yum dependencies if you decide to uninstall the packages.

Disabling PHP extension globally

If you want to disable PHP extension globally, you don't need to remove file /opt/alt/phpXX/etc/php.d.all/$EXTENSION.ini . You should just comment out "extension=" directives in it.

The extension will be visible in PHP Selector interface, but selecting it in users's interface will take no effect - extension will be disabled in fact.

Reinstalling of alt-php packages will not reset settings (will not enable extension again).

Configuration and using

Setting default version and modules

Administrator can set default interpreter version and extensions for all users. All file operations are actually done by CageFS. CageFS takes settings from /etc/cl.selector/defaults.cfg. Currently the /etc/cl.selector/defaults.cfg is created and handled by CloudLinux OS Shared PHP Selector scripts. It has the following format:

[global]
selector=enabled

[versions]
php=5.4

[php5.4]
modules=json,phar

[php5.3]
modules=json,zip,fileinfo

Individual PHP.ini files

For each customer, inside CageFS, file alt_php.ini is located in /etc/cl.php.d/alt-phpXX (XX - version of PHP, like 52 or 53). The file contains PHP extension settings and extension directives selected by customer. This file exists for each customer, for each PHP version.

Note, that this is 'local' to CageFS, and different users will have different files. The file is not visible in /etc/cl.php.d outside CageFS. If you would like to view that file, use:

# cagefsctl -e USERNAME 

to enter into CageFS for that user. Then type: exit ; to exit from CageFS

This file has to be updated using cagefsctl --rebuild-alt-php-ini after updating alt-php RPMs

Admin can change individual settings for PHP extensions by changing that extension's ini file, like editing /opt/alt/php54/etc/php.d.all/eaccelerator.ini and then running:

cagefsctl --rebuild-alt-php-ini
to propagate the change.

How to add additional php.ini file for a user inside CageFS

If you want to create additional php.ini file for a user inside CageFS in order to change some specific PHP options for that user, you can execute the following:

su -s /bin/bash - USER
cd /etc/cl.php.d/alt-php72/
echo "upload_tmp_dir=/tmp" >> custom.ini

The commands above create custom.ini file that will be used for alt-php72. By default this approach is valid only for alt-php version selected via PHP Selector. When /etc/cl.selector/symlinks.rules file contains php.d.location = selector line, then the approach is valid for all alt-php versions regardless whether it is selected in PHP Selector or not.

You can find more details here.

But the recommended way is to modify PHP options via PHP Selector web or CLI interfaces, as described here.

Substitute global php.ini for individual customer

Sometimes you might want to have a single customer with a different php.ini, than the rest of your customers.

To do that, you will use custom.etc directory functionality

  1. Move default php.ini into /etc directory and create a symlink to it:
mv /usr/local/lib/php.ini /etc/php.ini
ln -fs /etc/php.ini /usr/local/lib/php.ini
  1. Change path to php.ini in /etc/cl.selector/native.conf file to:
php.ini=/etc/php.ini
  1. For each user that needs custom file, create directory /etc/cagefs/custom.etc/USER_NAME/php.ini .

For example if you want to create custom for USER1 and USER2 you would create files:
/etc/cagefs/custom.etc/USER1/php.ini
/etc/cagefs/custom.etc/USER2/php.ini

Create such files for each user that should have custom file.

  1. Execute:
cagefsctl --force-update 

Notes

  1. Make sure that php.ini load path is set to /etc/php.ini

  2. Users will be able to override settings of those php.ini files (global or custom) via PHP Selector. If you want to prevent that, you should disable PHP Selector feature.

  3. Even if PHP Selector is disabled, user can override PHP settings by using ini_set() php function in PHP script, or by php -c command line option.

  4. If you modify anything in /etc/cagefs/custom.etc directory, you should execute:

cagefsctl --update-etc

in order to apply changes to CageFS for all users.

OR

cagefsctl --update-etc user1 user2

to apply changes to CageFS for specific users.

How to substitute global php.ini for individual customer on cPanel server with EasyApache4

Note

It is enough to put php.ini in the directory where PHP script is located in order to run the PHP script with a custom php.ini when using SuPHP. Also, you can use cPanel MultiPHP Manager to create user’s custom php.ini file, and this approach should work for CGI, FCGI, and LSAPI. Recommended ways to manage php.ini settings per user are to use cPanel MultiPHP or CloudLinux OS Shared PHP Selector interfaces.

  1. For each user that needs custom file, create directory /etc/cagefs/custom.etc/USER_NAME/php.ini.

    For example, if you want to create a custom file for USER1 and USER2 you would create files:

    /etc/cagefs/custom.etc/USER1/php.ini
    /etc/cagefs/custom.etc/USER2/php.ini
    

    Create such files for each user that should have a custom file.

  2. Execute the following command:

    $ cagefsctl --force-update
    
  3. Configure php.ini load path for user’s domains.

  • When using suphp handler, you should use SuPHP_ConfigPath directive in virtual host configuration for these domains, or use this directive in .htaccess files: suPHP_ConfigPath/etc.

  • When using mod_lsapi, you should use lsapi_phprc directive in virtual host configuration: lsapi_phprc/etc/. You can find the detailed description of mod_lsapi directives here.

  • When using FCGI or CGI, you should implement custom PHP wrapper and redefine the path to php.ini via -c command line option, like below:

    #!/bin/bash
    [ -f /etc/php.ini ] && exec /usr/bin/php -c /etc/php.ini
    exec /usr/bin/php
    

Notes:

  1. You should restart Apache web server after modifying virtual host configuration for the domains.

  2. Custom php.ini may break switching PHP version via CloudLinux OS Shared PHP Selector or cPanel MultiPHP Manager for the appropriate users or domains.

  3. When using cPanel ea-php for the domains, additional php.ini files may not be loaded, so you should load all needed PHP extensions in custom /etc/php.ini file:

  4. When using CloudLinux OS Shared alt-php, additional php.ini files will be loaded:

  5. If you have modified anything in /etc/cagefs/custom.etc directory, you should execute one of the following:

    • to apply changes to CageFS for all users, run:
    cagefsctl --update-etc
    
    • to apply changes to CageFS for specific users, run:
    cagefsctl --update-etc user1 user2
    

Managing interpreter version

Managing interpreter versions is done by means of manipulating a set of symbolic links that point to different versions of interpreter binaries. For example, if default PHP binary is /usr/local/bin/php :

  • First we move the default binary inside CageFS to /usr/share/cagefs-skeleton/usr/selector , and make /usr/local/bin/php a symlink pointing to /etc/cl.selector/php . This operation is done as part of CageFS deployment.
  • Next suppose we have additional PHP version, say 7.2.5. The information about all additional interpreter binaries and paths for them is kept in /etc/cl.selector/selector.conf . This config file is updated by RPM package manager each time alternative PHP package is added, removed or updated
  • /usr/bin/selectorctl --list --interpreter=php will get us list of all available PHP interpreter versions out of /etc/cl.selector/selector.conf file . Next we want to know which PHP version is active for a given user (to supply a selected option in options list). We type:
  • /usr/bin/selectorctl --user USERNAME --interpreter=php --user-current will retrieve PHP version set for a particular user. The script gets the path from /var/cagefs/LAST_TWO_DIGITS_OF_UID/USERNAME/etc/cl.selector/php symlink, compares it with contents of /etc/cl.selector/selector.conf file and if path is valid, prints out the current interpreter version.
  • /usr/bin/selectorctl --user USERNAME --interpreter=php --set-user-current=7.2 sets the current PHP version for particular user by creating symlink in /var/cagefs/LAST_TWO_DIGITS_OF_UID/USERNAME/etc/cl.selector directory. All old symlinks are removed, and new symlinks are set.

Including PHP Selector only with some packages (cPanel)

cPanel has a ' Feature Manager ' in WHM that allows you to disable PHP Selector for some of the packages that you offer.

In reality it only disables the icon in cPanel interface. Yet, in most cases it should be enough in shared hosting settings.

You can find more info on ' Feature Manager ' here: http://docs.cpanel.net/twiki/bin/view/11_30/WHMDocs/FeatureManager

Once PHP Selector is enabled, you can find it in the Feature Manager . Disabling it in Feature Manager , will remove the icon for users that are using that particular 'Feature List'

PHP extensions

Configuring Alt-PHP modules loading

CloudLinux OS Shared PHP Selector and Alt-PHP can be used in conjunction with Plesk PHP Selector and cPanel MultiPHP . To be compatible, CloudLinux OS Shared PHP Selector works as follows: modules that are selected in CloudLinux OS Shared PHP Selector are loaded for Alt-PHP version selected in CloudLinux OS Shared PHP Selector only. For the rest Alt-PHP versions default module set is loaded ( /opt/alt/phpXX/etc/php.d/default.ini ) . Described above is default behavior.

Note

If system default PHP version selected in cPanel MultiPHP Manager is not ea-php, then default module set is loaded for all Alt-PHP versions by default (/opt/alt/phpXX/etc/php.d/default.ini).

When "php.d.location = selector" option is in effect, modules selected via PHP Selector will be loaded for all alt-php versions.

This behavior is implemented in CageFS-6.1-10 and later.

In LVE Manager 1.0-9.40+ this behavior can be modified so that modules selected in CloudLinux OS Shared PHP Selector would be loaded for all Alt-PHP versions (with CageFS enabled), which can be quite useful if you use  ‘ per directory ’ or ‘ per domain ’ Alt-PHP configuration and want to select modules using CloudLinux OS Shared PHP Selector .

To modify it, create a file /etc/cl.selector/symlinks.rules (read-only for regular users) with the following content: php.d.location = selector

And run the command to apply changes:

/usr/bin/selectorctl --apply-symlinks-rules
To revert to the default behavior:
  • Delete /etc/cl.selector/symlinks.rules file.
  • Alternatively remove php.d.location option from the file.
  • Alternatively set default value for php.d.location option.

And run the command to apply changes:

/usr/bin/selectorctl --apply-symlinks-rules

FFmpeg

Note

The PHP-FFmpeg project has not been renewed for the last few years and is not compatible with the RHEL 8 based systems. Since code for alt-PHP-FFmpeg is outdated and FFmpeg has a lot of third-party dependencies, this module is not available for CloudLinux OS Shared 8.

Due to possible patent issues CloudLinux OS Shared does not provide FFmpeg libraries ( https://ffmpeg.org/legal.html ). We highly recommend researching if you can legally install FFmpeg extension on your server. This might differ based on where you and your servers are located. More information can be found on the link: https://ffmpeg.org/legal.html

For your convenience we provide FFMPEG PHP binding. For them to work, you need to install FFmpeg package from the “Nux Dextop” repository following the instructions.

Once FFmpeg is installed you can install PHP bindings, by running:

yum install alt-php*ffmpeg 

Enable PHP-FFmpeg extension via PHP Selector :

selectorctl --enable-extensions=ffmpeg --user USERNAME --version X.Y

Native PHP configuration

PHP Selector requires access to the native PHP version for proper work. It is specified in the file /etc/cl.selector/native.conf of the following content (example):

php=/usr/bin/php-cgi
php-cli=/usr/bin/php
php.ini=/etc/php.ini
lsphp=/usr/local/bin/lsphp
php-fpm=/usr/local/sbin/php-fpm

Then execute the following command to apply changes.

cagefsctl --setup-cl-selector

The file is created when installing CageFS on the servers with cPanel, Plesk, DA, Interworx and ISP Manager , if it is missing. On all other servers the file is not being created at all.

That is why, if the file is not created automatically, then it must be created manually and correct paths must be written to its directives.

Access permission 644 must be set:

chmod 0644 /etc/cl.selector/native.conf

How to configure alt-php72-zts to use with PHP Selector

Requirements

To use alt-php72-zts with PHP Selector you need the following:

  • cagefs-6.1.8-1 or later
  • alt-php72-zts-7.2.21-4 or later

Using zts PHP

  1. Install alt-php72-zts with the following command:
yum install alt-php72-zts
  1. Make sure that /etc/cl.selector/selector.conf file contains correct paths to the PHP zts binaries.

You should remove the old lines:

php 7.2 7.2.20 /opt/alt/php72/usr/bin/php-cgi
php-cli 7.2 7.2.20 /opt/alt/php72/usr/bin/php

And replace them with the lines with the new paths:

php 7.2 7.2.20 /opt/alt/php72/usr/bin/zts-php-cgi
php-cli 7.2 7.2.20 /opt/alt/php72/usr/bin/zts-php
  1. Make sure that /opt/alt/php72/etc/php.d.all path refers to the directory containing ini files for zts PHP extensions:
cd /opt/alt/php72/etc
ln -fsn php.d.all.zts php.d.all
  1. Execute the following command:
cagefsctl --setup-cl-selector

Using non-zts PHP

  1. Make sure that /etc/cl.selector/selector.conf file contains correct paths to the non-zts PHP binaries.

You should remove the old lines:

php 7.2 7.2.20 /opt/alt/php72/usr/bin/zts-php-cgi
php-cli 7.2 7.2.20 /opt/alt/php72/usr/bin/zts-php

And replace them with the lines with the new paths:

php 7.2 7.2.20 /opt/alt/php72/usr/bin/php-cgi
php-cli 7.2 7.2.20 /opt/alt/php72/usr/bin/php
  1. Make sure that /opt/alt/php72/etc/php.d.all path refers to the directory containing ini files for non-zts PHP extensions:
cd /opt/alt/php72/etc
ln -fsn php.d.all.def php.d.all
  1. Execute the following command:
cagefsctl --setup-cl-selector

Using

Once PHP Selector is installed, you will see the Selector tab in the LVE Manager.

Customers can use PHP Selector client plugin to change their PHP Selctor related settings.

Custom PHP.ini options

PHP Selector allows customer to edit php.ini settings. Admin has a full control over which settings can be modified.

To allow settings to be modifiable, it has to be whitelisted in /etc/cl.selector/php.conf.

Here are some of the examples of allowed directives:

Directive = safe_mode
Type      = bool
Remark    = <5.4.0
Comment   = Enables PHP safe mode. This mode puts a number of restrictions on scripts (say, access to file system) mainly for security reasons.
Directive = safe_mode_include_dir
Type      = value
Remark    = <5.4.0
Comment   = If PHP is in the safe mode and a script tries to access some files, files from this directory will bypass security (UID/GID) checks. The directory must also be in include_path. For example: /dir/inc
Directivephp.ini setting
Typebool, value (any text), list
Rangelist of values for list Type
Commentexplanation of the setting to display in UI

Default values, that are shown in PHP Selector web interface, are taken from '/opt/alt/phpXX/usr/bin/php -i' runtime values, if directive is not there, it will use the output of phpinfo() function. So, if you wish to change default value of any option for "alternative" php version, please modify /opt/alt/phpXX/etc/php.ini files (where XX = 55, 54, 53, etc according to php version).

Admin can modify the settings using selectorctl command.

Users can use web interface to modify php.ini settings:

End user files and directories

The following files and directories are created inside CageFS for each customer:

/etc/cl.selector - PHP binaries symbolic links.

/usr/selector/php - Native PHP binaries.

/etc/cl.php.d/alt-php* - Links to enabled modules.

/home/user/.cl.selector/alt_phpXX.cfg - Config file for custom PHP options.

like:

/etc/cl.php.d/alt-php54/fileinfo.ini - /opt/alt/php54/etc/php.d.all/fileinfo.ini

Compiling your own extensions

Sometimes you might want to compile your own PHP extension for your users to use. In most cases, it is better to contact our support by sending us a support ticket . We will try to provide such extension for you via regular updates within 5-7 days.

If you have decided that you want to build it on your own, you would need to build it for each and every supported version of PHP that you have installed. The module installation process is a bit different from standard - you would need to use the version of phpize and php-config binaries that come with particular Alt-PHP version.

The full process for PHP 5.X and 7.X looks as follows:

  1. Download and unpack extension, cd into it's directory.

  2. Execute our version of phpize if necessary:

/opt/alt/phpXX/usr/bin/phpize
  1. Execute configure with our binary:
./configure --with-php-config=/opt/alt/phpXX/usr/bin/php-config
  1. Make the .so file:
make
  1. Copy it to the modules directory (on 32-bit server, use usr/lib/php/modules ).
cp -rp modules/*.so /opt/alt/phpXX/usr/lib64/php/modules/
  1. Add ini file for module to /opt/alt/phpXX/etc/php.d.all .

  2. Register new Alt-PHP version with:

cagefsctl --setup-cl-selector

Roll your own PHP

To add your own PHP version in PHP Selector :

  • Create directory in (like: /opt/alt/php51), and mimic directory structure inside to be similar to the one of PHP versions bundled by CloudLinux OS Shared.
  • Put all the ini files for all the modules into /opt/alt/php51/etc/php.d.all
  • Create a symbolic link /opt/alt/php51/etc/php.d -> /etc/cl.php.d/alt-php51

Place all such files into /opt/alt/php51/usr/lib/php/modules

Add an absolute path to PHP binaries into /etc/cl.selector/selector.conf using the following format:

php     5.1 5.1.2 /opt/alt/php51/usr/bin/php-cgi 
php-cli 5.1 5.1.2 /opt/alt/php51/usr/bin/php 
php-fpm 5.1 5.1.2 /opt/alt/php51/usr/sbin/php-fpm
   ^     ^    ^                ^----- absolute path
   |     |    |---------------------- real version
   |     | -------------------------- version to display
   |--------------------------------- binary to 'substitute'

Execute:

cagefsctl --setup-cl-selector

The new PHP version must be available now for selection in PHP Selector.

Detect user's PHP version

Note

LVE Manager 0.5-63 or higher

PHP Selector provides an easy way to figure out which versions are available and selected for end user from the command line. You can get this information by running:

selectorctl --interpreter=php --user-summary --user=USERNAME
The output:
5.2 e - -
5.3 e - s
5.4 e - -
5.5 e - -
native e d -

The first column defines the PHP version. Native means native PHP version, like the one installed by cPanel with EasyApache.

The second column will contain either e or -. If e is present, it means that given version is enabled, and can be selected by the end user.

The third column can have values d or -. If d is present, that version is considered a 'default' version. Only one PHP version will have d indicator.

The fourth column can have values s or -. If s is present, that is the selected version, currently being used by the end user. Only one PHP version will have s indicator.

In case a user is not inside CageFS, and as such doesn't use PHP Selector , you will see the following error message:

ERROR:User USERNAME not in CageFS

PHP Selector without CageFS

[LVE Manager 2.0-11.1 or higher]

PHP Selector can now be used with CageFS turned off (in case when there is only one user account on the server).

To install run:

yum groupinstall alt-php
yum install cagefs lvemanager

(no need to initialize or turn on CageFS)

selectorctl --setup-without-cagefs USER

( USER - the name of a user who is using selector. If not specified, the first available cPanel account username will be used).

When executing --setup-without-cagefs, the following actions are performed:

  • Creating symlinks to the user modules and options for each Alt-PHP version:
    /opt/alt/php55/link/conf/alt_php.ini -> /home/USER/.cl.selector/alt_php55.ini

  • In user home directory creating:
    .cl.selector/

“Backup” settings files (selected version, modules, options):
.cl.selector/defaults.cfg
.cl.selector/alt_php44.cfg

Symlinks to the selected version:
.cl.selector/lsphp -> /opt/alt/php44/usr/bin/lsphp
.cl.selector/php.ini -> /opt/alt/php44/etc/php.ini
.cl.selector/php-cli -> /opt/alt/php44/usr/bin/php
.cl.selector/php -> /opt/alt/php44/usr/bin/php-cgi

Additional symlinks for environment variable $PATH (search path) in the file ~/.bashrc :
.cl.selector/selector.path/
.cl.selector/selector.path/php-cgi -> ../php
.cl.selector/selector.path/php -> ../php-cli

Generated ini files with selected modules and options for each version: .cl.selector/alt_php44.ini
.cl.selector/alt_php51.ini
.cl.selector/alt_php52.ini
.cl.selector/alt_php53.ini
.cl.selector/alt_php54.ini
.cl.selector/alt_php55.ini
.cl.selector/alt_php56.ini
.cl.selector/alt_php70.ini
.cl.selector/alt_php71.ini

Symlinks above are being created according to the settings in ~/.cl.selector/defaults.cfg and ~/.cl.selector/alt_php44.cfg files (44 - corresponding PHP version), which are storing PHP Selector settings for the user. These files are usually taken from user home directory backup or when migrating account from another server. Thus, when migrating account from server to server, PHP Selector settings are saved.

If no PHP Selector settings backup files are found when running selectorctl --setup-without-cagefs, then default settings from /etc/cl.selector/defaults.cfg global file are applied (as in selector normal mode). If the file is absent, then native PHP version will be selected for the user.

  • The following line: PATH=$HOME/.cl.selector/selector.path:$HOME/.cl.selector:$PATH

is being added to the user file ~/.bashrc

Apache PHP handlers settings are not changed.

  • Also selectorctl --setup-without-cagefs command does the following:

    • Turns off link traversal protection (linksafe);
    • Turns off cagefs service.

To get back to the selector normal mode (“with CageFS”) run:

selectorctl --revert-to-cagefs

(CageFS should be initialized by using cagefsctl --init command before running the command above)

This command removes symlinks:
/opt/alt/php55/link/conf/alt_php.ini -> /home/USER/.cl.selector/alt_php55.ini turns on link traversal protection (linksafe) and cagefs service.

Note

See also PHP Selector CLI

Configuring "global” php.ini options for all Alt-PHP versions

Note

CageFS 6.0-33 or higher, LVE Manager 2.0-11.2 or higher

There is /etc/cl.selector/global_php.ini file, where you can specify values of PHP options that should be applied for all Alt-PHP versions that are installed on a server. These settings will also be automatically applied to the new Alt-PHP versions that will be installed later.

Example:
# cat /etc/cl.selector/global_php.ini
[Global PHP Settings]
date.timezone = Europe/Warsaw
error_log = error_log
memory_limit = 192M
Sections are ignored. Only name of an option and a value have meaning.

When an option is absent in /etc/cl.selector/global_php.ini file, than it is not changed (applied) to php.ini for Alt-PHP versions.

date.timezone and error_log options are handled differently than the others. When these options are not in /etc/cl.selector/global_php.ini file, than values for the options will be taken from "native" php.ini file. And when the option is in php.ini for some Alt-PHP version already (and its value is not empty), than value from /etc/cl.selector/global_php.ini will be NOT applied.

Note

CageFS version 6.1.5-1 or later

The behavior above is changed for cPanel servers with EasyApache 4. The /usr/local/lib/php.ini file is removed for new installations of cPanel v80 and later.

  • When /usr/local/lib/php.ini file exists, error_log and date.timezone options will be taken from that php.ini file.
  • When /usr/local/lib/php.ini file does not exist, error_log and date.timezone options will be taken from the php.ini file for system default PHP version selected in MultiPHP Manager.

This functionality works when the system default PHP version is ea-php only. When the system default PHP version is alt-php, error_log and date.timezone directives will be NOT taken from that php.ini file.

To confirm changes (not affecting "date.timezone" and "error_log" options) please run:

/usr/sbin/cagefsctl --setup-cl-selector

To confirm changes (including date.timezone and error_log options) please run:

/usr/bin/selectorctl --apply-global-php-ini
or
/usr/sbin/cagefsctl --apply-global-php-ini
(two commands above work the same way).

If you don't want to change error_log, but want to change date.timezone, you can execute:

selectorctl --apply-global-php-ini date.timezone

Similarly, command selectorctl --apply-global-php-ini error_log applies error_log and all other options specified in /etc/cl.selector/global_php.ini file, except date.timezone .

So, you can specify 0, 1 or 2 parameters from the list: error_log, date.timezone .

Using --apply-global-php-ini without arguments applies all global PHP options including two above.

Example:

selectorctl --apply-global-php-ini error_log
selectorctl --apply-global-php-ini date.timezone
selectorctl --apply-global-php-ini date.timezone error_log

The latter command has the same effect as /usr/bin/selectorctl --apply-global-php-ini.

Note

See also PHP Selector CLI

Integration with control panels

This is the list of commands that we use to integrate PHP Selector with control panels.

PHP summary:

Command:

/usr/bin/selectorctl --summary
Result:
4.4 e -
5.1 e -
5.2 e -
5.3 e -
5.4 e -
5.5 e -
5.6 e -
7.0 e -
7.1 e -
native e d
When native PHP version needs to be displayed:

Command:

/usr/bin/selectorctl --summary --show-native-version

Result:

4.4 e -
5.1 e -
5.2 e -
5.3 e -
5.4 e -
5.5 e -
5.6 e -
7.0 e -
7.1 e -
native(5.6) e d

The first column: PHP version
The second column: enabled or not ( e - enabled)
The third column: if selected as default ( d - default)

Set default version:

/usr/bin/selectorctl --set-current=_VERSION_

Disable version:

/usr/bin/selectorctl --disable-alternative=_VERSION_

Enable version:

/usr/bin/selectorctl --enable-alternative=_VERSION_

List extensions for a version:

/usr/bin/selectorctl --list-extensions --version=5.6

Result:

- apc
- bcmath
- big_int
- bitset
- bloomy
~ bz2
- bz2_filter
~ calendar
- coin_acceptor
- crack
~ ctype
+ curl

+: enabled
~: included in php binary (cannot be disabled) or enabled in php global config file /opt/alt/phpXX/etc/php.ini
-: disabled

Select default extensions (enable comma-separated list of extensions globally for a version):

/usr/bin/selectorctl --version=5.6 --enable-extensions=pdo,json,mysql

Deselect default extensions (disable comma-separated list of extensions globally for a version):

/usr/bin/selectorctl --version=5.6 --disable-extensions=pdo,json,mysql

Replace extensions with comma-separated list of extensions for a version globally:

/usr/bin/selectorctl --version=5.6 --replace-extensions=pdo,json,mysql

Select PHP version for a user:

/usr/bin/selectorctl --set-user-current=_VERSION_ --user=_USER_

List enabled extensions for a user:

/usr/bin/selectorctl --list-user-extensions --user=_USER_ --version=_VERSION_

Enable comma-separated list of extensions for a user:

/usr/bin/selectorctl --enable-user-extensions=pdo,json,mysql --user=_USER_ --version=_VERSION_

Reset user’s extensions to defaults:

/usr/bin/selectorctl --reset-user-extensions --user=_USER_ --version=_VERSION_

Replace user extensions with comma-separated list of extensions:

/usr/bin/selectorctl --replace-user-extensions=EXT_LIST --user=_USER_ --version=_VERSION_

EXT_LIST a is comma separated list of PHP extensions (for example: pdo,json,mysql )

List available options for php.ini editing:

/usr/bin/selectorctl --print-options --user=_USER_ --version=_VERSION_ [--json]

List available options for php.ini editing (print safe strings):

/usr/bin/selectorctl --print-options-safe --user=_USER_ --version=_VERSION_ [--json]

Set php.ini options for end user:

/usr/bin/selectorctl --user=_USER_ --version=_VERSION_ --replace-options=_OPTIONS_ --base64 [--json]

Here is an example of how you can generate OPTIONS in base64 format:

OPTIONS=`echo disable_functions:exec,syslog|base64 -w 0`,`echo display_errors:off|base64 -w 0`,`echo post_max_size:128M|base64 -w 0`
echo $OPTIONS

See also PHP Selector CLI tools.

Bundled PHP extensions

You can find this information in the section List of extensions supported by the alt&ea team for each version of PHP

Python Selector

General information

Python Selector is a CloudLinux OS Shared component that allows each user to easily deploy and manage Python applications. Users can manage applications at the control panel interface level or from the command line (CLI).

Requirements

  • cPanel or DirectAdmin control panel. Plesk isn't supported.
  • Apache or LiteSpeed webserver. Apache + Nginx (as a reverse proxy) combination is also supported.
  • Python Selector uses mod_passenger to host Python. For more details about mod_passenger, please read documentation. The needed version will be installed along with Python Selector.

Note!

OpenLiteSpeed is not compatible with Python Selector due to lack of .htaccess support.

Supported versions

Installation

You can install Python Selector using the CloudLinux OS Shared Installation Wizard.

After installation, please make sure that you have set appropriate checkboxes in CloudLinux Manager Options tab to show Python App in the web-interface.

Manual installation

Here you can find the installation steps for supported control panels:

cPanel

To use Python Selector, it is required to install the following:

  • alternative Python packages by running the following command:
yum groupinstall alt-python
  • CloudLinux Manager, LVE Utils and Phusion Passenger to create isolated Python environments by running the following command:
yum install lvemanager lve-utils alt-python-virtualenv

Phusion Passenger could be installed by using either ea-ruby24-mod_passenger or ea-ruby27-mod_passenger. ea-ruby27-mod_passenger is not compatible with systems running CloudLinux OS Shared OS 6. CloudLinux OS Shared OS 7 supports both ea-ruby24-mod_passenger and ea-ruby27-mod_passenger. If your system runs CloudLinux OS Shared OS 8, you can only use ea-ruby27-mod_passenger.

Adding Python module requires executing permissions to gcc/make binaries. Please enable compilers in Compiler Access section of WHM, then run:

cagefsctl --force-update

DirectAdmin

To use Python Selector, it is required to install the following:

  • alternative Python packages by running the following command:
yum groupinstall alt-python
  • CloudLinux Manager, LVE Utils and Phusion Passenger to create isolated Python environments by running the following command:
yum install lve-utils lvemanager alt-python-virtualenv alt-mod-passenger

Ruby Selector

General information and requirements

We have the ability to deploy Ruby applications via an application server. Ruby Selector uses mod_passenger to host Ruby applications.

Ruby Selector works only on cPanel/WHM servers.

Supported versions

Installation and update

Note

The instructions below are suitable only for EasyApache 3 and EasyApache 4. You should follow this instruction if you use LiteSpeed.

To use Ruby Selector install alternative Ruby packages:

yum groupinstall alt-ruby 

Note

After installation, please make sure that you have unmarked appropriate checkboxes in VE Manager Options tab to show Ruby App in web-interface. Find the instructions on the link.

Note

Adding Ruby modules requires executing permissions to gcc/make binaries. Please enable compilers in Compiler Access section of WHM, then run: cagefsctl --force-update

Configuration and using

End user access

You can find an example of Ruby application setup here

Note

See also Ruby Selector CLI section.

Hide Ruby Selector icon

It is possible to hide or show Ruby Selector icon by marking or unmarking proper checkboxes in LVE Manager Options tab.

The same result can be accomplished in CLI by running:

cloudlinux-config set --json --data '{"options":{"uiSettings":{"hideRubyApp":false}}}'

Note

If you are using cPanel/WHM, you can also configure hide/show CloudLinux OS Shared Ruby Selectors in WHM | Feature Manager. For that, you’d need to first uncheck Hide Ruby App in web-interface in the LVE Manager. This will make the menu appear for all accounts. After that, you are free to disable this app in WHM | Feature Manager for the required feature lists.

Deploying Redmine using Ruby Selector

Note

You can find Redmine version 2.6.0 and newer deployment guide here

EasyApache 4

Note

ea-apache24-mod-alt-passenger is deprecated in favor of ea-ruby24-mod_passenger and ea-ruby27-mod_passenger

Starting with cPanel/WHM version 66 provides ea-ruby24-mod_passenger (more information on the link), this allows creating Ruby applications with cPanel application manager.

CloudLinux OS Shared features its own Python and Ruby Selectors, which allow creating applications with one of mod_passenger Apache modules:

  • ea-ruby24-mod_passenger
  • ea-ruby27-mod_passenger

ea-ruby27-mod_passenger is not compatible with systems running CloudLinux OS Shared 6. CloudLinux OS Shared 7 supports both ea-ruby24-mod_passenger and ea-ruby27-mod_passenger. If your system runs CloudLinux OS 8, you can only use ea-ruby27-mod_passenger.

The ea-ruby2X-mod_passenger allows you to run applications via cPanel application manager and Ruby Selector.

To install, run:

yum install lvemanager alt-python-virtualenv
yum install ea-ruby24-mod_passenger

or:

yum install lvemanager alt-python-virtualenv
yum install ea-ruby27-mod_passenger

See also Ruby Selector CLI tools.

Node.js Selector

General information and requirements

Node.js Selector is a CloudLinux OS Shared component that allows each user to easily create Node.js applications, choose Node.js version and other parameters for applications based on their needs.

Supported versions

Requirements

  • Node.js Selector requires LVE Manager 4.0 or later.
  • It supports cPanel and DirectAdmin servers as well as non-panel installations (Plesk is not supported as it already has Node.js support.) For more details, please go to Plesk & Node.js documentation here and here .
  • Node.js Selector uses mod_passenger to host Node.js. For more details about mod_passenger and Node.js, please read documentation here and here .
  • Node.js Selector supports both EasyApache 3 and EasyApache 4.

Note!

OpenLiteSpeed is not compatible with Node.js Selector due to lack of .htaccess support.

Limitations

Since Node.js Selector uses mod_passenger to host Node.js, then Node.js Selector has the same limitations as Phusion Passenger. Phusion Passenger cannot load ECMAScript modules and can only load Common js modules. If you get an ERR_REQUIRE_ESM error when starting an application app.js.

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: app.js

In this case you can use the following cjs wrapper app_wrapper.cjs to load esm script app.js:

cat app_wrapper.cjs
(() => import('app.js'))();

Installation and update

Note

Node.js Selector support is added to LiteSpeed Web Server starting from the 5.3RC1 release. See details here.

cPanel

To use Node.js Selector, install Node.js packages by running the following command:

yum groupinstall alt-nodejs
Also, please install LVE Manager, LVE Utils and Phusion Passenger by running the following command:
yum install lvemanager lve-utils

Phusion Passenger could be installed by using either ea-ruby24-mod_passenger or ea-ruby27-mod_passenger. ea-ruby27-mod_passenger is not compatible with systems running CloudLinux OS Shared 6. CloudLinux OS Shared 7 supports both ea-ruby24-mod_passenger and ea-ruby27-mod_passenger. If your system runs CloudLinux OS Shared 8, you can only use ea-ruby27-mod_passenger.

Note

ea-apache24-mod-alt-passenger is deprecated in favor of ea-ruby24-mod_passenger and ea-ruby27-mod_passenger

For EasyApache 3:

yum install lvemanager lve-utils alt-mod-passenger
And we recommend to install CageFS for better security (not mandatory) by running the following command:
yum install cagefs

Note

If during Node.js Selector usage on cPanel servers you get "ENOMEM npm ERR! errno-12" error, try to increase Memory limit in cPanel WHM → Server Configuration → Tweak Settings → System → Max cPanel process memory, then restart cPanel service with the following command to apply changes.

CloudLinux OS Shared 7:

systemctl restart cpanel.service

CloudLinux OS Shared 6:

``` service cpanel restart ```

DirectAdmin

To use Node.js Selector, please install Node.js packages by running the following command:

yum groupinstall alt-nodejs6 alt-nodejs8 alt-nodejs9
Also, please install LVE Manager, LVE Utils and Fusion Passenger by running the following command:
yum install lvemanager lve-utils alt-mod-passenger
And we recommend to install CageFS for better security (not mandatory) by running the following command:
yum install cagefs

Node.js deployment

Remote usage of Node.js interpreters

  1. Create a Node.js project in IntelliJ IDEA/WebStorm . You can download this archive and use it as a basis.
  2. Install alt-nodejs packages on the server in use. See installation instructions.
  3. Create an application on the server. You can do it by three ways:
  • Via UI of the Node.js plugin.
  • Using the following command to create an application:
cloudlinux-selector create --interprete=nodejs --json --app-root=<USER_NAME> --app-uri=<APP_NAME> --app-mode=develompent --version=<VERSION> --domain=<DOMAIN>

Note

In the IntelliJ IDEA you can create and run any remote script (Preferences — Remote SSH External Tools — Add).

  • Choose a location of the application on the server and synchronize the files with the IntelliJ IDEA project.
  1. Set up Run/Debug Configurations in the project created.

  • Specify a path to the remote Node.js interpreter. To be able to specify the remote interpreter, you should install the Node.js Remote Interpreter plugin first. Please find more information here , using server access credentials for a user (Main menu → Run → Edit configurations...) .
  • Specify initial JavaScript file that will be run with the node command (it is the app.js file from the archive).
  • Specify Path Mappings between a local and a remote project (Preferences → Deployments → Add) . If you have created your application with the cloudlinux-selector utility or via plugin UI the Path Mappings should be as follows:
/home/<USER_NAME>/<APP_NAME>
  1. Synchronize the project directories on the local and the remote machine as per Path Mappings specified.
  2. Deploy the modules on the remote and the local machine with the npm install command (if there are dependent modules). In the UI you can click the Run NPM Install button.
  3. Run Node.js application with the configuration set at the 4th step (Main menu → Run → Run… → Select configuration) .

  1. If you are using the application from the archive attached, you can see the running application on port 3003 — http://DOMAIN:3003 .

Note

The port should be available to a server user.

The following information should be displayed on this page:

  • A version of the running Node.js interpreter;
  • Current environment variables;
  • The current time.

So that, you can be sure that deployed modules are used properly.

If you’d like to use a different version of Node.js to run an application, change a path to the interpreter in the configuration settings of the running.
To apply all changes to the project, synchronize all changes with the server and restart the running application.

  1. To debug a script, set breakpoints in the code and run the configuration via Main Menu (Main menu → Run → Debug… → Select configuration) .

Useful links:

Note

It is not required to install Passenger while working in IDE if you are using this approach.

Remote usage of the cloudlinux-selector utility

  1. Create an application via UI or with the command as described in the Remote Usage of Node.js Interpreters approach, step 3 (a,b).
  2. Set up project mapping on the local machine with the created remote application /home/<USER_NAME>/<APP_NAME> (Preferences → Deployments → Add).
  3. Set up the remote commands of cloudlinux-selector (Preferences → Remote SSH External Tools → Add) for the following actions:
  • Restart application;

  • Install packages;

  • Run script;

  • Change Node.js version for the application. You can see the running app at http://DOMAIN/APPLICATION_URL To apply all changes, restart the application.

  • See also Node.js Selector CLI tools.

  • See also Node.js Selector UI.

Troubleshooting

Debugging errors

Since alt-mod-passenger-5.3.7-2, directives such as PassengerFriendlyErrorPages and PassengerAppEnv are available for use from htaccess file. This allows end users to see errors from their application during the development process. For example, if you add one of the following lines to the htaccess file on the application page, you will see the information (if there was an error) similar to one on the picture.

PassengerAppEnv development
or
PassengerFriendlyErrorPages on

This is a much more convenient approach to developing an application and debugging errors. On the other hand, if these directives are turned off you will see:

In this case, there is no useful information for debugging errors and this is suitable for production mode. More information about PassengerFriendlyErrorPages and PassengerAppEnv.

Known Restrictions and Issues

  1. In some cases running commands e.g. npm build can produce an Out Of Memory error.

    When the error is encountered in Nodejs Selector UI or in the web browser terminal, check the physical and virtual memory LVE limits. Also check the process limits through ulimit -v -m command in Terminal UI or "Max data size", "Max address space" or "Max resident set" values inside the /proc/<process_id>/limits file. Increase the limits if necessary.

    On cPanel, increase the default process memory limit:

    whmapi1 set_tweaksetting key=maxmem value=8192
    /usr/local/cpanel/scripts/restartsrv_cpsrvd
    

    Due to bug CPANEL-43590 a server reload might be required for the new maxmem value to become applied.

    In some cases, due to bugs in underlying Node.js frameworks (e.g. Out of memory: wasm memory) it could be necessary to increase maxmem up to 50 Gb (value=51200) or even to unlimited.

  2. Sometimes an Out Of Memory error appears in the web browser terminal in cPanel. Because of CPANEL-39397 bug terminal inherences limits of cpsrvd process. As a workaround, you could increase maxmem value as described above.

    Alternetive workaround: override soft and hard virtual memory limit only for the user encountering the problem. Override hard limit by adding the following line to the /etc/security/limits.conf file:

    username	hard	as	unlimited
    

    Override soft limit by adding into $USER_HOME/.bashrc:

    ulimit -S -v unlimited
    

Apache mod_lsapi PRO

General information and requirements

mod_lsapi PRO is an Apache HTTP Server module based on LiteSpeed Technologies API . It serves to execute PHP scripts on a web-server by analogy with other modules like mod_suphp, php-fpm, mod_php. However, mod_lsapi PRO usage offers excellent PHP performance, low memory footprint coupled with great security and support for opcode caching.

How does it work?

  1. Apache passes handling for PHP request to mod_lsapi PRO;
  2. mod_lsapi PRO use liblsapi to transfer request to lsphp parent process;
  3. lsphp is forking child process which executes the request and returns data to mod_lsapi PRO;
    mod_lsapi PRO integrates with Apache, allows to handle concurrent requests and manages the lsphp processes
  • If there are no requests for lsapi_backend_pgrp_max_idle seconds, lsphp parent process will be  terminated;
  • If there are no lsphp child processes available when a new request comes, the new lsphp child process will be created;
  • lsphp childs process concurrent requests simultaneously;
  • The maximum number of simultaneously running lsphp child processes can be set by the lsapi_backend_children directive.

What is lsphp?

lsphp - PHP + LSAPI. What is PHP LSAPI? LiteSpeed Server Application Programming Interface (LSAPI) is designed specifically for seamless, optimized communication between LiteSpeed Web Server and third-party web applications. Now with mod_lsapi PRO this protocol is available for Apache 2.2/2.4.

Using mod_lsapi PRO, we have seen the higher performance than Apache with mod_php, easier installation than php-fpm and easier integration with any control panel. mod_lsapi PRO means faster and more stable dynamic web pages.

Requirements

Currently, the mod_lsapi is not compatible with:

  • Apache mod_ruid2 - should be disabled;
  • Apache mod_itk - should be disabled;
  • PHP-FPM - should be disabled because PHP-FPM is also a PHP Handler just as mod_lsapi.

Optional requirements

  • Configured LVE containers for end-users ( recommended - higher security level );
  • Installed and configured mod_hostinglimitsfor Apache ( recommended - higher security level );
  • Installed mod_suexec for Apache and configured SuExecUserGroup directive for each virtual host ( recommended - higher security level );
  • Enabled CageFS for end-users ( recommended - higher security level );
  • PHP Selector with alt-php - an easy way to select different PHP versions for each end-user provided by CloudLinux OS Shared;
  • ea-php - alternative to alt-php provided by cPanel (for cPanel only).

Installation

mod_lsapi PRO can be installed through YUM package manager, however, the installation process varies depending on the control panel.

Select the control panel you are using:

Note

See also mod_lsapi PRO.

Installing on cPanel servers with EasyApache 4

Install mod_lsapi PRO and related packages through YUM package manager as follows:

yum install liblsapi liblsapi-devel
yum install ea-apache24-mod_lsapi
After installing mod_lsapi PRO packages run the next command to setup mod_lsapi to cPanel:
/usr/bin/switch_mod_lsapi --setup
Now, when the module is installed, restart Apache to ensure that the mod_lsapi PRO is enabled:
service httpd restart
Now the lsapi handler is available for managing through cPanel MultiPHP Manager.

For more details about switch_mod_lsapi, please visit switch_mod_lsapi tool.

Installing on Plesk servers

Install mod_lsapi PRO and related packages through YUM package manager as follows:

yum install liblsapi liblsapi-devel
yum install mod_lsapi
Once completed, run the command to setup mod_lsapi PRO and register LSPHP handlers to Plesk Panel:
/usr/bin/switch_mod_lsapi --setup

Now, when the module is installed, restart Apache to ensure that mod_lsapi PRO is enabled:

service httpd restart

Now LSPHPXY alt-php PHP handlers are available for managing through Plesk PHP Settings.

For more details about switch_mod_lsapi, please visit switch_mod_lsapi tool.

Installing on DirectAdmin servers

Installation process is done with custombuild script:

yum install liblsapi liblsapi-devel
cd /usr/local/directadmin/custombuild
./build update
./build set php1_mode lsphp
./build php n
./build apache
Restart Apache afterwards:
service httpd restart
Now all domains under php1_mode are using lsphp handler and no further actions are required to enable mod_lsapi PRO on DirectAdmin.

Installing on servers with no control panel

Install mod_lsapi PRO and related packages through YUM package manager as follows:

yum install liblsapi liblsapi-devel
yum install mod_lsapi
Once completed, run a command to setup mod_lsapi PRO:
/usr/bin/switch_mod_lsapi --setup
Now, when the module is installed, restart Apache to ensure that mod_lsapi PRO is enabled:
service httpd restart

If you are using an alternative Apache - httpd24 , then install mod_lsapi as follows:

yum install liblsapi liblsapi-devel
yum install httpd24-mod_lsapi
Once completed, run a command to setup mod_lsapi PRO:
/opt/rh/httpd24/root/usr/bin/switch_mod_lsapi --setup
Now, when the module is installed, restart Apache to ensure that mod_lsapi PRO is enabled:
service httpd24-httpd restart

For more details about switch_mod_lsapi, please visit switch_mod_lsapi tool.

Uninstalling

Uninstall mod_lsapi PRO is performed depending on your control panel.

Select the control panel you are using:

Uninstall procedure for cPanel servers with EasyApache 4

To remove lsapi handler from cPanel MultiPHP Manager and uninstall mod_lsapi PRO, run a command:

/usr/bin/switch_mod_lsapi --uninstall
Then remove packages with YUM package manager:
yum erase liblsapi liblsapi-devel ea-apache24-mod_lsapi
Restart Apache afterwards:
service httpd restart
Now mod_lsapi PRO is fully uninstalled.

Uninstall procedure for Plesk servers

To unregister LSPHP handlers and uninstall mod_lsapi PRO, run the command:

/usr/bin/switch_mod_lsapi --uninstall
Then remove packages with YUM package manager:
yum erase liblsapi liblsapi-devel mod_lsapi
Restart Apache afterwards:
service httpd restart
Now LSPHPXY alt-php PHP handlers and mod_lsapi PRO are fully uninstalled.

Uninstall procedure for DirectAdmin servers

Uninstall is done with custombuild script:

cd /usr/local/directadmin/custombuild
./build update
./build set php1_release [any other php mode]
./build php n
./build apache
The following PHP modes are available for DirectAdmin:
  • mod_php
  • fastcgi
  • php-fpm
  • suphp

Restart Apache afterwards:

service httpd restart
Now all domains under php1_mode are using the chosen handler and mod_lsapi PRO is fully uninstalled.

Uninstall procedure for servers with no control panel

To uninstall mod_lsapi PRO, run the command:

/usr/bin/switch_mod_lsapi --uninstall
Then remove packages with YUM package manager:
yum erase liblsapi liblsapi-devel mod_lsapi
rm [path to mod_lsapi.conf]
Restart Apache to restore the standard PHP handler:
service httpd restart

If you are using an alternative Apache: - httpd24, then uninstall mod_lsapi PRO as follows:

/usr/bin/switch_mod_lsapi --uninstall
Then remove packages with YUM package manager:
yum erase liblsapi liblsapi-devel httpd24-mod_lsapi
rm [path to mod_lsapi.conf]
Restart Apache afterwards:
service httpd24 restart
Now mod_lsapi PRO is fully uninstalled.

Configuration

In order to get mod_lsapi PRO work properly, you'll need to configure Apache. To do this, we use a separate lsapi.conf file.

First of all, for the mod_lsapi PRO work, you need to ensure that the module is loaded. In your lsapi.conf you need to make sure the LoadModule directive has not been commented out. A correctly configured directive may look like this:

LoadModule lsapi_module modules/mod_lsapi.so

In order to enable the module to process requests, you need to add the lsapi_engine directive to your lsapi.conf file as follows:

lsapi_engine On

The mod_lsapi PRO handler can be enabled using the AddType directive. The AddType directive tells Apache that a given filename extension should be handled by mod_lsapi PRO. Apache will assume that and will attempt to execute it when that particular resource is requested by a client.

AddType application/x-httpd-lsphp .php

If no handler is explicitly set for a request, the specified content type will be used as the handler name, therefore, please disable php.conf or any other PHP handler for using mod_lsapi PRO. In this example application/x-httpd-lsphp is a default handler by which mod_lsapi PRO process requests with lsphp binary from /usr/local/bin/ directory.

The final lsapi.conf configuration might look like this:

LoadModule lsapi_module modules/mod_lsapi.so


<IfModule lsapi_module>      
	lsapi_engine On      
	AddType application/x-httpd-lsphp .php      
	lsapi_backend_connect_timeout 100000
	lsapi_backend_connect_tries 10
	lsapi_backend_children 20
	lsapi_backend_pgrp_max_idle 30
	lsapi_backend_max_process_time 300
	lsapi_debug Off
</IfModule>

In order to mod_lsapi PRO work lsapi.conf should be loaded to Apache through Include directive.

For more detailed description of the module directives please visit Configuration reference.
For installation guide mod_lsapi PRO please visit Installation.

Configuration references

mod_lsapi customization:

Tuning LSPHP backend:

Connection pool mode:

CRIU support:

PHP configuration management:

Security:

mod_lsapi customization

lsapi_engine

Syntax : lsapi_engine on/off
Default : lsapi_engine off
Context : httpd.conf, htaccess

Description :
Switching mod_lsapi handler on or off.


lsapi_socket_path

Syntax : lsapi_socket_path [path] Default : lsapi_socket_path /var/mod_lsapi
Context : httpd.conf

Description:
Path to backend lsphp sockets. By default /var/mod_lsapi


lsapi_poll_timeout

Syntax : lsapi_poll_timeout [number]
Default : lsapi_poll_timeout 300
Context : httpd.conf, htaccess

Description :
Time to wait for response from the lsphp daemon, in seconds. 0 stands for infinity. For preventing long running processes which can use EP (limit number of entry processes). Default value is 300. Should be more or equal to 0. In the case of wrong format, the default value will be used.


lsapi_per_user

Syntax : lsapi_per_user On/Off
Default : lsapi_per_user Off
Context : httpd.conf

Description :
Invoke master lsPHP process not per VirtualHost but per account. When On, invoke backend not per VirtualHost but per account. Default value is Off. It is possible, for example, to set it to On in global config file and to Off in config files of some particular Virtual Hosts. Then these Virtual Hosts will have a dedicated backend process, while others will have backend processes shared on account basis.


lsapi_output_buffering

Syntax : lsapi_output_buffering On/Off
Default : lsapi_output_buffering On
Context : httpd.conf, virtualhost, htaccess

Description :
Enable or disable output buffering on Apache level. Default value is On.


lsapi_disable_reject_mode

Syntax : lsapi_disable_reject_mode On/Off
Default : lsapi_disable_reject_mode Off
Context : httpd.conf, virtualhost

Description :
If a new HTTP request is coming to LSPHP daemon when all LSPHP workers are still busy, it can process this situation in two different ways. In REJECT mode LSPHP daemon will reject such request immediately. Otherwise, in legacy mode, LSPHP daemon will put this request into infinite queue, until one or more LSPHP daemon becomes free. When HTTP request is rejected in REJECT mode, mod_lsapi will write into Apache error_log the following message: Connect to backend rejected, and the client will receive 507 HTTP response. By default LSPHP daemon in CloudLinux OS Shared uses REJECT mode. It can be switched off with this option.


lsapi_terminate_backends_on_exit

Syntax : lsapi_terminate_backends_on_exit On/Off
Default : lsapi_terminate_backends_on_exit On
Context : httpd.conf

Description :
httpd.conf, On - stop lsphp services on apache restart, Off - leave live started lsphp services on apache restart (for php+opcache). The lsphp will not restart, even if Apache gets restarted.


lsapi_avoid_zombies

Syntax : lsapi_avoid_zombies On/Off
Default : lsapi_avoid_zombies Off
Context : httpd.conf, virtualhost

Description :
Enable or disable a mechanism to avoid creation of zombie processes by lsphp. Default value is Off.


lsapi_use_req_hostname

Syntax : lsapi_use_req_hostname On/Off
Default : lsapi_use_req_hostname Off
Context : httpd.conf, virtualhosts

Description :
By default, we are using hostname from the server_rec structure (off), it means that mod_lsapi takes hostname from the VirtualHost section of the configuration file. Using hostname from the request_rec structure (On) means that mod_lsapi takes hostname from the HOST section of the request. It could be useful for those who use dynamically generated configs for virtual hosts for example with mod_lua.


lsapi_sentry

Syntax : lsapi_sentry On/Off
Default : lsapi_sentry On
Context : httpd.conf

Description :
When this option is enabled, errors that occur in the operation of the mod_lsapi will be sent to the remote sentry server. You can see the error messages that were sent to the sentry server in the directory /var/log/mod_lsapi. If you do not want to send error notifications from your server, you can disable this directive in lsapi.conf.


lsapi_debug

Syntax : lsapi_debug On/Off
Default : lsapi_debug Off
Context : httpd.conf, virtualhost

Description :
Extended debug logging.


Tuning LSPHP backend

lsapi_set_env

Syntax : lsapi_set_env VAR_NAME [VAR_VALUE]
Default : -
Context : httpd.conf

Description :
Pass env variable to lsphp. By default lsphp environment have only TEMP, TMP and PATH variables set.
Example: lsapi_set_env TMP "/var/lsphp-tmp"
Note: PATH env var default "/usr/local/bin:/usr/bin:/bin" cannot be changed because of security reasons.
To change it, use explicitly lsapi_set_env_path option.

Beta

When the lsapi_server_tweak option is switched On, this option can be used in the virtualhost context.


lsapi_set_env_path

Syntax : lsapi_set_env_path [path(s)]
Default : lsapi_set_env_path /usr/local/bin:/usr/bin:/bin
Context : httpd.conf

Description :
Change PATH variable in the environment of lsPHP processes. Default path /usr/local/bin:/usr/bin:/bin will be used if not set.

Beta

When the lsapi_server_tweak option is switched On, this option can be used in the virtualhost context.


lsapi_backend_children

Syntax : lsapi_backend_children [number]
Default : lsapi_backend_children [EP]
Context : httpd.conf

Description :
Sets maximum number of simultaneously running child backend processes. Optional, a default directive value is equal to 120. LSAPI_CHILDREN environment variable is set according to the following rules:

  • If NPROC and EP are unlimited, the directive value is used.
  • If NPROC is set to any limited value and the directive value is set to a number less than NPROC-1, the directive value is used.
  • If the value is bigger than NPROC-1 and EP is set to unlimited, NPROC-1 is used.
  • If the value is bigger than NPROC-1 and EP is set to any limited value, EP+1 is used.

For example, with the default lve settings NPROC=100 and EP=20, the effective LSAPI_CHILDREN will be EP+1, that is LSAPI_CHILDREN=21.


lsapi_backend_connect_tries

Syntax : lsapi_backend_connect_tries [number]
Default : lsapi_backend_connect_tries 20
Context : httpd.conf

Description :
Number of retries to connects to lsPHP daemon.


lsapi_backend_connect_timeout

Syntax : lsapi_backend_connect_timeout [number]
Default : lsapi_backend_connect_timeout 500000
Context : httpd.conf

Description :
Number of usec to wait while lsPHP starts (if not started on request).


lsapi_backend_max_process_time

Syntax : lsapi_backend_max_process_time [number]
Default : lsapi_backend_max_process_time 300
Context : httpd.conf, virtualhost

Description :
Sets env variable LSAPI_MAX_PROCESS_TIME
Optional. Default value is 300.
Timeout to kill runaway processes.


lsapi_backend_pgrp_max_idle

Syntax : lsapi_backend_pgrp_max_idle [number]
Default : lsapi_backend_pgrp_max_idle 30
Context : httpd.conf

Description :
Sets env variable LSAPI_PGRP_MAX_IDLE, in seconds.
Controls how long a control process will wait for a new request before it exits. # 0 stands for infinite.
Optional, default value is 30.
Should be more or equal to 0.


lsapi_backend_use_own_log

Syntax : lsapi_backend_use_own_log On/Off
Default : lsapi_backend_use_own_log Off
Context : httpd.conf, virtualhost

Description :
Redirecting log output of backend processes from Apache error_log to dedicated log file or files, depending on value of lsapi_backend_common_own_log option. If Off, use Apache error log file for backend output, or separate file otherwise.


lsapi_backend_common_own_log

Syntax : lsapi_backend_common_own_log On/Off
Default : lsapi_backend_common_own_log Off
Context : httpd.conf, virtualhost

Description :
It will be used only when lsapi_backend_use_own_log set to On. On - backend processes of the all virtual hosts will share the common log file. Off - every virtual host will have its own backend log file.


lsapi_backend_coredump

Syntax : lsapi_backend_coredump On/Off
Default : lsapi_backend_coredump Off
Context : httpd.conf, htaccess

Description :
env variable LSAPI_ALLOW_CORE_DUMP (On or Off). Pass LSAPI_ALLOW_CORE_DUMP to lsphp or not. If it will be passed - core dump on lsphp crash will be created.
Off by default.
By default LSAPI application will not leave a core dump file when crashed. If you want to have LSAPI PHP dump a core file, you should set this environment variable. If set, regardless the value has been set to, core files will be created under the directory that the PHP script in.


lsapi_backend_accept_notify

Syntax : lsapi_backend_accept_notify On/Off
Default : lsapi_backend_accept_notify On
Context : httpd.conf, virtualhost

Description :
Switch LSAPI_ACCEPT_NOTIFY mode for lsphp. This option can be used both in Global and VirtualHost scopes.This mode is incompatible with PHP 4.4.


lsapi_backend_pgrp_max_reqs

Syntax : lsapi_backend_prgrp_max_reqs [number]
Default : lsapi_backend_max_reqs 0
Context : httpd.conf, virtualhost

Description :
Controls how many requests a control process will process before it exits. Should be more or equal to 0. In the case of wrong format, a default value will be used. Optional, the default value is 0, which means an unlimited number of requests.


lsapi_backend_pgrp_max_crashes

Syntax : lsapi_backend_prgrp_max_crashes [number]
Default : lsapi_backend_max_crashes 0
Context : httpd.conf, virtualhost

Description :
Controls how many crashes of its worker processes a control process will detect before it exits. Should be more or equal to 0. In the case of wrong format, a default value will be used. Optional, the default value is 0, which means an unlimited number of crashes.


lsapi_backend_loglevel_info

Syntax: lsapi_backend_loglevel_info [On/Off]

Default: lsapi_backend_loglevel_info Off

Context: httpd.conf, virtualhost

Description: Controls which log level will be used to write PHP warnings and notices into Apache’s error_log. Optional, the default value is Off. In that case LOG_WARNING log level will be used. Otherwise, with On value, LOG_INFO log level will be used.


lsapi_backend_oom_score_adj

Syntax: lsapi_backend_oom_score_adj [number]

Default: lsapi_backend_oom_score_adj 0

Context: httpd.conf, virtualhost

Description: This option can be used to apply oom_score_adj values for PHP processes created by mod_lsapi. Value is an integer in the -1000 to 1000 range. The lower the value, the lower the chance that the process will be killed. When your server becomes low on free memory and an OOM killer is invoked then desirable that lsphp processes are sacrificed to free up memory. To do this, you need to set oom_score_adj to a large value. For more information on setting value oom_score_adj, see the page https://man7.org/linux/man-pages/man5/proc.5.html


lsapi_server_tweak

Syntax: lsapi_server_tweak [On/Off]

Default: lsapi_server_tweak Off

Context: httpd.conf

Description: This option, when switched on, allows the use of lsapi_set_env and lsapi_set_env_path config options in the virtualhost context.


Connection pool mode

lsapi_with_connection_pool

Syntax : lsapi_with_connection_pool On/Off
Default : lsapi_with_connection_pool Off
Context : httpd.conf

Description :
On/Off - disable enable connect pool, default is Off.


lsapi_backend_max_idle

Syntax : lsapi_backend_max_idle [number]
Default : lsapi_backend_max_idle 300
Context : httpd.conf

Description :
It is relevant only with lsapi_with_connection_pool option switched on. Controls how long a worker process will wait for a new request before it exits. 0 stands for infinite. Should be more or equal to 0. In the case of wrong format default value will be used. Optional, default value is 300.


lsapi_backend_max_reqs

Syntax : lsapi_backend_max_reqs [number]
Default : lsapi_backend_max_reqs 10000
Context : httpd.conf

Description :
It is relevant only with lsapi_with_connection_pool option switched on. Controls how many requests a worker process will process before it exits. Should be more than 0. In the case of wrong format default value will be used. Optional, default value is 10000.


CRIU support

lsapi_criu

Syntax : lsapi_criu On/Off
Default : lsapi_criu Off
Context : httpd.conf

Description :
Enable/disable CRIU for lsphp freezing. Default: Off.


lsapi_criu_socket_path

Syntax : lsapi_criu_socket_path [path]
Default : lsapi_criu_socket_path /var/run/criu/criu_service.socket
Context : httpd.conf

Description :
Set path to socket for communication with criu service. Default: /var/run/criu/criu_service.socket.


lsapi_criu_imgs_dir_path

Syntax : lsapi_criu_imgs_dir_path [path]
Default : lsapi_criu_imgs_dir_path /var/mod_lsapi/
Context : httpd.conf

Description :
Path to folder where images of freezed PHP will be stored. Should be path. Default: /var/mod_lsapi/


lsapi_backend_initial_start

Syntax : lsapi_backend_initial_start [number]
Default : lsapi_backend_initial_start 0
Context : httpd.conf

Description :
Number of requests to virtualhost, when lsphp will be freezed. Default: 0 - means disable freezing.


lsapi_criu_use_shm

Syntax : lsapi_criu_use_shm Off/Signals
Default : lsapi_criu_use_shm Off
Context : httpd.conf

Description :
Method of requests counting. Off - use shared memory. Signals - use signals from child processes to parent. Default: Off


lsapi_backend_semtimedwait

Syntax : lsapi_backend_semtimedwait On/Off
Default : lsapi_backend_semtimedwait On
Context : httpd.conf

Description :
Use semaphore for checking when lsphp process will be started. Speed of start lsphp increased with semaphore using. This method avoid cycles of waiting for lsphp start. Default: On.


lsapi_reset_criu_on_apache_restart

Syntax : lsapi_reset_criu_on_apache_restart On/Off
Default : lsapi_reset_criu_on_apache_restart Off
Context : httpd.conf, virtualhost

Description :
This option allows cleaning all CRIU images on Apache restart.
Setting lsapi_reset_criu_on_apache_restart to On means that on each Apache restart the CRIU images which are stored in directory specified by lsapi_criu_imgs_dir_path directive will be recreated on new request to domain(only after restart).
If this option set to Off then CRIU images won’t be recreated on Apache restart.


lsapi_criu_debug

Syntax: lsapi_criu_debug On/Off
Default: lsapi_criu_debug Off
Context: httpd.conf, virtualhost

Description :
Enable/disable CRIU related debug logging.


PHP configuration management

lsapi_process_phpini

Syntax : lsapi_process_phpini On/Off
Default : lsapi_process_phpini Off
Context : httpd.conf, virtualhost, directory

Description :
Enable or disable phpini_* directive processing. Default value is Off.


lsapi_phpini

Syntax : lsapi_phpini [path]
Default : lsapi_phpini -
Context : httpd.conf, virtualhost, htaccess

Description :
When lsapi_process_phpini option switched to Off, these values will be silently ignored. lsapi_phpini values with absolute filename of php.ini file can be inserted into .htaccess files in order to set custom php.ini which will override/complement settings from system default php.ini.


lsapi_phprc

Syntax : lsapi_phprc [No | Env | Auto | DocRoot]
Default : lsapi_phprc No
Context : httpd.conf, virtualhost

Description : The value of PHPRC env variable.
Special values are "No", "Env", "Auto" and "DocRoot".
Default value is "No" - without PHPRC at all.
"Auto" value stands for php.ini from DocumentRoot of the corresponding VirtualHost if it is present, or from user's home directory otherwise "DocRoot" value stands for php.ini from DocumentRoot of the corresponding VirtualHost.
"Env" value for using PHPRC from the environment, to set it with SetEnv config option, for example
lsapi_phprc No
lsapi_phprc Auto
lsapi_phprc DocRoot
lsapi_phprc Env
lsapi_phprc /etc/


lsapi_tmpdir

Syntax : lsapi_tmpdir [path]
Default : lsapi_tmpdir /tmp
Context : httpd.conf, virtualhost

Description :
Set alternate request body temporary files directory.


lsapi_enable_user_ini

Syntax : lsapi_enable_user_ini On/Off
Default : lsapi_enable_user_ini Off
Context : httpd.conf, virtualhost

Description :
Enable .user.ini files for backend. Same as suphp, php-fpm and fcgid mechanism of .user.ini. Default value is Off.


lsapi_user_ini_homedir

Syntax : lsapi_user_ini_homedir On/Off
Default : lsapi_user_ini_homedir Off
Context : httpd.conf, virtualhost

Description :
If lsapi_enable_user_ini option is set to On, then enable/disable processing .user.ini file in home directory also. Default value is Off.


lsapi_keep_http200

Syntax : lsapi_keep_http200 On/Off
Default : lsapi_keep_http200 Off
Context : httpd.conf, .htaccess

Description :
If set to On, always keep backend's response status as mod_php does. If set to Off, response status can be overridden by Apache as suphp does (in case of call via ErrorDocument directive).


lsapi_mod_php_behaviour

Syntax : lsapi_mod_php_behaviour On/Off
Default : lsapi_mod_php_behaviour On
Context : httpd.conf, virtualhost, directory

Description :
On/Off - disable php_* directives, default On.


php_value, php_admin_value, php_flag, php_admin_flag

Syntax : [php_value|php_admin_value|php_flag|php_admin_flag]
Default :
Context : httpd.conf, virtualhost, htaccess

Description :
mod_php emulation.


Security

lsapi_use_suexec

Syntax : lsapi_use_suexec On/Off
Default : lsapi_use_suexec On
Context : httpd.conf

Description :
Use or not suexec to a target user.


lsapi_user_group

Syntax : lsapi_user_group [user_name] [group_name]
Default : -
Context : httpd.conf, virtualhost, directory

Description :
Set user & group for requests.


lsapi_uid_gid

Syntax : lsapi_uid_gid [uid] [gid]
Default : -
Context : httpd.conf, virtualhost, directory

Description :
Set user & group for request.


lsapi_use_default_uid

Syntax : lsapi_use_default_uid On/Off
Default : lsapi_use_default_uid On
Context : httpd.conf

Description :
Use default Apache UID/GID if no uid/gid set. Values: On/Off. If Off, and no UID/GID set, error 503 will be returned.


lsapi_target_perm

Syntax : lsapi_target_perm On/Off
Default : lsapi_target_perm Off
Context : httpd.conf

Description :
Check target PHP script permissions. If set to On, lsapi will check that script is owned by the same user, as user under which it is being executed. Return 503 error if they don't match. Default: Off.


lsapi_paranoid

Syntax : lsapi_paranoid On/Off
Default : lsapi_paranoid Off
Context : httpd.conf

Description :
Check or not permissions of target php scripts. Optional, default value is Off.


lsapi_check_document_root

Syntax : lsapi_check_document_root On/Off
Default : lsapi_check_document_root On
Context : httpd.conf

Description :
Check or not owner of DOCUMENT_ROOT. Optional, default value is On.


lsapi_disable_forced_pwd_var

Syntax : lsapi_disable_forced_pwd_var On/Off
Default : lsapi_disable_forced_pwd_var Off
Context : httpd.conf, virtualhost

Description :
To disable addition of PWD variable. Default value is Off. If set to On, the PWD variable will not be added into a backend environment.


lsapi_max_resend_buffer

Syntax : lsapi_max_resend_buffer [number]tmp
Default : lsapi_max_resend_buffer 200
Context : httpd.conf, virtualhost

Description :
Maximum buffer in KiB to resend for request that has a body (like POST request body).

See also Apache mod_lsapi PRO CLI tools.

Troubleshooting

Debugging mod_lsapi issues: error_log & sulsphp_log

mod_lsapi errors will be located in error_log and sulsphp_log. Note that errors can appear in both logs at the same time, and you might need to refer to both of them to solve the issue.

See next table for more details:

error_logsulsphp_logSolution
Could not connect to lsphp backend: connect to lsphp failed: 111 Connection refused. Increase memory limit for LVE IDuid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphpIncrease pmem or vmem limits for the user uid.
Error sending request: ReceiveLSHeader: nothing to read from backend socketNo need to check this log.lsphp was killed. It can be due to apache restart or lfd. If you see this message too often - change lsapi_terminate_backends_on_exit to Off in lsapi.conf or add to /etc/csf/csf.pignore the following lines: exe:/usr/local/bin/lsphp pexe:/opt/alt/php.*/usr/bin/lsphp
Error sending request (lsphp is killed?): ReceiveLSHeader: nothing to read from backend socket, referer: http://XXXXXXX Child process with pid: XXXXX was killed by signal: 11, core dump: 0No need to check this log.lsphp has crashed. Next slide will explain what to do (core dump creating). Also, check configuration options for apc and suhosin in php.ini. Once you have a core file generated at DocumentRoot contact https://cloudlinux.zendesk.com/ so we can investigate the cause.
Could not connect to lsphp backend: connect to lsphp failed: 111 Connection refusedfile is writable by others: (///usr/local/bin/lsphp)Incorrect lsphp file permissions. For fixing: chmod 755 /usr/local/bin/lsphp cagefsctl --force-update.
Could not determine uid/gid for requestNo need to check this log.UID/GID are not set in virtualhost. Set lsapi_use_default_uid On in lsapi.conf (it is On by default since 0.1-98 version, this solution is for older versions).
Own id for script file (/xxxx/xxx/xxxx) is xxx; should be xxxxNo need to check this log.File is not owned by the user PHP executed by. To overwrite (insecure), set lsapi_target_perm Off in lsapi.conf.
Could not connect to lsphp backend: connect to lsphp failed: 111 Connection refusedEntering jail errorCheck if СageFS enabled. Try running cagefsctl --remount-all.
Backend error on sending request(GET /XXXX HTTP/1.1); uri(/XXXX) content-length(0) (lsphp is killed?): ReceiveAckHdr: backend process reset connection: errno 104 (possibly memory limit for LVE ID XXXX too small)uid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphpIncrease PMEM limits for the user UID.
Reached max children process limit: XX, extra: 0, current: XX, please increase LSAPI_CHILDREN.

Backend error on sending request(GET /XXXX HTTP/1.1); uri(/XXXX) content-length(0) (lsphp is killed?): ReceiveAckHdr: backend process reset connection: errno 104 (possibly memory limit for LVE ID XXXX too small)
uid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphpIncrease value of lsapi_backend_children for UID in vhost.conf or globally in lsapi.conf.
fork() failed, please increase process limit: Cannot allocate memory

Backend error on sending request(GET /XXXX HTTP/1.1); uri(/XXXX) content-length(0) (lsphp is killed?): ReceiveAckHdr: backend process reset connection: errno 104 (possibly memory limit for LVE ID XXXX too small)
uid:(xxx); gid:(xxx); uid limit warning: EP should be < than NPROC, current EP: XX, NPROC: XX

uid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphp
Increase NPROC limits for the UID. It should be greater than EP and lsapi_backend_children.
Child process with pid: XXXXXX was killed by signal: 9, core dump: 0

Backend error on sending request(GET /XXXX HTTP/1.1); uri(/XXXX) content-length(0) (lsphp is killed?): ReceiveAckHdr: nothing to read from backend socket (LVE ID XXXX
uid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphpThese errors occurs when the amount of PMEM limits is incommensurable with the number of EP. Increase PMEM limits or decrease EP number for the user UID.
totBytesRead (X) != bodyLen (X), referer: XXXX

Backend error on sending request(POST /XXXX HTTP/1.1); uri(/XXXX) content-length(X) (lsphp is killed?): ReceiveAckHdr: nothing to read from backend socket (LVE ID XXXX)

lsphp(XXXX): Child process with pid: XXXX was killed by signal: 15, core dump: 0
No need to check this log.Increase LimitRequestBody (Apache) or/and SecRequestBodyNoFilesLimit (mod_security) configuration limits
Connect to backend failed: connect to lsphp failed: 13No need to check this log.Check that mod_ruid2 is disabled
Connect to backend rejected on sending request(POST /XXXXX HTTP/1.1); uri(/XXXXX)No need to check this log.Set lsapi_disable_reject_mode On in your lsapi.conf and reload Apache. This way LSPHP daemon will put requests that cannot be served by LSPHP daemon right away into infinite queue, until one or more LSPHP daemon becomes free. Visit Configuration Reference for more info.

Non-standard apache user

If apache runs under a username other than "apache" or "nobody" , you should rebuild sulsphp (where username is built in for security reasons) with corresponding username:

yum install liblsapi liblsapi-devel   
cd ~$ wget [https://repo.cloudlinux.com/cloudlinux/sources/da/mod_lsapi.tar.gz](https://repo.cloudlinux.com/cloudlinux/sources/da/mod_lsapi.tar.gz)  
tar zxvf mod_lsapi.tar.gz  
cd mod-lsapi-0.1-37  
cmake -DHTTPD_USER=<new user name> .  
make
make install
This will:
- Install: /usr/lib/apache/mod_lsapi. so (or to another correct httpd modules path)
- Install: /usr/sbin/sulsphp

lsphp started under user apache/nobody

Check if SuExecUserGroup specified for virtual hosts. This parameter is used by mod_lsapi for user identification.

Could not connect to lsphp backend: connect failed: 111 Connection refused

  • Switch in lsapi.conf or mod_lsapi.conf value to: lsapi_terminate_backends_on_exit Off

  • Check if empty: cat /etc/cron.d/kill_orphaned_php-cron | grep lsphp , then run:

yum install lve-utils
Then restart cron service.

Running PHP for users with UID < 99

If you need to run PHP using mod_lsapi using users with UID < 99, you would need to re-compile sulsphp:

yum install liblsapi liblsapi-devel
cd ~
wget [https://repo.cloudlinux.com/cloudlinux/sources/da/mod_lsapi.tar.gz](https://repo.cloudlinux.com/cloudlinux/sources/da/mod_lsapi.tar.gz)
tar zxvf mod_lsapi.tar.gz
cd mod-lsapi-0.1-XX
cmake -DUID_MIN=80 -DGID_MIN=80 .
make
make install
will be installed
- Installing: /usr/lib/apache/mod_lsapi.so (or another httpd modules path)
- Installing: /usr/sbin/sulsphp

Apache binary called not httpd (httpd.event, httpd.worker)

yum install liblsapi liblsapi-devel 
cd ~
wget https://repo.cloudlinux.com/cloudlinux/sources/da/mod_lsapi.tar.gz        
tar zxvf mod_lsapi.tar.gz
cd mod-lsapi-0.1-XX
cmake -DPARENT_NAME="<apache binary name>".
make
make install
Will be installed:
- Installing: /usr/lib/apache/mod_lsapi.so (or another httpd modules path)
- Installing: /usr/sbin/sulsphp

WHMCS Status page not accessible after installing CL and mod_lsapi (cPanel).

  • add user: useradd userstat
  • add to file (to the end of file before </IfModule>) /usr/local/apache/conf/conf.d/lsapi.conf: <Directory /usr/local/apache/htdocs/> lsapi_user_group userstat userstat </Directory>
  • service httpd restart

This is safe solution for easyapache rebuilding and cpanel-mod-lsapi updating.

PHP page with Suhosin return 503 error

Make php.ini for suhosin as recommended below:

[suhosin]
suhosin.simulation = Off
suhosin.mail.protect = 1
suhosin.cookie.disallow_nul = Off
suhosin.cookie.max_array_depth = 1000
suhosin.cookie.max_array_index_length = 500
suhosin.cookie.max_name_length = 500
suhosin.cookie.max_totalname_length = 500
suhosin.cookie.max_value_length = 200000
suhosin.cookie.max_vars = 16384
suhosin.get.disallow_nul = Off
suhosin.get.max_array_depth = 1000
suhosin.get.max_array_index_length = 500
suhosin.get.max_name_length = 500
suhosin.get.max_totalname_length = 500
suhosin.get.max_value_length = 1000000
suhosin.get.max_vars = 16384
suhosin.post.disallow_nul = Off
suhosin.post.max_array_depth = 1000
suhosin.post.max_array_index_length = 500
suhosin.post.max_name_length = 500
suhosin.post.max_totalname_length = 500
suhosin.post.max_value_length = 1000000
suhosin.post.max_vars = 16384
suhosin.request.disallow_nul = Off
suhosin.request.max_array_depth = 1000
suhosin.request.max_array_index_length = 500
suhosin.request.max_totalname_length = 500
suhosin.request.max_value_length = 1000000
suhosin.request.max_vars = 16384
suhosin.request.max_varname_length = 524288
suhosin.upload.max_uploads = 300
suhosin.upload.disallow_elf = Off
suhosin.session.cryptua = Off
suhosin.session.encrypt = Off
suhosin.session.max_id_length = 1024
suhosin.executor.allow_symlink = Off
suhosin.executor.disable_eval = Off
suhosin.executor.disable_emodifier = Off
suhosin.executor.include.max_traversal = 8

PHP page with APC return 503 error

Make php.ini for APC as recommended below:

[apc]...apc.shm_segments=1apc.shm_size=32...
shared memory should be not less than 32MB

Messages appearing in error_log: Child process with pid: XXXXX was killed by signal: 11, core dump: 0

This means that lsphp was crashed. The solution is:

  • Check if apc for user enabled. Tune its options as described in previous slide.
  • Check if suhosin is enabled for user. Tune its options as described in this article.
  • If previous items do not help, contact us at https://helpdesk.cloudlinux.com/

How to get lsphp core dump on crash

  • Configure mod_lsapi to allow lsphp to generate core dumps. In mod_lsapi.conf:
lsapi_backend_coredump On
  • Enable core file generation in sysctl:
sysctl -w ‘kernel.core_uses_pid=1’
sysctl -w ‘kernel.core_pattern=core.%p’
  • Configure system to change max size of core files. In /etc/security/limits.conf add:
user1 soft core unlimited
user1 hard core unlimited
where user1 is the username for which lsphp crashes.
  • If /etc/profile.d/limits.sh exists, look up for the following lines:
if [ "$LIMITUSER" != "root" ]; then
ulimit -n 100 -u 35 -m 200000 -d 200000 -s 8192 -c 200000 -v unlimited 2>/dev/null
Substring “-c 200000” must be replaced with “-c unlimited” .
  • Add line into ulimit -c unlimited into apachectl script just after another invokes of the ulimit command.

  • Do cold restart of Apache with the command like this:

service httpd stop; sleep 2; killall lsphp; service httpd start
  • You can make sure that ulimit for lsphp is changed to unlimited successfully with the following command:
cat /proc/PID/limits | grep ‘Max core file size’

where PID is a pid of any lsphp process. ps -u user1 | grep lsphp

  • Core dump of lsphp will be created in the DocumentRoot of the corresponding virtual server. On cPanel server it should map to

mod_lsapi is not included in output of httpd -M after installation and setup command for cPanel EasyApache 3

  1. Check if the file /usr/local/apache/conf/conf.d/lsapi.conf exists and not empty;

  2. Check if output of the command

cat /usr/local/apache/conf/httpd.conf | grep "/usr/local/apache/conf/conf.d/\*\.conf"
is not empty.

If it is empty:

  1. Add to "include" section of /var/cpanel/conf/apache/main string:

"include": '"/usr/local/apache/conf/conf.d/*.conf"'
 "include":
 "directive": 'include'
 "items":
 ...
 -
 "include": '"/usr/local/apache/conf/conf.d/*.conf"' 
 "listen":
  1. Do:
mkdir -p /usr/local/apache/conf/conf.d/;                                                                                 
cp /usr/share/lve/modlscapi/confs/lsapi.conf /usr/local/apache/conf/conf.d/lsapi.conf
  1. Call:
/scripts/rebuildhttpdconf/scripts/restartsrv_httpd

See also mod_lsapi PRO FAQ here.

CRIU Support

Note

CloudLinux OS Shared 7, CloudLinux OS Shared 7 Hybrid, and CloudLinux OS Shared 8 only

CRIU is Checkpoint/Restore In Userspace , (pronounced kree-oo ), is a software tool for Linux operating system. Using this tool, you can freeze a running application (or part of it) and checkpoint it as a collection of files on disk. You can then use the files to restore the application and run it exactly as it was during the time of freeze (more information on the link https://criu.org/Main_Page ).

mod_lsapi-1.1-1 is the first beta version with freezing PHP implemented. mod_lsapi now supports the following parameters:

Option nameDescriptionValuesDefault
lsapi_criuEnable/disable CRIU for lsphp freezing. On/Off Off
lsapi_criu_socket_pathSet path to socket for communication with criu service.[path to socket] /var/run/criu/criu_service.socket
lsapi_backend_semtimedwaitEnable/disable flag for notification about lsphp started. This method avoid cycles of waiting for lsphp start. On/Off On
lsapi_backend_initial_startNumber of request when lsphp should be freezed.[number] 0 - no freezing0
lsapi_criu_use_shmMethod of requests counting. Off - use shared memory. Signals - use signals from child processes to parent. Off/Signals Off
lsapi_criu_imgs_dir_pathPath to folder where imgs of freezed PHP will be stored.[path] /var/mod_lsapi/
lsapi_criu_debugEnable/Disable CRIU related debug logging. On/Off Off

Example:

lsapi_criu On
lsapi_criu_socket_path /var/run/criu/criu_service.socket
lsapi_backend_semtimedwait On
lsapi_backend_initial_start 15
lsapi_criu_use_shm Off
lsapi_criu_debug Off

When Apache module mod_lsapi detects CRIU enabled (lsapi_criu On) it prepares a directory for images (on the first request of virtualhost) to store ( lsapi_criu_imgs_dir_path /var/mod_lsapi/[dir_name] ), and starts lsphp process. Lsphp increases counter ( lsapi_criu_use_shm Off|Signals ) via shared memory or signals, when counter reaches limit ( lsapi_backend_initial_start 15 ), lsphp sends the request to CRIU for freezing. CRIU service makes images of requested processes. Lsphp will not be frozen if counter has not reached the limit. The next time when lsphp will be stopped, it will be unfrozen from the images.

The images of the processes will be saved even if Apache is restarted. But all images will be deleted after server restart by default configuration. This can be modified by setting the new path lsapi_criu_imgs_dir_path .

Important!

If php.ini or configuration file from php.d is changed, the images must be deleted manually.

Note

CRIU (version lower than criu-lve-3.6-1) can't correctly freeze lsphp with PrivateTmp enabled. For correct work, PrivateTmp must be disabled in httpd.service file .

To disable it, copy httpd.service to /etc/systemd/system and change there PrivateTmp:  

cat httpd.service  
............ 

[Unit]
Description=Apache web server managed by cPanel Easy
ApacheConditionPathExists=!/etc/httpddisable
ConditionPathExists=!/etc/apachedisable
ConditionPathExists=!/etc/httpdisable

[Service]Type=forking
ExecStart=/usr/local/cpanel/scripts/restartsrv_httpd --no-verbose
PIDFile=/var/run/apache2/httpd.pid
PrivateTmp=false 

[Install]
WantedBy=multi-user.target 
Or it would be technically better to provide a small override of service file rather than copying the whole new version in /etc/systemd/system

http://www.freedesktop.org/software/systemd/man/systemd.unit.html

mkdir /etc/systemd/system/httpd.service.d
echo "[Service]" >  /etc/systemd/system/httpd.service.d/nopt.conf
echo "PrivateTmp=false" >> /etc/systemd/system/httpd.service.d/nopt.conf

and

systemctl daemon-reload

CRIU Installation

Criu is installed with dependency to mod_lsapi package. To activate it:

  1. Enable service and start it:
systemctl enable criu
systemctl start criu
  1. Edit lsapi.conf file, turn CRIU On and set some defaults:
lsapi_criu On
lsapi_criu_socket_path /var/run/criu/criu_service.socket
lsapi_backend_semtimedwait On
lsapi_backend_initial_start 15
lsapi_criu_use_shm Off
  1. Restart apache:
service httpd restart

CRIU Image Cleanup

  1. An option added to the Apache configuration for cleaning all the images earlier saved by CRIU.
Option nameDescriptionValueDefault
lsapi_reset_criu_on_apache_restartThis option allows cleaning all CRIU images on Apache restart. On/Off Off

On the next restart of Apache all of the images will be cleaned.

It can be enabled by writing lsapi_reset_criu_on_apache_restart On in lsapi.conf (Virtual Host and .htaccess do not allow to use this option).

Note that this option works only if lsapi_terminate_backends_on_exit is On (default value is On , it is set in lsapi.conf too).

  1. If you need to clean CRIU images for one user you can simply add file to the user's directory with CRIU images (default /var/mod_lsapi/lsapi * criu_imgs ). On the next restart of lsphp the images will be cleaned.

  2. Global reset flag for cleaning all earlier saved images by CRIU.

Current mod_lsapi allows cleaning all images only with one flag file.

Create /usr/share/criu/mod_lsapi/lsphp.criu.reset file. Also don't forget to set such permissions [nobody:nobody] (or [apache:apache] for non cPanel) and access mode [700] to the /usr/share/criu/mod_lsapi directory.

Steps to do :

mkdir /usr/share/criumkdir /usr/share/criu/mod_lsapi
chown nobody:nobody /usr/share/criu/mod_lsapi
touch /usr/share/criu/mod_lsapi/lsphp.criu.reset

On the next requests to all virtual hosts images will be recreated (deleted first and created again later - it depends on lsapi_backend_initial_start value).

  1. Аdded possibility to clean CRIU images from user space.

If a user needs to clean CRIU images for lsphp, he should create a file: ~/mod_lsapi_reset_me_[vhost_name] . Where [vhost_name] is a ServerName from the VirtualHost block in the configuration file. On the next restart of lsphp, the images will be cleaned.

Example:

cd; touch mod_lsapi_reset_me_criu.test.com

where vhost.conf contains:
ServerName criu.test.com

This mode is enabled by default and creates a separate lsphp process for each virtual host.

mod_lsapi_reset_me[vhost_name] flag will not work for a user when lsapi_per_user option is On.

  1. There is (default off) option in mod_lsapi that creates only one lsphp process for a user, regardless of the number of his virtual hosts. We don't recommend to use this option with CRIU, but if you use it, make sure that your virtual hosts (under the same user) have the same environment configurations. If they are not the same, this may cause undesirable lsphp process operation.

NGINX LSAPI Module

General information and requirements

The nginx-lsapi-module is an extension for the NGINX web server, designed to seamlessly integrate the lightweight, high-performance LiteSpeed PHP processing capability directly within the NGINX architecture. This module enables NGINX to efficiently handle PHP requests using the LiteSpeed SAPI, offering an alternative to traditional PHP-FPM or mod_php approaches. It aims to leverage the speed and flexibility of LiteSpeed's PHP processing in environments where the NGINX is the preferred web server.

How does it work?

  1. NGINX passes handling for PHP request to nginx-mod-lsapi;
  2. nginx-mod-lsapi uses liblsapi to transfer request to the lsphp parent process;
  3. lsphp forks the child process, which executes the request and returns data to nginx-mod-lsapi;
  • If there are no requests for lsapi_pgrp_max_idle seconds, lsphp parent process will be terminated;
  • If there are no lsphp child processes available when a new request comes, the new lsphp child process will be created;
  • lsphp children process concurrent requests simultaneously;
  • The maximum number of simultaneously running lsphp child processes can be set by the lsapi_backend_children directive.

What is lsphp?

lsphp - PHP + LSAPI. What is PHP LSAPI? LiteSpeed Server Application Programming Interface (LSAPI) is designed specifically for seamless, optimized communication between the LiteSpeed Web Server and third-party web applications. With nginx-mod-lsapi, this protocol is now available for NGINX.

Using nginx-mod-lsapi, we have seen the higher performance than NGINX with fastcgi+php-fpm, easier installation than php-fpm and easier integration with any control panel. nginx-mod-lsapi means faster and more stable dynamic web pages.

Optional requirements

  • Configured LVE containers for end-users ( recommended - control over resource consumption );
  • Enabled CageFS for end-users ( recommended - higher security level );
  • PHP Selector with alt-php - an easy way to select different PHP versions for each end-user provided by CloudLinux OS Shared;
  • ea-php - alternative to alt-php provided by cPanel (for cPanel only).

Installation

nginx-mod-lsapi can be installed through YUM package manager, however, the installation process varies depending on the control panel.

Select the control panel you are using:

Installing on cPanel servers with ea-nginx

Install nginx-mod-lsapi and related packages through YUM package manager as follows:

yum install ea-nginx-mod-lsapi --enablerepo=cl-ea4-testing
Now, when the module is installed, restart NGINX to ensure that the nginx-mod-lsapi is enabled:
service nginx restart

Use Apache2Nginx to set up NGINX hosting on the server and automatically convert .htaccess files into corresponding NGINX configuration. For more details, please visit Apache2Nginx.

Installing on servers with no control panel

Install nginx-mod-lsapi and related packages through YUM package manager as follows:

yum install nginx-mod-lsapi
Now, when the module is installed, restart NGINX to ensure that nginx-mod-lsapi is enabled:
service nginx restart

Uninstalling

The uninstallation process of the nginx-mod-lsapi module also varies depending on your control panel.

Select the control panel you are using:

Uninstallation procedure for cPanel servers with EA-NGINX

Remove packages with YUM package manager:

yum erase ea-nginx-mod-lsapi
Restart NGINX afterwards:
service nginx restart

To remove NGINX from your system and restore Apache configuration, please visit Apache2Nginx.

Uninstallation procedure for servers with no control panel

Remove packages with YUM package manager:

yum erase nginx-mod-lsapi
rm [path to nginx-mod-lsapi.conf]
Restart NGINX to restore the standard PHP handler:
service nginx restart

Configuration

In order to get nginx-mod-lsapi to work properly, you'll need to configure NGINX. To do this, we use a separate lsapi.conf file.

First of all, you need to make sure that the appropriate LSAPI module configuration exists and contains the correct content.

[root@nginx]# cat /usr/share/nginx/modules/ngx-mod-lsapi.conf
load_module "/usr/lib64/nginx/modules/ngx_http_lsapi_module.so";

In case of ea-nginx the file can be: /etc/nginx/conf.d/modules/ea-nginx-lsapi-module.conf

Then make sure that the loading of dynamic modules is not omitted in your main nginx configuration (nginx.conf).

include /usr/share/nginx/modules/*.conf;
In order to enable the module to process requests, you need to add the lsapi_enabled directive to your _lsapi.conf_ file as follows:
lsapi_enabled on;

The lsapi_enabled parameter is supported in global, server, and local configurations. Therefore, it can be enabled/disabled globally, only for the server, and also only for a specific location.

The LSPHP handler can be enabled using the lsapi_handler directive. The lsapi_handler directive tells NGINX that the files with .php extension should be handled by nginx-mod-lsapi with the selected handler.

 lsapi_handler application/x-httpd-lsphp;

If no handler is explicitly set for a request, the specified content type will be used as the handler name, therefore, please disable php.conf or any other PHP handler for using nginx-mod-lsapi. In this example application/x-httpd-lsphp is a default handler by which nginx-mod-lsapi processes requests with the lsphp binary from /usr/local/bin/ directory.

The final lsapi.conf configuration might look like this:

server {
	listen          80;
	server_name     testlsapi.com;
	root            /home/testlsapi/public_html;

	location ~ \.php$ {
		lsapi_enabled    on;
		lsapi_handler    application/x-httpd-lsphp;
		lsapi_user       testlsapi;
	}

	error_page  500 502 503 504  /50x.html;
	location = /50x.html {
		root   html;
	}
}

In order for nginx-mod-lsapi to work lsapi.conf should be loaded to NGINX through Include directive.

For a more detailed description of the module directives please visit the Configuration reference.
For the installation guide for nginx-mod-lsapi please visit Installation.

Configuration references

nginx-mod-lsapi customization:

Tuning LSPHP backend:

Connection pool mode:

CRIU support:

PHP configuration management:

Security:

nginx-mod-lsapi customization

lsapi_enabled

Syntax : lsapi_enabled [on/off]
Example : lsapi_enabled on;
Default : off
Context : main config, server config, local config.
Type : Mandatory

Description :
Enable/disable LSAPI module for nginx, server, or local directory.


lsapi_sock_path

Syntax : lsapi_sock_path [path] Example : lsapi_sock_path /var/ngx_lsapi;
Default : /var/ngx_lsapi
Context : main config Type : Optional

Description:
Path to backend lsphp sockets. By default /var/ngx_lsapi


lsapi_per_user

Syntax : lsapi_per_user [on/off]
Example : lsapi_per_user off;
Default : off
Context : server config, location config Type : Optional

Description :
Invoke the master lsPHP process not per VirtualHost, but per account. When On, invoke backend not per VirtualHost but per account. Default value is off. It is possible, for example, to set it to On in global config file and to Off in config files of some particular Virtual Hosts. Then these Virtual Hosts will have a dedicated backend process, while others will have backend processes shared on account basis.


lsapi_disable_reject

Syntax : lsapi_disable_reject [on/off]
Example : lsapi_disable_reject on;
Default : off
Context : server config
Type : Optional

Description :
If a new HTTP request is coming to LSPHP daemon when all LSPHP workers are still busy, it can process this situation in two different ways. In REJECT mode LSPHP daemon will reject such requests immediately. Otherwise, in legacy mode, LSPHP daemon will put this request into an infinite queue, until one of LSPHP daemons becomes free. When a HTTP request is rejected in REJECT mode, mod_lsapi will write the following message into the NGINX error.log: Connect to backend rejected, and the client will receive a 507 HTTP response. By default, the LSPHP daemon in CloudLinux OS Shared uses the REJECT mode. It can be switched off with this option.


lsapi_terminate_backends_ex

Syntax : lsapi_terminate_backends_ex [on/off]
Example : lsapi_terminate_backends_ex on;
Default : on
Context : main config
Type : Optional

Description :
nginx.conf, on - stop lsphp services on nginx restart, off - leave live started lsphp services on nginx restart (for php+opcache). The lsphp will not restart, even if NGINX gets restarted.


lsapi_avoid_zombies

Syntax : lsapi_avoid_zombies [on/off]
Example : lsapi_avoid_zombies on;
Default : off
Context : server config
Type : Optional

Description :
Enable or disable a mechanism to avoid the creation of zombie processes by lsphp. The default value is Off.


lsapi_starter_sock

Syntax : lsapi_starter_sock [path]
Example : lsapi_starter_sock /var/run/lsapi-starter.sock;
Default : /var/ngx_lsapi/starter.sock
Context : main config
Type : Optional

Description :
This parameter configures the socket address for the lsphp backend spawner process.


lsapi_starter_log

Syntax : lsapi_starter_log [path]
Example : lsapi_starter_log /var/log/lsapi-starter.log;
Default : -
Context : main config
Type : Optional

Description :
This parameter configures the log file address for the lsphp backend spawner process.


lsapi_log

Syntax : lsapi_log [path]
Example : lsapi_log /var/log/lsapi.log;
Default : -
Context : main config
Type : Optional

Description :
This parameter configures the log file address for the NGINX LSAPI module context.
Note :
This parameter only works if the NGINX LSAPI module is built with the NGX_LSAPI_CUSTOM_LOGGER flag. Otherwise, the NGINX logger will be used for LSAPI module logging.


Tuning LSPHP backend

lsapi_set_env

Syntax : lsapi_set_env [var] [value]
Example: lsapi_set_env TMP "/var/lsphp-tmp";
Default : -
Context : main config
Type : Optional

Description :
Pass an environment variable to lsphp. By default the lsphp environment has only TEMP, TMP and PATH variables set.
Note: PATH env var default "/usr/local/bin:/usr/bin:/bin" cannot be changed because of security reasons.


lsapi_env_path

Syntax : lsapi_env_path [path(s)]
Default : lsapi_env_path /usr/local/bin:/usr/bin:/bin
Context : main config
Type : Optional

Description :
Change PATH variable in the environment of lsphp processes. Default path /usr/local/bin:/usr/bin:/bin will be used if not set.


lsapi_backend_children

Syntax : lsapi_backend_children [number]
Example : lsapi_backend_children 300;
Default : 120
Context : server config
Type : Optional

Description :
Sets the maximum number of simultaneously running child backend processes. Optional - a default directive value is equal to 120.


lsapi_retry_max

Syntax : lsapi_retry_max [number]
Example : lsapi_retry_max 20;
Default : 10
Context : server config
Type : Optional

Description :
Number of retries for connections to the lsPHP daemon.


lsapi_max_process_time

Syntax : lsapi_max_process_time [number]
Example : lsapi_max_process_time 300;
Default : 300
Context : server config
Type : Optional

Description :
Sets the env variable LSAPI_MAX_PROCESS_TIME. Optional - default value is 300. Timeout to kill runaway processes.


lsapi_common_own_log

Syntax : lsapi_common_own_log [on/off]
Example : lsapi_common_own_log on;
Default : off
Context : server config
Type : Optional

Description :
It will be used only when lsapi_backend_use_own_log is set to On. On-backend processes of the all virtual hosts will share the common log file. Off - every virtual host will have its own backend log file.


lsapi_own_log

Syntax : lsapi_own_log [on/off]
Example : lsapi_own_log on;
Default : off
Context : server config
Type : Optional

Description :
Redirecting log output of backend processes from NGINX error.log to dedicated log file or files, depending on the value of the lsapi_common_own_log option. If off, use the NGINX error log file for backend output, or a separate file otherwise.


lsapi_pgrp_max_crashes

Syntax : lsapi_pgrp_max_crashes [number]
Example : lsapi_pgrp_max_crashes 300;
Default : 300
Context : server config
Type : Optional

Description :
Controls how many crashes of its worker processes a control process will detect before it exits. Should be more or equal to 0. In the case of a wrong format, a default value will be used. Optional, the default value is 0, which means an unlimited number of crashes.


lsapi_pgrp_max_idle

Syntax : lsapi_pgrp_max_idle [number]
Example : lsapi_pgrp_max_idle 30;
Default : 30
Context : server config
Type : Optional

Description :
Sets env variable LSAPI_PGRP_MAX_IDLE, in seconds. Controls how long a control process will wait for a new request before it exits. 0 stands for infinite. Optional, default value is 30. Should be more or equal to 0.


lsapi_pgrp_max_reqs

Syntax : lsapi_pgrp_max_reqs [number]
Example : lsapi_pgrp_max_reqs 0;
Default : 0
Context : server config
Type : Optional

Description :
Controls how many requests a control process will process before it exits. Should be more or equal to 0. In the case of a wrong format, the default value will be used. Optional, the default value is 0, which means an unlimited number of requests.


lsapi_backend_loglevel_info

Syntax: lsapi_backend_loglevel_info [on/off] Example: lsapi_backend_loglevel_info on; Example: off Context: server config, location config Type : Optional

Description:
Controls which log level will be used to write PHP warnings and notices into NGINX’s error.log. Optional, the default value is off. In that case, the warn log level will be used. Otherwise, with on value, the info log level will be used.


lsapi_oom_score_adj

Syntax : lsapi_oom_score_adj [number]
Example : lsapi_oom_score_adj 100;
Default : 0
Context : server config
Type : Optional

Description :
This option can apply oom_score_adj values for PHP processes created by mod_lsapi. Value is an integer in the -1000 to 1000 range. The lower the value, the lower the chance that the process will be killed. When your server becomes low on free memory and an OOM killer is invoked, then it may be desirable for lsphp processes to be sacrificed to free up memory. To do this, you need to set oom_score_adj to a large value. For more information on setting a proper value for oom_score_adj, see the page https://man7.org/linux/man-pages/man5/proc.5.html


lsapi_dump_debug_info

Syntax : lsapi_dump_debug_info [on/off]
Example : lsapi_dump_debug_info on;
Default : off
Context : server config
Type : Optional

Description :
Enable or not lsphp backend debug information dump.


lsapi_accept_notify

Syntax : lsapi_accept_notify [on/off]
Example : lsapi_accept_notify on;
Default : off
Context : server config
Type : Optional

Description :
Switch LSAPI_ACCEPT_NOTIFY mode for lsphp. This option can be used both in Global and VirtualHost scopes. This mode is incompatible with PHP 4.4.


lsapi_backend_coredump

Syntax : lsapi_backend_coredump [on/off]
Example : lsapi_backend_coredump on;
Default : off
Context : server config
Type : Optional

Description :
env variable LSAPI_ALLOW_CORE_DUMP (On or Off). Pass LSAPI_ALLOW_CORE_DUMP to lsphp or not. If it is passed - a core dump on lsphp crash will be created. Off by default. By default LSAPI application will not leave a core dump file when crashed. If you want to have LSAPI PHP dump a core dump file, you should set this environment variable. If set, regardless of the value is has been set to, core dump files will be created under the directory that the PHP script is in.


lsapi_pwd_disabled

Syntax : lsapi_pwd_disabled [on/off]
Example : lsapi_pwd_disabled on;
Default : off
Context : server config
Type : Optional

Description :
To disable the addition of the PWD variable. The default value is Off. If set to On, the PWD variable will not be added to a backend environment.


lsapi_handler

Syntax : lsapi_handler [string]
Example : lsapi_handler application/x-httpd-lsphp;
Default : -
Context : server config
Type : Mandatory

Description :
Sets LSPHP handler for server (virtual host) configuration.


Connection pool mode

lsapi_use_pool

Syntax : lsapi_use_pool [on/off]
Example : lsapi_use_pool off;
Default : on
Context : main config
Type : Optional

Description :
Enable/disable LSAPI connection pool mode.


lsapi_max_idle

Syntax : lsapi_max_idle [number]
Example : lsapi_max_idle 300;
Default : 300
Context : server config
Type : Optional

Description :
It is relevant only with lsapi_use_pool option switched on. Controls how long a worker process will wait for a new request before it exits. 0 stands for infinite. Should be more or equal to 0. In the case of a wrong format the default value will be used. Optional, default value is 300.


lsapi_max_reqs

Syntax : lsapi_max_reqs [number]
Example : lsapi_max_reqs 20000;
Default : 10000
Context : server config
Type : Optional

Description :
It is relevant only with lsapi_use_pool option switched on. Controls how many requests a worker process will process before it exits. Should be more than 0. In the case of a wrong format the default value will be used. Optional, default value is 10000.


lsapi_pool_size

Syntax : lsapi_pool_size [number]
Example : lsapi_pool_size 20;
Default : 50
Context : main config
Type : Optional

Description :
The parameter sets the connection pool size for each virtual host.


CRIU support

lsapi_use_criu

Syntax : lsapi_use_criu [on/off]
Example : lsapi_use_criu on;
Default : off
Context : main config
Type : Optional

Description :
Enable/disable CRIU for lsphp freezing. Default: Off.


lsapi_criu_sock_path

Syntax : lsapi_criu_sock_path [path]
Example : lsapi_criu_socket_path /var/run/criu/criu_service.socket;
Default : /var/run/criu/criu_service.socket
Context : main config
Type : Optional

Description :
Set path to socket for communication with criu service.


lsapi_criu_imgs_path

Syntax : lsapi_criu_imgs_path [path]
Example : lsapi_criu_imgs_path /var/ngx_lsapi/criu;
Default : /var/ngx_lsapi/criu
Context : main config
Type : Optional

Description :
Path to the folder where images of frozen PHP will be stored. Should be a path.


lsapi_criu_use_shm

Syntax : lsapi_criu_use_shm [off/signals]
Example : lsapi_criu_use_shm signals;
Default : off
Context : main config
Type : Optional

Description :
The method of request counting. Off - use shared memory. Signals - use signals from child processes to parents. Default: off


lsapi_reset_criu_on_restart

Syntax : lsapi_reset_criu_on_restart [on/off]
Example : lsapi_reset_criu_on_restart off;
Default : off
Context : main config
Type : Optional

Description :
This option allows cleaning all CRIU images on NGINX restart.
Setting lsapi_reset_criu_on_restart to on means that on each NGINX restart the CRIU images, which are stored in the directory specified by lsapi_criu_imgs_path directive, will be recreated upon a new request to domain (only after restart).


lsapi_criu_debug

Syntax: lsapi_criu_debug [on/off]
Example: lsapi_criu_debug off;
Default : off
Context : main config
Type : Optional

Description :
Enable/disable CRIU related debug logging.


lsapi_backend_initial_start

Syntax : lsapi_backend_initial_start [number]
Example : lsapi_backend_initial_start 15;
Default : 0
Context : main config
Type : Optional

Description :
Number of requests to virtualhost, when lsphp will be freezed. Default: 0 - means disable freezing.


PHP configuration management

lsapi_process_phpini

Syntax : lsapi_process_phpini [on/off]
Example : lsapi_process_phpini on;
Default : off
Context : server config, location config Type : Optional

Description :
Enable or disable phpini_* directive processing. Default value is off.


lsapi_phpini

Syntax : lsapi_phpini [value]
Example: lsapi_phpini /usr/home/lsapiuser/public_html/php.ini;
Default : -
Context : server config, location
Type : Optional

Description :
Sets custom php.ini within server or location configuration. Absolute file path is required.
When lsapi_process_phpini configuration directive is switched to Off, the value for the lsapi_phpini will be silently ignored.


lsapi_phprc

Syntax : lsapi_phprc [No | Env | Auto | DocRoot]
Example : lsapi_phprc No;
Default : No
Context : server config
Type : Optional

Description :
The value of PHPRC env variable.
Special values are "No", "Env", "Auto" and "DocRoot".
Default value is "No" - without PHPRC at all.
The "Auto" value stands for php.ini from DocumentRoot of the corresponding VirtualHost if it is present, or from the user's home directory otherwise "DocRoot" value stands for php.ini from DocumentRoot of the corresponding VirtualHost.
"Env" value for using PHPRC from the environment, to set it with SetEnv config option, for example:
lsapi_phprc No;
lsapi_phprc Auto;
lsapi_phprc DocRoot;
lsapi_phprc Env;
lsapi_phprc /etc/;


lsapi_tmpdir

Syntax : lsapi_tmpdir [path]
Example : lsapi_tmpdir /usr/tmp;
Default : /tmp
Context : server config
Type : Optional

Description :
Set an alternate request body temporary files directory.


lsapi_enable_user_ini

Syntax : lsapi_enable_user_ini [on/off]
Example : lsapi_enable_user_ini off;
Default : off
Context : server config
Type : Optional

Description :
Enable .user.ini files for backend. Same as suphp, php-fpm and fcgid mechanism of .user.ini. Default value is off.


lsapi_user_ini_homedir

Syntax : lsapi_user_ini_homedir [on/off]
Example : lsapi_user_ini_homedir on;
Default : off
Context : server config
Type : Optional

Description :
If the lsapi_enable_user_ini option is set to On, then enable/disable processing .user.ini file in the home directory also. The default value is Off.


lsapi_mod_php_behaviour

Syntax : lsapi_mod_php_behaviour [on/off]
Example : lsapi_mod_php_behaviour off;
Default : on
Context : server config, location config
Type : Optional

Description :
on/off - disable php_* directives, default On.


lsapi_param

Syntax : lsapi_param [var] [value]
Example: lsapi_param PHP_ADMIN_VALUE "memory_limit=1024M";
Default : -
Context : server config, location config
Type : Optional

Description :
Sets a parameter that should be passed to the LSPHP handler. The value can contain text, variables, and their combinations.
Supported directives:
PHP_ADMIN_VALUE
PHP_VALUE
PHP_ADMIN_FLAG
PHP_FLAG
Examples:
lsapi_param PHP_ADMIN_VALUE "memory_limit=1024M \n max_execution_time=600";
lsapi_param PHP_FLAG "display_startup_errors=on";
lsapi_param PHP_ADMIN_FLAG "html_errors=on";
lsapi_param PHP_VALUE "max_file_uploads=20";
lsapi_param QUERY_STRING $query_string;
lsapi_param REQUEST_METHOD $request_method;
lsapi_param CONTENT_TYPE $content_type;
lsapi_param CONTENT_LENGTH $content_length;


Security

lsapi_suexec

Syntax : lsapi_suexec [on/off]
Example : lsapi_suexec on;
Default : on
Context : server config
Type : Optional

Description :
Enable or disable suexec usage with a target user.


lsapi_user

Syntax : lsapi_user [username] [group]
Example : lsapi_user testlsapi testlsapi;
Default : -
Context : server config, local config
Type : Mandatory

Description :
Set user & group for requests.
This parameter can take only one argument for username or two arguments for username and group.


lsapi_paranoid

Syntax : lsapi_paranoid [on/off]
Example : lsapi_paranoid on;
Default : off
Context : server config
Type : Optional

Description :
Enable or disable permission checking for the target php scripts.


lsapi_check_owner

Syntax : lsapi_check_owner [on/off]
Example : lsapi_check_owner on;
Default : off
Context : server config
Type : Optional

Description :
Enable or disable checking the owner of the target PHP scripts.


lsapi_check_doc_root

Syntax : lsapi_check_doc_root [on/off]
Example : lsapi_check_doc_root on;
Default : off
Context : server config
Type : Optional

Description :
Enable or disable checking the owner of DOCUMENT_ROOT.


lsapi_check_script

Syntax : lsapi_check_script [on/off]
Example : lsapi_check_script on;
Default : off
Context : server config
Type : Optional

Description :
Enable or disable checking the owner of target php scripts before sending the request to the lsphp backend. Optional, the default value is On.


lsapi_uid_gid

Syntax : lsapi_uid_gid [uid] [gid]
Example : lsapi_uid_gid 1001 1001;
Default : -
Context : local config
Type : Optional

Description :
Set user & group for requests.


Troubleshooting

Debugging nginx-mod-lsapi issues: error.log & sulsphp.log

nginx-mod-lsapi errors will be located in error_log and sulsphp_log. Note that errors can appear in both logs at the same time, and you might need to refer to both of them to solve the issue.

See the following table for more details:

error_logsulsphp_logSolution
Could not connect to lsphp backend: connect to lsphp failed: 111 Connection refused. Increase memory limit for LVE IDuid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphpIncrease pmem or vmem limits for the user uid.
Error sending request: ReceiveLSHeader: nothing to read from backend socketNo need to check this log.lsphp was killed. It can be due to nginx restart or lfd. If you see this message too often - change lsapi_terminate_backends_ex to off in lsapi.conf or add to /etc/csf/csf.pignore the following lines: exe:/usr/local/bin/lsphp pexe:/opt/alt/php.*/usr/bin/lsphp
Error sending request (lsphp is killed?): ReceiveLSHeader: nothing to read from backend socket, referer: http://XXXXXXX Child process with pid: XXXXX was killed by signal: 11, core dump: 0No need to check this log.lsphp has crashed. Next slide will explain what to do (core dump creating). Also, check configuration options for apc and suhosin in php.ini. Once you have a core file generated at DocumentRoot contact https://cloudlinux.zendesk.com/ so we can investigate the cause.
Could not connect to lsphp backend: connect to lsphp failed: 111 Connection refusedfile is writable by others: (///usr/local/bin/lsphp)Incorrect lsphp file permissions. For fixing: chmod 755 /usr/local/bin/lsphp cagefsctl --force-update.
Backend error on sending request(GET /XXXX HTTP/1.1); uri(/XXXX) content-length(0) (lsphp is killed?): ReceiveAckHdr: backend process reset connection: errno 104 (possibly memory limit for LVE ID XXXX too small)uid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphpIncrease PMEM limits for the user UID.
Reached max children process limit: XX, extra: 0, current: XX, please increase LSAPI_CHILDREN.

Backend error on sending request(GET /XXXX HTTP/1.1); uri(/XXXX) content-length(0) (lsphp is killed?): ReceiveAckHdr: backend process reset connection: errno 104 (possibly memory limit for LVE ID XXXX too small)
uid: (xxx/xxxxxxxx) gid: (xxx/xxxxxxxxxx) cmd: /usr/local/bin/lsphpIncrease value of lsapi_backend_children for UID in vhost.conf or globally in lsapi.conf.
Connect to backend rejected on sending request(POST /XXXXX HTTP/1.1); uri(/XXXXX)No need to check this log.Set lsapi_disable_reject on in your lsapi.conf and reload NGINX. This way LSPHP daemon will put requests that cannot be served by LSPHP daemon right away into infinite queue, until one or more LSPHP daemon becomes free. Visit Configuration Reference for more info.

CRIU Support

Note

CloudLinux OS Shared 7, CloudLinux OS Shared 7 Hybrid, and CloudLinux OS Shared 8 only

CRIU is Checkpoint/Restore In Userspace , (pronounced kree-oo ), is a software tool for the Linux operating system. Using this tool, you can freeze a running application (or part of it) and checkpoint it as a collection of files on disk. You can then use the files to restore the application and run it exactly as it was at the time of freeze (more information on the link https://criu.org/Main_Page ).

nginx-mod-lsapi now supports the following parameters:

Option nameDescriptionValuesDefault
lsapi_use_criuEnable/disable CRIU for lsphp freezing. on/off off
lsapi_criu_sock_pathSet path to socket for communication with criu service.[path to socket] /var/run/criu/criu_service.socket
lsapi_backend_initial_startNumber of request when lsphp should be freezed.[number] 0 - no freezing0
lsapi_criu_use_shmMethod of requests counting. off - use shared memory. Signals - use signals from child processes to parent. off/signals off
lsapi_criu_imgs_pathPath to folder where imgs of freezed PHP will be stored.[path] /var/nginx-mod-lsapi/
lsapi_criu_debugEnable/Disable CRIU related debug logging. on/off off

Example:

lsapi_criu on;
lsapi_criu_socket_path /var/run/criu/criu_service.socket;
lsapi_backend_initial_start 15;
lsapi_criu_use_shm off;
lsapi_criu_debug off;

When NGINX module nginx-mod-lsapi detects CRIU enabled (lsapi_criu On), it prepares a directory for images (on the first request of virtualhost) to store ( lsapi_criu_img_path /var/ngx_lsapi/criu/[dir_name] ), and starts the lsphp process. Lsphp increases a counter ( lsapi_criu_use_shm off|Signals ) via shared memory or signals, and when counter reaches the limit ( lsapi_backend_initial_start 15 ), lsphp sends the freezing request to CRIU. The CRIU service makes images of requested processes. Lsphp will not be frozen if counter does not reach the limit. The next time when lsphp will be stopped, it will be unfrozen from the images.

The images of the processes will be saved even if NGINX is restarted. However, all images will be deleted after a server restart by default. This can be modified by setting the new path lsapi_criu_imgs_path .

Important!

If php.ini or the configuration file from php.d is changed, the images must be deleted manually.

Note

CRIU (version lower than criu-lve-3.6-1) can't correctly freeze lsphp with PrivateTmp enabled. For correct functionality, PrivateTmp must be disabled in nginx.service file .

To disable it, copy nginx.service to /etc/systemd/system and change there PrivateTmp:  

cat nginx.service  
............ 

[Unit]
Description=The nginx HTTP and reverse proxy server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=false

[Install]
WantedBy=multi-user.target
Alternatively, it could be technically preferable to provide a small override of the service file, rather than copying the whole new version in /etc/systemd/system

http://www.freedesktop.org/software/systemd/man/systemd.unit.html

mkdir /etc/systemd/system/nginx.service.d
echo "[Service]" >  /etc/systemd/system/nginx.service.d/nopt.conf
echo "PrivateTmp=false" >> /etc/systemd/system/nginx.service.d/nopt.conf

and

systemctl daemon-reload

CRIU Installation

CRIU is installed as a dependency to the nginx-mod-lsapi package. To activate it:

  1. Enable service and start it:
systemctl enable criu
systemctl start criu
  1. Edit lsapi.conf file, turn CRIU On and set some defaults:
lsapi_criu on;
lsapi_criu_socket_path /var/run/criu/criu_service.socket;
lsapi_backend_initial_start 15;
lsapi_criu_use_shm off;
lsapi_criu_debug off;
  1. Restart NGINX:
service nginx restart

CRIU Image Cleanup

  1. An option added to the NGINX configuration for cleaning all the images earlier saved by CRIU.
Option nameDescriptionValueDefault
lsapi_reset_criu_on_restartThis option allows cleaning all CRIU images on NGINX restart. on/off Off

On the next restart of NGINX all of the images will be cleaned.

It can be enabled by writing lsapi_reset_criu_on_restart on; in lsapi.conf

Note that this option works only if lsapi_terminate_backends_ex is on (default value is On , it is set in lsapi.conf too).

  1. If you need to clean CRIU images for one user you can simply add file to the user's directory with CRIU images (default /var/ngx_lsapi/criu/lsapi * criu_imgs ). On the next restart of lsphp the images will be cleaned.

  2. Global reset flag for cleaning all earlier saved images by CRIU.

Current nginx-mod-lsapi allows cleaning all images only with one flag file.

Create /usr/share/criu/mod_lsapi/lsphp.criu.reset file. Also don't forget to set permissions as follows: [nobody:nobody] (or [nginx:nginx] for non cPanel) and access mode [700] to the /usr/share/criu/mod_lsapi directory.

Steps to do :

mkdir /usr/share/criumkdir /usr/share/criu/mod_lsapi
chown nobody:nobody /usr/share/criu/mod_lsapi
touch /usr/share/criu/mod_lsapi/lsphp.criu.reset

Upon the next incoming request to all virtual hosts, images will be recreated (deleted first and created again later - it depends on lsapi_backend_initial_start value).

  1. Аdded possibility to clean CRIU images from user space.

If a user needs to clean CRIU images for lsphp, he should create a file: ~/mod_lsapi_reset_me_[server_name] . Where [server_name] is a server_name from the server block in the configuration file. On the next restart of lsphp, the images will be cleaned.

Example:

cd; touch mod_lsapi_reset_me_criu.test.com

where vhost.conf contains:
server_name criu.test.com;

This mode is enabled by default and creates a separate lsphp process for each virtual host.

mod_lsapi_reset_me[server_name] flag will not work for a user when lsapi_per_user option is on.

  1. There is an (default off) option in nginx-mod-lsapi that creates only one lsphp process for a user, regardless of the number of his virtual hosts. We don't recommend to use this option with CRIU, but if you use it, make sure that your virtual hosts (under the same user) have the same environment configurations. If they are not the same, this may cause undesirable lsphp process operations.

Additional integration components

CloudLinux OS Shared uses various ways to integrate with existing system.

LVE PAM module

pam_lve.so is a PAM module that sets up LVE environment. It provides easy way to setup LVE for SSH sessions, as well as other PAM enabled applications, such as crontab, su, etc.

pam_lve.so is installed by default when you convert existing server.

Installation:

yum install pam_lve

After you install RPM , add the following line to the PAM config file for the required application:

session    required     pam_lve.so 500 1 wheel,other

In this line:

  • 500 stands for minimum UID for which LVE will be setup. For any user with UID < 500, LVE will not be setup. If CageFS is installed, use: cagefsctl --set-min-uid UID to setup minimum UID. The parameter in PAM files will be ignored in that case.
  • 1 stands for CageFS enabled (0 – CageFS disabled)
  • 3rd optional argument defines group of users that will not be placed into LVE or CageFS. Starting with pam_lve 0.3-7 you can specify multiple groups, comma separated.

Warning

It is crucial to place all users that su or sudo to root into that group. Otherwise, once such user gains root, user will be inside LVE, and all applications restarted by that user will be inside that user LVE as well.

Warning

Please do not add the pam_lve.so in the PAM configuration for sudo. It can affect user’s work with the resource usage plugin, and Python/Node.js/PHP selector plugins.

For example, to enable LVE for SSH access, add that line to the /etc/pam.d/sshd. To enable LVE for SU, add that line to the /etc/pam.d/su.

By default, module will not place users with group wheel into lve. If you want to use different group to define users that will not be placed into LVE by pam_lve - pass it as the 3rd argument.

Warning

Be careful when you test it, as if you incorrectly add this line to the /etc/pam.d/sshd, it will lock you out ssh. Don't log out of your current SSH session, until you sure it works.

For preventing cases when user enters under usual user (using ssh) and then tries to enter as super user (via sudo or su) - pam_sulve was created, which tries to enter to LVE=1 and leaves it right away. If action fails, user gets message:

!!!!  WARNING: YOU ARE INSIDE LVE !!!!

To check if pam_sulve is enabled on the server:

grep pam_sulve.so /etc/pam.d/*

should not be empty.

LVE wrappers

LVE wrappers are the set of tools that allow system administrator to run various users, programs & daemons within Lightweight Virtual Environment. This allows system administrator to have control over system resources such program can have. Additionally it prevents misbehaving programs running within LVE to drain system resources and slow down or take down the whole system. The tools are provided by lve-wrappers RPM.

You can install them by running:

yum install lve-wrappers

Placing programs inside LVE

LVE Wrappers provide two tools for placing programs inside LVE: lve_wrapper and lve_suwrapper.

/bin/lve_wrapper can be used by any non-root user, as long as that user is in group lve (see /etc/groups file).

Syntax

lve_wrapper <command_to_run>

Example

lve_wrapper make install

The program will be executed within LVE with ID matching user's id.

/bin/lve_suwrapper can be used by root user or any user in group lve (see /etc/groups file) to execute command within specified LVE.

Syntax

lve_suwrapper LVE_ID <command_to_run>

Example

lve_suwrapper 10000 /etc/init.d/postgresql start

Switches

  • -f - force namespace
  • -n - without namespace
  • -c - remove CPU limits (requires lve_wrapper >= 2.1.2)

MPM ITK

CloudLinux OS Shared httpd RPM comes with MPM ITK built in. Yet, if you would like to build your own Apache, you need to apply our patch for MPM ITK

When running MPM ITK , you should disable mod_hostinglimits. All the functionality needed by MPM ITK is already built into the patch.

Directives which can be used by Apache with ITK patch:

  • AssignUserID - uses ID as LVE ID
  • LVEErrorCodeITK - error code to display on LVE error (508 by default)
  • LVERetryAfterITK - same as LVERetryAfter - respond with Retry-After header when LVE error 508 occurs
  • LVEId - ovverides id used for LVE ID instead of AssignUserID
  • LVEUser - overrides user to use to retrieve LVE ID, instead of AssignUserID

HostingLimits module for Apache

mod_hostinglimits works with existing CGI/PHP modules, to put them into LVE context. In most cases the CGI/PHP process will be placed into LVE with the ID of the user that sites belongs to. mod_hostinglimits detects the user from SuexecUserGroup (suexec module), SuPHP_UserGroup (from mod_suphp), AssignUserID (MPM ITK), RUidGid (mod_ruid2 ) directives.

This can be overwritten via LVEId or LVEUser parameter on the Directory level.

Note

Those parameters will not work with mod_fcgid and mod_cgid.

The order of detection looks as follows:

  • LVEId
  • LVEUser
  • SuexecUserGroup
  • suPHP_UserGroup
  • RUidGid
  • AssignUserID

Note

LVE doesn't work for mod_include #include due to its "filter" nature.

Example:

LoadModule hostinglimits_module modules/mod_hostinglimits.so
<IfModule mod_hostinglimits.c>
 AllowedHandlers cgi-script php5-script php4-script
 SecureLinks On
</IfModule>

Additional notes

mod_hostinglimits (since version 1.0-22) supports min-uid - cagefsctl --set-min-uid=600.

Min UID is read on Apache start/restart and stored in the memory during apache runtime.

If the min UID has changed, you should restart Apache for mod_hostinglimits applying new min UID value. Full min UID is supported only with APR.

The following message should appear:

[notice] mod_hostinglimits: found apr extention version 3.

This means that the correct APR is installed with mod_hostinglimits.

mod_hostinglimist has variable for Apache CustomLog format string %{LVE_ID}y.

How to use:

LogFormat

"%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i" req for lve "%{LVE_ID}y"

combined

shows in access_log the following info:

*.*.*.* - - [09/Apr/2015:07:17:06 -0400] "GET /1.php HTTP/1.1" 200 43435 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0" req for lve 500

*.*.*.* - - [09/Apr/2015:07:17:06 -0400] "GET /1.php?=PHPE9568F34-D428-11d2-A769-00AA001ACF42 HTTP/1.1" 200 2524 "************/1.php""Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0" req for lve 500

*.*.*.* - - [09/Apr/2015:07:17:06 -0400] "GET /1.php?=PHPE9568F35-D428-11d2-A769-00AA001ACF42 HTTP/1.1" 200 2146 "************/1.php""Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0" req for lve 500

Installation

cPanel

Installed by default during EasyApache build. Requires lve-stats & lve-utils packages to be installed.

DirectAdmin

Can be built using custombuild:

yum install liblve-devel
cd /usr/local/directadmin/custombuild
./build update
./build set cloudlinux yes
./build apache
./build rewrite_confs

If you run suphp, then run the following:

./build suphp

Plesk

yum install mod_hostinglimits

ISPmanager

yum install mod_hostinglimits

InterWorx

yum install mod_hostinglimits

H-Sphere

Included by default in H-Sphere 3.5+

Standard Apache from RPM

yum install mod_hostinglimits

Custom Apache installation

Compile from the source: https://repo.cloudlinux.com/cloudlinux/sources/mod_hostinglimits.tar.gz

wget https://repo.cloudlinux.com/cloudlinux/sources/mod_hostinglimits.tar.gz
yum install cmake
tar -zxvf mod_hostinglimits*.tar.gz
cd mod_hostinglimits*
cmake .
make
make install
  • Apache Module Identifier: hostinglimits_module
  • Source Files: mod_hostinglimits.c
  • Compatibility: MPM prefork, worker, event, ITK

Directives

DescriptionMakes sure that for any virtual hosts, only files owned by user specified via SuexecUserGroup or other ways as described above are served. For files owned by any other user apache will return Access Denied error. The directive will not affect VirtualHost without user id specified, or with uid < 100
SyntaxSecureLinks On
DefaultSecureLinks Off
Contextserver config

Prevents apache from serving files not owned by user, stopping symlink attacks against php config files.

Example

SecureLinks On

SkipErrors

DescriptionAllow apache to continue if LVE is not available
SyntaxSkipErrors On
DefaultSkipErrors On
Contextserver config

Prevents Apache from exiting if LVE is not available.

Example

SkipErrors Off

AllowedHandlers

DescriptionList of handlers that should be placed into LVE, support regexp
SyntaxAllowedHandlers cgi-script %^php% my-script
Defaultcgi-script %php% fcgid-script application/x-miva-compiled
Contextserver config

This directive allows to list handlers which will be intercepted and placed into LVE.

Examples

  • Match requests handled by cgi-script handler:

    AllowedHandlers cgi-script 
    
  • Match all requests:

    AllowedHandlers *
    
  • Match all requests that handled by handler that contains PHP:

    AllowedHandlers %php%
    
  • Match all requests handled by handler that starts with PHP:

    AllowedHandlers %^php%
    

The default modhostinglimits module configuration only handles cgi scripts and ignores static content like html files and images. With the default configuration, an Apache server denial of service situation can occur when there are many requests for large html files and images. But on the other hand, processing all static files is unprofitable, since when processing many small files, the load average will increase significantly due to the overhead of entering/exiting lve. Therefore, it is better to enable processing only for a subset of static files based on criteria such as file location or file name extension using Apache directives as Location, Directory and Files.

This functionality is available since version 1.0-40.

In the following example, the module hostinglimits will process files for a URLs starting with weightcontent, for files with an .avi extension, and for files from the video subdirectory.

<IfModule mod_hostinglimits.c>

 SkipErrors Off
 AllowedHandlers cgi-script %php% fcgid-script application/x-miva-compiled

<LocationMatch "^/weigthcontent/">
 AllowedHandlers *
</LocationMatch>

<Files "*.avi">
 AllowedHandlers *
</Files>

<DirectoryMatch "/home/user[0-5]/public_html/video/">
 AllowedHandlers *
</DirectoryMatch>

</IfModule>

DenyHandlers

DescriptionList of handlers that should not be placed into LVE, support regexp
SyntaxDenyHandlers text/html
Defaultnone
Contextserver config

This directive works together with AllowHandlers, to exclude some handlers from being allowed in LVE.

Example:

Match all requests, but text/*

AllowedHandlers *DenyHandlers %text/*%

LVEErrorCode

DescriptionError code to display once entry is rejected due to maxEntryProcs
Syntaxvalues from 500 to 510
Default508
Contextdirectory config

Specifies ErrorCode to use on LVE error (like too many concurrent processes running).

The message that will be displayed by default is:

Resource Limit Is Reached.

The website is temporarily unable to serve your request as it exceeded resource limit. 

Please try again later.

You can redefine error message using ErrorDocument directive

Example:

LVEErrorCode 508ErrorDocument 508 508.html

LVEid

DescriptionAllows to setup separate LVE ID on per directory level. If not set, user ID of a corresponding user is used.
SyntaxLVEId number
DefaultUser Id is used
Contextdirectory config

Specifies LVE id for particular directory

Example:

<Directory "/home/user1/domain.com/forums">
 LVEId 10001
</Directory>

LVEUser

DescriptionAllows to setup separate LVE ID on per directory level.
SyntaxLVEUser username
Defaultnone
Contextdirectory config

Specifies LVE ID for particular directory.

Example:

<Directory "/home/user1/domain.com/forums">
       LVEUser user1
</Directory>

LVEUserGroupID

DescriptionUse group ID instead of user ID for LVE container number.
SyntaxLVEUserGroupID On/Off
DefaultUser Id is used
Contextglobal config only
  • If the option enabled, group ID will be used instead of a user ID. Apache will display the following string in error logs:
mod_hostinglimits: use GroupID instead of UID 
mod_hostinglimits: found apr extension version 2 
mod_hostinglimits: apr_lve_environment_init_group check ok
  • If a compatible apr library is not found, the following error message will be display in error logs.
mod_hostinglimits:  apr_lve_* not found!!!

Example:

<Directory "/home/user1/domain.com/forums">
       LVEUserGroupID On
</Directory>

LVERetryAfter

DescriptionReturns Retry-After header when LVE error 508 occurs.
SyntaxLERetryAfter MINUTES
Default240 minutes
Contextdirectory config

Specifies interval for Retry-After header. The Retry-After response-header field can be used to indicate how long the service is expected to be unavailable to the requesting client.

Example:

LVERetryAfter 180

LVESitesDebug

DescriptionProvides extended debug info for listed sites.
SyntaxLVESitesDebug test.com test2.com
Default none
Contextdirectory config

Specifies virtual hosts to provide extra debugging information.

Example:

<Directory "/home/user1/domain.com/forums">
       LVESitesDebug abc.com yx.cnet
</Directory>

LVEParseMode

DescriptionDetermines the way LVE ID will be extraced. In Conf
SyntaxLVEParseMode CONF PATH OWNER REDIS
Default:CONF
Context:directory config
  • In CONF mode, standard way to extract LVE ID is used (SuexecUserGroup, LVEId, or similar directives).

  • In PATH mode, username is extracted from the home directory path. The default way to match username is via the following regexp: /home/([^/]*)/ . Custom regexp can be specified in LVEPathRegexp.

  • In OWNER mode, the owner of the file is used as an LVE ID.

  • In REDIS mode, LVE ID is retrieved from Redis database.

Example:

LVEParseMode CONF

LVEPathRegexp

DescriptionRegexp used to extract username from the path. Used in conjuction with LVEParseMode PATH
SyntaxLVEPathRegexp regexp
Default/home/([^/]*)/
Contextdirectory config

Used to extract usersname via path.

Example:

LVEPathRegexp /home/([^/]*)/

LVELimitRecheckTimeout

DescriptionTimeout in milliseconds, a site will return EP without lve_enter for LA decreasing after this time
SyntaxLVELimitRecheckTimeout number
Default0
Contexthttpd.conf, virtualhost

Example:

LVELimitRecheckTimeout 1000

LVEUse429

DescriptionUse 429 error code as code returned on max entry limits ( on/off ).
SyntaxLVEUse429 on
Defaultoff
Contexthttpd.conf, virtualhost

Example:

LVEUse429 on

Available for RPM based panels, EasyApache 4 and DirectAdmin.

cPanel/WHM JSON API

CloudLinux OS Shared offers JSON API for lvectl and cloudlinux-limits via WHM. You can access it using the following URL:

https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&command=lvectl&handler=list
https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&command=cloudlinux-limits&handler=get

The output will look as follows:

{"data":[{"ID":"default","CPU":"30","NCPU":"1","PMEM":"1024M","VMEM":"1024M","EP":"28","NPROC":"0","IO":"2048"}]}

Parameters

cgiactionalways jsonhandler
commandlvectl or cloudlinux-limits (default lvectl)
handlershould match lvectl or cloudlinux-limits command

For commands like set, destroy & delete, where you need to specify LVE (user) ID, like lveid=500 (matches user ID 500).

Example:

https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&command=lvectl&handler=set&lveid=500&speed=30%&io=2048

https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&command=lvectl&handler=set&lveid=500&speed=300Mhz&io=2048

https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&command=lvectl&handler=set&lveid=500&speed=3Ghz&io=2048

https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&command=cloudlinux-limits&handler=set&lveid=500&inodes=9090,8989

Note

Speed limit can be specified in several units of measure - %, MHz, GHz . The figures will be different according to the unit of measure.

Output:

{"status":"OK"}

To do set default, use lveid=0, like:

https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=set&lveid=0&speed=30%&io=2048

For commands like apply all, destroy all, use:

handler=apply-all

handler=destroy-all

You can use the following commands that allow to specify user name instead of user ID:

set-userSet parameters for a LVE and/or create a LVE using username instead of ID.
list-user List loaded LVEs, display username instead of user ID.
delete-user Delete LVE and set configuration for that user to defaults.

If the limits for users are set with cPanel LVE Extension, then turnkey billing solutions can be applied (e.g. WHMCS).

Manage reseller limits/users/packages via cPanel/WHM JSON API (JSONHandler)

Actionlvectl commandJSONHandler
enable reseller limitslvectl set-reseller res1 --speed=35%https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=set-reseller&lveid=res1&speed=30%&io=2048
disable reseller limitslvectl remove-reseller res1https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=remove-reseller&lveid=res1
set default limits for resellerlvectl set-reseller-default res1 --speed=91%https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=set-reseller-default&lveid=res1&speed=30%
set limits for package, created by resellerlvectl package-set res1_pack1 --reseller res1 --speed=88%https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=package-set&lveid=res1_pack1&reseller=res1&speed=30%
set limits for user, created by resellerlvectl set-user r1user1 --reseller res1 --speed=77%https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=set-user&lveid=r1user1&reseller=res1&speed=30%
set unlimited for user, created by resellerlvectl set-user r1user1 --reseller res1 --unlimitedhttps://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=set-user&lveid=r1user1&reseller=res1&unlimited
set inodes limitcloudlinux-limits set --username=r1user1 --inodes=9090,8989https://IP:2087/cpsess_YOURTOKEN/cgi/CloudLinux.cgi?cgiaction=jsonhandler&command=cloudlinux-limits&handler=set&username=r1user1&inodes=9090,8989

Using a WHM API token

You can use a WHM API token with curl as follows:

curl -X POST -k -s -H "Authorization: whm root:WHM_TOKEN" "https://SERVER_IP:2087/cgi/CloudLinux.cgi?cgiaction=jsonhandler&handler=LVE_METHOD&PARAMETERS

Where:

  • WHM_TOKEN – a generated WHM API token (see: Creating an API token)
  • LVE_METHOD – lvectl method (for example: list. See also: lvectl)
  • PARAMETERS – all other parametrs and options for a method according to the documentation

mod_proctitle

mod_proctitle is a module for gathering URL information per request. It is available only for Apache 2.4 now.

For installation:

cPanel EasyApache 3 and non cPanel ( CloudLinux 7 only for non cPanel ):

yum install mod_proctitle --enablerepo=cloudlinux-updates-testing
service httpd restart

cPanel EasyApache 4:

yum install ea-apache24-mod_proctitle
service httpd restart
DirectAdmin:
cd /usr/local/directadmin/custombuild
./build update
./build mod_procticle

How to read mod_proctitle information

How to read information gathered by the module

For reading information saved by module use the following script (the script is not in the package):

#!/bin/bash

httpd=httpd 

for pid in `/usr/bin/pgrep $httpd`; do
    for tid in `ls /proc/$pid/task`; do
		found=no
		for shm in `ls /dev/shm/apache_title_shm_${pid}_${tid}_* 2>/dev/null`; do
			found=yes
			title=`/usr/bin/tr -d '\0' < $shm`
			thread_id=`/bin/basename "${shm}" | sed "s/apache_title_shm_${pid}_${tid}_//"`
			echo "$pid.$tid - $thread_id - $title"
		break
		done
	if [ "$found" = "no" ]; then
		echo "$pid.$tid not found"
	fi
    done
done
Here are the examples of saved by module:
# sh proctitles_info.sh
571258.571258 NOT FOUND
571300.571300 NOT FOUND
571303.571303 - 000000000000000 - 1466513333.6 test.cloudlinux.com GET /1.php HTTP/1.1
571304.571304 - 000000000000000 - 1466513335.3 test.cloudlinux.com GET /1.php HTTP/1.1
571305.571305 - 000000000000000 - httpd
571306.571306 - 000000000000000 - httpd
571307.571307 - 000000000000000 - httpd
571372.571372 - 000000000000000 - httpd
571374.571374 - 000000000000000 - httpd

Item info:
[pid].[tid] - [posix thread id] - [request info]

Request information can contain:

NOT FOUND - means that process of Apache doesn't handle requests.
httpd - request is active and waiting for new connection.
[seconds].[tenths of second] [host] [METHOD] [URL] [PROTOCOL]

Tuning parameters

Module parameters for tuning

WatchHandlersList of handlers for monitoring (httpd.conf, virtualhost).
ProctitleUseFilter On/OffUse old method of cleaning information or new via filter (for prefork better to use Off )

alt-suexec

What is alt-suexec package needed for?

If you use standard httpd from our repository, but your users' sites do not match standard Apache location of /var/www, then you should use alt-suexec. alt-suexec package brings suEXEC binaries pre-compiled for specific locations, like /home .

How to switch suEXEC with alt-suexec

Based on httpd 2.2.16 basic for Cloudlinux OS Shared 6, httpd 2.4.6 basic for CloudLinux OS Shared 7 and httpd 2.4.37 basic for CloudLinux OS Shared 8, the package brings to a server a set of suEXECs with different DOCUMENT ROOTs and MIN_UID/MIN_GID parameters. The first set of suEXECs is listed by such modes:

switch_suexec -h  
............  

USE_BIZ - DOCUMENT ROOT /biz/ MIN_UID 500 MIN_GID 100 CALLER apache
USE_HOSTING - DOCUMENT ROOT /hosting/ MIN_UID 500 MIN_GID 100 CALLER apache
USE_HSPHERE - DOCUMENT ROOT /hsphere/local MIN_UID 100 MIN_GID 100 CALLER httpd
USE_HOME - DOCUMENT ROOT /home/ MIN_UID 500 MIN_GID 100 CALLER apache
USE_WWW - DOCUMENT ROOT /var/www/ MIN_UID 500 MIN_GID 100 CALLER apache
USE_FSROOT - DOCUMENT ROOT / MIN_UID 500 MIN_GID 100 CALLER apache
USE_STORAGE - DOCUMENT ROOT /storage/content/ MIN_UID 500 MIN_GID 100 CALLER apache
USE_DATAS - DOCUMENT ROOT /datas/ MIN_UID 500 MIN_GID 100 CALLER apache
The package also brings its own utility for installing specific suEXEC:
-llist of available suexec
-uupdate suexec according to /etc/sysconfig/alt-suexec
-sset new suexec and install it
-pset new suexec path and install it
-oset new suexec owners and install it
-rrestore native apache suexec

There are two ways to set up new suEXEC binary:

  1. via config file /etc/sysconfig/alt-suexec
  2. via utility switch_suexec

Here are the examples of how to set up suEXEC with DOC_ROOT = "/home":

1.

  1. add string "USE_HOME" to /etc/sysconfig/alt-suexec
  2. run the command switch_suexec -u

2.

  1. switch_suexec -sUSE_HOME

Result of both methods:

cat /etc/sysconfig/alt-suexec  
............  

USE_HOME

Here is standard suEXEC for CloudLinux OS Shared 6 clean server:

/usr/sbin/suexec -V  
............  

-D AP_DOC_ROOT="/var/www"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="apache"
-D AP_LOG_EXEC="/var/log/httpd/suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=500
-D AP_USERDIR_SUFFIX="public_html"
-D AP_SAFE_DIRECTORY="/usr/local/safe-bin"

Here is output of new suEXEC after USE_HOME installtion:

/usr/sbin/suexec -V  
............  

-D AP_DOC_ROOT="/home/"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="apache"
-D AP_LOG_EXEC="/var/log/httpd/suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=500
-D AP_USERDIR_SUFFIX="public_html"
-D AP_SAFE_DIRECTORY="/usr/local/safe-bin"

Description of other switch_suexec parameters:

-pif suexec binary file will be placed not in standard way /usr/sbin - specify this new path with p-option
-oif suexec binary file not owned by root:apache - specify new owner with o-option

For most cases -p and -o options for standard Apache are useless.

Correct suEXEC will be restored even after httpd update or reinstall.

List of pre-built suEXEC binary files stored without suid bit and not executable.

How to install alt-suexec?

For installation run the command:

yum install alt-suexec

New suexec with custom parameters

If you need suEXEC with custom parameters absent in current set of alt-suexec, please submit a ticket on https://cloudlinux.zendesk.com and we will add new suEXEC with needed parameters.

cPanel Nginx and application selectors

Recently, cPanel added support for the Nginx web server and for Python and Node.js applications.

We have checked the compatibility of ea-nginx and cPanel application Selectors with CloudLinux OS Shared LVE and CageFS. All tests passed successfully and all processes started by ea-nginx and cPanel selectors are launched inside LVE and CageFS.

Don’t forget, you can use Ruby/Python/Node.js Selectors from CloudLinux OS Shared. Here you can find a large number of supported versions for Ruby/Python/Node.js applications.

Note

Nginx support is currently experimental.

How to use Certbot with alt-python36

To run Certbot with alt-python36, follow the next steps:

  1. Add a path to the alt-python36 in the environment variable PATH as the first element: /opt/alt/python36/bin/.
  2. Run Certbot with the --no-bootstrap parameter.

Example:

The old command to run Certbot on CentOS 6/Cloudlinux OS Shared 6:

certbot-auto --nginx

The new command to run Certbot on CentOS 6/CloudLinux OS Shared 6:

PATH="/opt/alt/python36/bin/:$PATH" certbot-auto --no-bootstrap --nginx

Apache suexec module

General information and requirements

This module is used by the Apache HTTP Server to switch to another user before executing CGI programs. The suEXEC feature provides users of the Apache HTTP Server with the ability to run CGI and SSI programs under user IDs different from the user ID of the calling web server (apache/nobody). Normally, when a CGI or SSI program executes, it runs as the same user who is running the web server.

If we are talking about shared hosting where different accounts are launched on the same server, the installation of this module is necessary to ensure security.

How does it work with CloudLinux OS Shared?

The DirectAdmin and CloudLinux OS Shared (for httpd, httpd24-httpd and cPanel EasyApache 4) both provide a patched version of suexec. For other distributions you can use patches available here: https://repo.cloudlinux.com/cloudlinux/sources/da/cl-apache-patches.tar.gz

  1. Besides the ability to run CGI programs under user IDs, suexec with CloudLinux OS Shared patch adds the ability to run that script under CageFS.

NOTE

Therefore, this module is necessary for the proper work of PHP Selector.

  1. This module is also necessary for the proper work of mod_hostinglimits. The SuexecUserGroup directive indicates for mod_hostinglimits in which LVE the user process should be put in.

Configuration

SuexecUserGroup Directive

Syntax: SuexecUserGroup User Group

Context: httpd.conf, virtualhost

Description: The SuexecUserGroup directive allows you to specify a user and a group for CGI programs to run as. Startup will fail if this directive is specified but the suEXEC feature is disabled.

Note

Control panels such as cPanel, Plesk, and DirectAdmin add this directive to the Apache configuration automatically when creating a domain. If you use the server without a control panel, make sure this directive is added for each virtual host.

Installation

The mod_suexec installation process varies depending on the control panel and Apache.

Installing on cPanel servers with EasyApache 4

Via command line

  1. Install mod_suexec through YUM package manager as follows:
yum install ea-apache24-mod_suexec

NOTE

ea-apache24-mod_suexec conflicts with the mod_ruid2 therefore, before installing the module, remove ea-apache24-mod_ruid2 as follows: $ yum remove ea-apache24-mod_ruid2

  1. Now, when the module is installed, restart Apache:
service httpd restart

Note

If you use CageFS + PHP Selector, you should run the cagefsctl --force-update command.

Via administrator interface

  1. Open EasyApache4 page.
  2. Click Customize for Currently installed Packages.

  1. Click Apache Modules. Find mod_suexec and click Yes to install it.

  1. Select Review and Provision.

  1. Wait while Provision will be finished.

    Note

    If you use CageFS + PHP Selector, you should run the cagefsctl --force-update command.

Installing on Plesk servers

This module is integrated into Apache for Plesk control panel by default.

Installing on DirectAdmin servers

This module is integrated into Apache for DirectAdmin control panel by default.

Installing on servers with no control panel

This module is integrated into httpd Apache rpm provided by Cloudlinux OS Shared by default.

If you are using an alternative Apache - httpd24, nothing has to be done as this module is also integrated into httpd24-httpd Apache rpm provided by Cloudlinux by default.