QODDI DOCUMENTATION
PHP
Last updated

Qoddi automatically builds your PHP app from your code.
In order to do that, Qoddi uses buildpacks to detect and build your app automatically.
You can fork this repository to get started with PHP on Qoddi
PHP buildpack security policy
PHP builds require the patched buildpack and newer builder image, even when legacy is selected. Legacy fallback is disabled. PHP 8.1 and earlier, plus older patches of newer branches, are rejected. Use a PHP patch accepted by Qoddi’s current policy; the upstream version table below is not a list of minimum accepted patches.
PHP combined with Apt, FFmpeg, static, or certain other buildpacks is rejected. PHP combined with Node selects the newer Node buildpack too, so existing Node dependencies and asset build scripts may need updates.
The updated profile can report heroku/[email protected] and heroku/[email protected]. These are buildpack versions, not PHP versions. Default PHP Apache Procfile added. reports startup configuration; read the actual failure message in the complete build log to diagnose a failed build.
Read the PHP build security update and migration steps before rebuilding an affected app.
Before Building your code
Commit a composer.json at the repository root so the buildpack detects PHP. Commit composer.lock whenever you declare requirements, including PHP itself or extensions. Add a Procfile to select your web server and document root explicitly.
Create your app on Qoddi
Create a new app and link it with your repository using SSH keys.
Read this article for a detailed procedure or check this video :
Prepare your app
Use Composer 2 locally with the PHP version and extensions your application needs. Run composer install to install locked dependencies; when creating or changing requirements, run composer update, review the resulting lockfile, and test before committing both Composer files. Composer installs PHP packages; it does not install PHP extensions on your development machine.
See Composer dependency and lockfile management.
PHP Version
Set require.php in composer.json. For an application tested on PHP 8.5, use a minor-series constraint to accept patch updates:
{
"require": {
"php": "~8.5.0"
}
}~8.5.0 allows 8.5 releases below 8.6. Use ~8.4.0 if your application targets 8.4. A constraint such as ^8.4 also allows later PHP 8 minor releases. See Composer version constraints.
The following snapshot was checked on November 2, 2026 against Heroku’s PHP buildpack runtime list and PHP’s support calendar.
| PHP series | Listed buildpack patch | Upstream status | Security support ends |
|---|---|---|---|
| 8.5 | 8.5.12 | Active support | December 31, 2029 |
| 8.4 | 8.4.27 | Active support | December 31, 2028 |
| 8.3 | 8.3.36 | Security fixes only | December 31, 2027 |
| 8.2 | 8.2.35 | Security fixes only | December 31, 2026 |
PHP 8.1 and earlier are end-of-life. PHP 8.2 is still receiving security fixes until the date above; plan its upgrade.
Available binaries depend on the builder’s stack and buildpack release. Heroku lists 8.2 and 8.3 on stacks 22/24, and 8.4 and 8.5 on stacks 22/24/26. This upstream inventory is a reference, not confirmation of the image running on a particular Qoddi builder. Check the runtime selected in your build log.
Without a PHP requirement, current Heroku builds default to PHP 8.5, subject to extension constraints. Declare your tested version explicitly. After changing it, regenerate and commit composer.lock, rebuild, and check the PHP version reported in the Qoddi build log.
Procfile
The Heroku PHP buildpack provides these launch commands. For Apache with a public/ document root:
web: heroku-php-apache2 public/For Nginx:
web: heroku-php-nginx public/Omit public/ only if the repository root is the intended public directory. Older Qoddi builders documented qoddi-php-apache2 and qoddi-php-nginx aliases; use the executable supplied by your builder. The examples here use the upstream Heroku names.
Apache and Nginx patch releases are managed by the buildpack. See the current web server inventory and PHP launch configuration.
composer.json
A root-level composer.json is required for detection. A project with no requirements can use:
{}Only this empty configuration can omit composer.lock. Once you add PHP, extension, or library requirements, generate and commit the lockfile. Keep vendor/ out of Git; the build installs it from the lockfile without require-dev packages.
See PHP buildpack detection and lockfile requirements.
Built-in extensions
Declare every extension your application requires as an ext-* requirement, even when it is loaded by default. The buildpack resolves those requirements against the chosen PHP runtime. Use php -m locally to inspect your development environment. For the Qoddi build, review the build log for extension installation or dependency errors.
Consult the current built-in extension matrix for each PHP series.
Third-party extensions
Optional extension binaries vary with the PHP series, stack, and buildpack release. Check the third-party extension inventory before choosing a version constraint. An ext-* requirement cannot make an unavailable binary installable.
Using Optional Extensions
For example, an app that needs arbitrary-precision arithmetic and internationalization can declare:
{
"require": {
"php": "~8.5.0",
"ext-bcmath": "*",
"ext-intl": "*"
}
}Install these extensions locally too, refresh the lockfile, and rebuild. If resolution fails, inspect the missing extension and supported PHP combination in the build log. See Composer platform dependencies.
Private Composer repositories
To use private composer repositories such as Private Packagist, you need to pass Authentification values as an environment variable.
Make sure to add this environment variable before requesting the build of your app or the build will fail :
The COMPOSER_AUTH environment variable is similar to the auth.json variable used on development environments :

When using GitHub Enterprise or the Self-Managed version of GitLab, remember to also set the github-domains or gitlab-domains config option inside your project’s composer.json.
Composer configuration
$COMPOSER_MEMORY_LIMITdefaults to the available app memory;$COMPOSER_MIRROR_PATH_REPOSdefaults to1;$COMPOSER_NO_INTERACTIONdefaults to1.
App Dependencies
Load Composer’s autoloader to use installed PHP libraries. For an entry point inside public/:
require dirname(__DIR__) . '/vendor/autoload.php';Native PHP extensions are loaded by PHP itself; the Composer autoloader loads application and library classes.
Build Behavior
The following is run during build unless composer.json is empty and no composer.lock is present :
composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
Note: Qoddi will not install development dependencies from the require-dev section of composer.json.
Custom compile scripts
Custom compile scripts can be added into the composer.json in form of a single line or multiple arguments :
{
"scripts": {
"compile": [
"php app/console assetic:dump --env=prod --no-debug",
"MyVendor\\MyClass::postDeployComposerCallback"
]
}
}
The custom compile command will be executed by the builder like this :
composer compile --no-dev --no-interaction
Default Behavior
The Default web server is Apache2, you can use Nginx by editing the Procfile but .htaccess files are not available with Nginx.
Qoddi will launch a Fast CGI Process Managed and treats your Git root folder as the document root of the application, serving the .php files directly from that folder. Subsequent folders are served as well and you can alter this behavior using a .htaccess file.
Apache is set up with a Virtual Host that will respond to all hostnames. The document root is set up to be reachable without limitations and AllowOverride All is activated to allow for the use of .htaccess files.
Nginx uses a server that replies to all hostnames. The index directive is set to index.php index.html index.html
Apache Configuration (Not working with Nginx servers)
Similar to httpd.conf configuration you can create an apache_app.conf file inside the root directory of your app to alter the behavior of Apache. This can be especially useful if you need, for instance, to alter the behavior of the Proxy timeout settings (30 seconds by default). This file can also contain htaccess directives or any other directives you can set inside an httpd.conf file
Edit your Procfile to load the apache_app.conf file with the -C directive when your app starts :
web: heroku-php-apache2 -C apache_app.conf public/
Nginx Configuration (Not working with Apache Servers)
The default Nginx configuration file, similar to sites_availables, included with Qoddi PHP apps is basic :
location / {
index index.php index.html index.htm;
}
# for people with app root as doc root, restrict access to a few things
location ~ ^/(composer\.(json|lock|phar)$|Procfile$|<?=getenv('COMPOSER_VENDOR_DIR')?>/|<?=getenv('COMPOSER_BIN_DIR')?>/) {
deny all;
}
You can add a nginx_app.conf at the root directory of your app and add more directives at the startup of your app, for example :
client_max_body_size 256m;
More information about Nginx configuration files
Then edit the Procfile of your app to load this configuration file when your app starts with the -C directive :
web: heroku-php-nginx -C nginx_app.conf
php.ini configuration file
For per-directory PHP settings, place .user.ini in your document root (for example public/.user.ini). PHP-FPM reads it automatically; a -i launch flag is not required.
memory_limit = 256M
max_execution_time = 60Only settings permitted in user or per-directory contexts can be changed this way. Use an FPM include for settings that must be applied at startup. See PHP runtime configuration.
php-fpm configuration
Create fpm_custom.conf for startup settings, such as upload limits:
php_value[post_max_size] = 128M
php_value[upload_max_filesize] = 100MLoad it with the launch script’s -F option:
web: heroku-php-apache2 -F fpm_custom.conf public/FPM pool directives also belong in this file. Using -F does not disable per-directory .user.ini processing. Review the FPM configuration reference before changing startup settings.
Maximum Execution Time
Prefer background jobs for long-running tasks. PHP, the web server, and the platform router have separate timeout limits; increasing one does not change the others.
For an application that needs a longer PHP execution limit, configure fpm_custom.conf:
php_value[max_execution_time] = 300
request_terminate_timeout = 300sFor Apache, set apache_app.conf:
TimeOut 300
ProxyTimeout 300Load both files:
web: heroku-php-apache2 -C apache_app.conf -F fpm_custom.conf public/These settings do not override Qoddi’s external request limits. Use a background worker when the request cannot complete within the platform’s limits.
Launch a Console Command
Please check this article on how to launch a command directly to your running app. This can be used to perform database migrations, launch local scripts, or perform database backups.
Ephemeral Disk
Contrary to Marketplace apps that can use Qoddi’s virtual disks features to store and retrieve persistent data, apps that are created from code use an immutable file system.
You can write data locally, for instance, to process files temporarily during your application life, but the data written into your app’s disk is not persistent and gets wiped out after your app restart.
All apps restart at least once every 24 hours, to apply a new setting or to deploy a new build.
To store data and keep it persistent you can use external services like Amazon S3 or Wasabi, an S3 compatible storage service with very affordable pricing.
Add a domain name
Please check this article for a detailed method on how to use your domain name with Qoddi apps.
Define environment variables
Note: Environment variables set in your app settings are injected during the build process. If the build of your app needs to use an env variable make sure to set it before requesting the build.
Qoddi lets you externalize the configuration of your app : the Qoddi’s cluster will automatically injects the data when the app start or restart.
Environment variables can include any external data your app needs to run like external resources, databases addresses, encryption keys, etc…
To add an environment variable, access your app settings and click “Add ENV Variable” :

Enter the key (like DATABASE_USER) and the value and click “Add”. Qoddi will automatically add your environment variable and restart your app after a one-minute delay (to let you add more env variables if necessary without restarting the app each time a new variable is added).
Provision a database
Qoddi’s Marketplace includes all popular datastore engines including Mysql, Postgres, MongoDB, and Redis.
To add a database to your app, click on Marketplace and select the database you want to run, then create the new app database inside the same stack as your PHP app.
Once the app is running, visit the database app’s settings and retrieve the “internal name” :

Use this internal name as you would use a server address, for instance for MySQL this name replaces the usual “localhost”. Some scripts require you to add the database port at the end of the server address. Qoddi uses a common port for each database software, for MySQL it will be 3306 and you can write it like that inside your scripts: internal-name:3306
Postgres usually uses credentials sent inside the login URL, like that : postgres://<user>:<password>@<internalname>:5432/<database>
The login and password information is available inside the Environment Variables of the database app.
You can also choose a datastore to be deployed at the same time of your PHP app. In that case, Qoddi will automatically create the environment variables required for your app to access the database.
Note: Qoddi’s datastores run on a private and encrypted network. Therefore SSL support is deactivated for all datastores available on Qoddi. Ensure that your code doesn’t require using SSL before deploying your app.
Reach your app from another app
Inside your app stack, you can reach any other Qoddi app member of this stack on any port. Qoddi automatically routes the traffic internally and opens the appropriate port. By opposition, your app is only reachable on ports 80/443 from outside of Qoddi.
To reach your app internally, retrieve the internal name from your app settings page :

This name can be used to reach your app from another app inside the same stack. Use it in any form that works with your script :
http://<internalname>
<internalname>
http://<internalname>:port
Note: https:// is not available, all the traffic inside apps is already encrypted by the cluster.
Encryption
Qoddi uses at rest encryption for the app virtual disks, build images, and encrypted transport from inside the cluster. To make sure the traffic is encrypted between your app and the browser of your users you can set up SSL certificates for any domain name connected to the app (including Qoddi’s default domain name).
Scale your App
From your app settings you can scale your app vertically and horizontally: by upgrading the app size (refer to our pricing for app sizes details) or by adding more nodes.
Additional nodes will run another copy of your app inside another Qoddi server located inside the same cluster (same geographical location). Traffic is automatically sent to the least occupied node by Qoddi’s load balancer.