Percio Castelo.
PT Home
Fixing the upload limit in CloudLinux + DirectAdmin: why php.ini does not update and how to force it via CLI
← Back to blog

Fixing the upload limit in CloudLinux + DirectAdmin: why php.ini does not update and how to force it via CLI

10/28/2026 · 4 min · Servers · 897 views

You need to import a large database dump, restore a massive WordPress backup, or let users upload files exceeding 1GB. You open the configuration file, edit php.ini, save, restart services, and test again. To your frustration, the application still claims the file exceeds the upload limit, and phpinfo() continues to display the old baseline values.

On servers running the DirectAdmin and CloudLinux stack with PHP Selector and CageFS, this is a classic stumbling block. CloudLinux’s layered architecture does not directly parse traditional global /etc/php.ini files for individual tenant accounts. Furthermore, subtle PHP byte shorthand notation traps and Nginx buffer restrictions quietly block large payloads before they ever reach your scripts.

Here is a practical breakdown of how this configuration architecture works, how to avoid syntax pitfalls, and how to reliably force updates from the command line.


1. Where does CloudLinux keep PHP directives?#

In standard shared hosting setups, PHP directives usually reside in predictable system-wide configuration files. Under the CloudLinux PHP Selector, however, every user account is assigned an isolated file system jail (CageFS) and the ability to choose an independent PHP version with custom runtime directives.

When you inspect a user's home directory (e.g., formula), you will notice a specific layout:

[root@server formula]# ls -la
total 92
drwx--x---    12 formula access   4096 Sep 16  2025 .
drwx--x--x. 1433 root    root    40960 May  8 16:55 ..
drwxr-xr-x     2 formula formula   59 May 11 10:42 .cl.selector
drwxrwx---     2 formula apache   138 May 11 00:26 .php
lrwxrwxrwx     1 formula formula   48 May 29  2025 public_html -> ./domains/mysite.com/public_html

Breaking down those hidden directories:

  • .php/: Contains execution logs, such as php-mail.log, and transient session storage. The PHP runtime does not read configuration directives from here.
  • .cl.selector/: This is the control directory. It stores account overrides generated whenever settings are saved in the web control panel.

Inside .cl.selector/, configuration files correspond to the active PHP version:

[root@server formula]# ls .cl.selector/
alt_php74.cfg  defaults.cfg

The file alt_php74.cfg (where 74 denotes PHP 7.4) holds custom user overrides:

;>=== Start of PHP Selector Custom Options ===
upload_max_filesize=1G
;<=== End of PHP Selector Custom Options =====

2. The decimal syntax trap in PHP configuration#

Suppose your application requires accepting file uploads up to 1.6 GB. The intuitive reaction is to enter:

upload_max_filesize = 1.6G

While readable, PHP's configuration parser does not support floating-point numbers in shorthand byte values (K, M, G). When the parser hits the period (.) in 1.6G, it truncates the input unpredictably, frequently falling back to 1G or even single bytes on legacy releases.

Converting values to megabytes#

To configure exactly 1.6 GB cleanly, convert the target into whole Megabytes:

$$1.6 \times 1024 = 1638.4 \text{ MB} \approx 1638\text{M}$$

The memory directive hierarchy#

Increasing upload_max_filesize on its own will not work. File uploads travel via HTTP POST requests and consume script execution memory. If neighboring thresholds are lower, PHP aborts the request before file processing completes.

Always maintain this operational rule:

$$\text{memory\_limit} \ge \text{post\_max\_size} \ge \text{upload\_max\_filesize}$$

For a 1.6 GB target, the aligned configuration should look like:

upload_max_filesize = 1638M
post_max_size = 1640M
memory_limit = 1700M

3. Why manual edits to .cfg files fail to take effect#

If you edit /home/formula/.cl.selector/alt_php74.cfg directly with an editor, save the file, and refresh your browser, phpinfo() will likely continue reporting the old values.

This occurs because CloudLinux Selector treats the .cfg file as a persistent metadata manifest rather than an active runtime configuration file. The actual .ini files loaded by PHP-FPM and Apache are compiled inside the user's CageFS mount points.

Unless you notify the selector binary to rebuild this compiled structure, manual filesystem changes remain ignored.

Applying changes via CLI: selectorctl#

Attempting undocumented arguments like --replace-config-file triggers usage syntax errors:

[root@server .cl.selector]# selectorctl --user formula --replace-config-file --version=7.4
Usage: /usr/bin/selectorctl [OPTIONS]

The native argument supported by CloudLinux is --replace-options (or -m), which accepts a comma-separated list of key:value pairs:

selectorctl --user formula --version 7.4 --replace-options "upload_max_filesize:1638M,post_max_size:1640M,memory_limit:1700M"

Restarting user PHP-FPM workers#

Even after rebuilding configuration manifests, running PHP-FPM master pools may retain previous parameters in worker memory. Clear active processes for that specific user:

killall -u formula php-fpm

Synchronizing CageFS mount points#

Finally, ensure the user's isolated virtual filesystem maps the updated PHP symlinks correctly:

cagefsctl --update formula

4. Validating settings via CLI without browser caching#

Testing through a web browser can be misleading due to proxy caches, CDN buffering, or cached static headers. The most reliable way to verify active values is to query PHP directly under the user's security identity:

su - formula -s /bin/bash -c "php -i | grep upload_max_filesize"

The output should confirm the target value across both local and master scopes:

upload_max_filesize => 1638M => 1638M

5. The hidden proxy bottleneck (Nginx)#

If phpinfo() reports 1638M, but uploading your 1.6 GB file immediately produces an HTTP 413 Request Entity Too Large error, the file was blocked before reaching PHP.

When Nginx serves as a reverse proxy in front of Apache, Nginx intercepts incoming HTTP traffic first. By default, Nginx enforces a conservative client_max_body_size limit of 1MB or 10MB. Any payload exceeding that limit is dropped at the edge.

Adjusting Nginx body size in DirectAdmin#

  1. Log in to DirectAdmin with administrative privileges (Admin).
  2. Go to Custom HTTPD Configurations.
  3. Select the domain hosting the upload application.
  4. Locate the nginx.conf customization block and add the following directive:
client_max_body_size 1700M;
  1. Save the configuration and restart Nginx via terminal to apply the updated buffer rules:
systemctl restart nginx

Operational checklist for frictionless large file uploads#

When tuning upload thresholds in CloudLinux and DirectAdmin environments, follow this concise troubleshooting sequence:

  1. Avoid decimals: Convert your intended target to whole Megabytes ($1.6\text{ GB} = 1638\text{M}$).
  2. Align memory ceilings: Ensure that memory_limit (e.g., 1700M) $\ge$ post_max_size (e.g., 1640M) $\ge$ upload_max_filesize (e.g., 1638M).
  3. Use selectorctl instead of manual edits: Apply parameters cleanly with selectorctl --user USER --version X.Y --replace-options "...".
  4. Kill stale worker processes and sync CageFS: Run killall -u USER php-fpm followed by cagefsctl --update USER.
  5. Inspect the edge proxy: If an HTTP 413 error appears, update client_max_body_size inside Nginx to match your payload expectations.

Following these steps ensures consistent, reliable file transfers without unexpected timeouts or silent configuration drops.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

897 reads
Creative Commons Attribution-NonCommercial

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments