Heroku PHP Support
Last updated November 22, 2024
Table of Contents
This document describes the general behavior of Heroku as it relates to the recognition and execution of PHP applications.
Activation
The Heroku PHP Support will be applied to applications only when the application has a file named composer.json
in the root directory. Even if an application has no Composer dependencies, it must include at least an empty ({}
) composer.json
in order to be recognized as a PHP application.
When Heroku recognizes a PHP application, it will respond accordingly during a push:
$ git push heroku main
-----> PHP app detected
…
If composer.json
specifies dependencies of any kind in its require
section, the corresponding composer.lock
that gets generated by running composer update
must also be committed to the repository, or the push will be rejected. This ensures that the dependencies Heroku installs are exactly the same as in any other environment. Please refer to the “Manage Dependencies” section of the Deploying PHP guide for detailed instructions.
PHP runtimes
Heroku allows you to run your application using the official PHP runtime.
Supported versions
Heroku’s PHP support extends to applications using the latest available releases in the PHP 8.1, PHP 8.2, and PHP 8.3 series.
The support for PHP release series on the Heroku platform follows the PHP Group’s support policy, typically with active updates for two years after an initial x.y.0 version, followed by two years of security updates.
Once a PHP release series has reached end-of-life, it will no longer be supported by Heroku, but its latest release will remain available for builds to allow customers to upgrade their applications to a newer PHP version.
PHP 8.1 is in security-only maintenance mode and will be fully end-of-life at the end of 2023. Only critical security fixes will be provided by the PHP maintainers for this release series. Users are strongly encouraged to update their applications to PHP 8.3 at their earliest convenience. For more information on support timelines for PHP releases, refer to the Supported Versions page on the official PHP website.
Available versions
The following table lists the runtime versions that are available for builds on each stack.
PHP 7 and PHP 8.0 are end-of-life. No more bugfixes, including critical security fixes, will be provided by the PHP maintainers for these version series, and Heroku does not provide support for applications using these releases. Users are strongly encouraged to update their applications to PHP 8.3 at their earliest convenience. For more information on support timelines for PHP releases, refer to the Supported Versions page on the official PHP website.
Runtime / series | heroku-20 |
heroku-22 |
heroku-24 |
---|---|---|---|
PHP 7.3 | 7.3.33 | - | - |
PHP 7.4 | 7.4.33 | - | - |
PHP 8.0 | 8.0.30 | - | - |
PHP 8.1 | 8.1.31 | 8.1.31 | - |
PHP 8.2 | 8.2.26 | 8.2.26 | 8.2.26 |
PHP 8.3 | 8.3.14 | 8.3.14 | 8.3.14 |
Rows marked up with yellow text and background indicate a PHP release series that is only receiving security updates from the upstream maintainers. Rows marked up with red text and background indicate a PHP release series that is fully end-of-life and no longer receiving updates of any kind from the upstream maintainers and is no longer supported by Heroku.
Runtime settings
All PHP runtimes use the respective release’s php.ini-production
file as their base php.ini
configuration.
Notwithstanding the above, the following INI directives are set to Heroku-specific values:
date.timezone
is set toUTC
error_reporting
is set toE_ALL & ~E_STRICT
for PHP versions before 8.1E_ALL & ~E_STRICT & ~E_DEPRECATED
for PHP 8.1 or later
expose_php
is set toOff
session.sid_length
is set to32
(for PHP 7.1 and later)short_open_tag
is set toOn
user_ini.cache_ttl
is set to86400
variables_order
is set toEGPCS
In addition, the PHP runtimes always have OPcache enabled for improved performance, with the following configuration changes optimized for the specific characteristics of Heroku’s dynos:
opcache.enable_cli
is set to1
opcache.validate_timestamps
is set to0
PHP CLI
The memory_limit
PHP INI directive for the php
CLI executable defaults to the full available dyno memory for PHP versions 7.2 or later. This means that e.g. worker dynos using a php
command use this memory limit instead of the default PHP INI value of 128M
.
Default runtime
Applications that do not use a composer.json
, or where composer.lock
contains no requirements for package php
even in any dependent package, will pick the latest possible version of either PHP 7 or PHP 8 on the heroku-20
stack, and PHP 8 on all other stacks.
Selecting a runtime
You may select the runtime(s) to use via Composer Platform Packages in composer.json
. Upon a push, Heroku will read the necessary information from composer.lock
, if present, and fall back to composer.json
otherwise.
For example, the following composer.json
will instruct Heroku to use the latest version of PHP 8 greater or equal to 8.2.0, but not PHP 9:
{
"require": {
"php": "^8.2.0"
}
}
Never specify an exact version like “8.2.13
” for PHP, or any other package.
Instead, use the “^
” or “~
” next significant release operators to ensure that you get appropriate updates upon push as they become available.
For PHP, that means specifying e.g. “~8.2.0
” will always get you the latest 8.2.x release, which will be fully compatible with other releases from the 8.2 series (but may contain security or performance updates), but not 8.3.0 or later.
To get the latest PHP 8.2 or later (including PHP 8.3, 8.4, etc.) but not PHP 9, you’d specify “^8.2.0
”.
Heroku will print the versions that were resolved and will be installed:
-----> Installing platform packages...
- php (8.2.13)
Specifying an unknown or unsupported version will result in an error listing potential alternative versions.
PHP
Specify “php
” as a dependency in the require
section of your composer.json
to use PHP as the runtime; for instance, for PHP 8.1 or later:
{
"require": {
"php": "^8.1.0"
}
}
It is recommended you always prefix the minimum version you’d like to receive with the ^
selector. This ensures that you’ll receive updated versions as they become available. In the example above, you’d get PHP 8.1.0 or later, including newer versions in the 8.x series, but not PHP 9 once it’s released (which you may want to test your application against first in case of any unexpected backwards compatibility issues).
Next, ensure that your new requirements are “frozen” to composer.lock
by running:
$ composer update
Finally, don’t forget to git add
and git commit
both files!
Upgrades
If you deploy an application that does not declare a runtime version dependency, the then-latest version of PHP will be used. Your application will be upgraded to more recent versions of PHP if available automatically upon the next deploy.
If your application declares runtime version dependencies, the most recent version matching the version constraint will be selected for installation.
Extensions
Available built-in extensions
The following table lists which of the built-in extensions that are bundled with PHP are available for each release series of PHP, and whether the extension in question is loaded by default, or whether it has to be explicitly enabled via composer.json
.
Extension | PHP 7.3 | PHP 7.4 | PHP 8.0 | PHP 8.1 | PHP 8.2 | PHP 8.3 |
---|---|---|---|---|---|---|
ext-bcmath |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-bz2 |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-calendar |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-ctype |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-curl |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-date |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-dom |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-exif |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-fileinfo |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-filter |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-ftp |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-gd |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-gettext |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-gmp |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-hash |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-iconv |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-imap |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-intl |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-json |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-ldap |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-libxml |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-mbstring |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-mysqli |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-mysqlnd |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-openssl |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-pcntl |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-pcre |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-pdo |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-pdo_mysql |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-pdo_pgsql |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-pdo_sqlite |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-pgsql |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-phar |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-posix |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-random |
- | - | - | - | ✔ | ✔ |
ext-readline |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-reflection |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-session |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-shmop |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-simplexml |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-soap |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-sockets |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-sodium |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-spl |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-sqlite3 |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-tokenizer |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-xml |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-xmlreader |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-xmlrpc |
✔ | ✱ | - | - | - | - |
ext-xmlwriter |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-xsl |
✔ | ✱ | ✱ | ✱ | ✱ | ✱ |
ext-zend-opcache |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-zip |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
ext-zlib |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
✔: enabled by default ✱: optional, can be enabled via composer.json |
Columns marked up with yellow text and background indicate a PHP release series that is only receiving security updates from the upstream maintainers. Columns marked up with red text and background indicate a PHP release series that is fully end-of-life and no longer receiving updates of any kind from the upstream maintainers and is no longer supported by Heroku.
Available third-party extensions
The following table lists which third-party extensions are available for each release series of PHP. As these are distributed independently of the PHP runtime, their versions are also listed. They are never loaded by default and thus have to be explicitly enabled via composer.json
.
Extension | PHP 7.3 | PHP 7.4 | PHP 8.0 | PHP 8.1 | PHP 8.2 | PHP 8.3 |
---|---|---|---|---|---|---|
ext-amqp (1.x) |
1.11.0 | 1.11.0 | 1.11.0 | 1.11.0 | 1.11.0[3] | - |
ext-amqp (2.x) |
- | - | 2.1.2 | 2.1.2 | 2.1.2 | 2.1.2 |
ext-apcu |
5.1.24 | 5.1.24 | 5.1.24 | 5.1.24 | 5.1.24 | 5.1.24 |
ext-blackfire |
1.92.28 | 1.92.28 | 1.92.28 | 1.92.28 | 1.92.28 | 1.92.28 |
ext-cassandra |
1.3.2 | - | - | - | - | - |
ext-ev |
1.1.5 | 1.1.5 | 1.2.0 | 1.2.0 | 1.2.0 | 1.2.0 |
ext-event (2.x) |
2.5.7 | 2.5.7 | - | - | - | - |
ext-event (3.x) |
3.1.4 | 3.1.4 | 3.1.4 | 3.1.4 | 3.1.4 | 3.1.4 |
ext-imagick |
3.7.0 | 3.7.0 | 3.7.0 | 3.7.0 | 3.7.0 | 3.7.0 |
ext-memcached |
3.3.0 | 3.3.0 | 3.3.0 | 3.3.0 | 3.3.0 | 3.3.0 |
ext-mongodb |
1.16.2 | 1.20.0 | 1.20.0 | 1.20.0 | 1.20.0 | 1.20.0 |
ext-newrelic (9.x) |
9.21.0.311 | 9.21.0.311 | 9.21.0.311 | 9.21.0.311 | - | - |
ext-newrelic (10.x) |
10.22.0.12 | 10.22.0.12 | 10.22.0.12 | 10.22.0.12 | 10.22.0.12 | 10.22.0.12 |
ext-newrelic (11.x) |
11.3.0.16 | 11.3.0.16 | 11.3.0.16 | 11.3.0.16 | 11.3.0.16 | 11.3.0.16 |
ext-oauth |
2.0.9 | 2.0.9 | 2.0.9 | 2.0.9 | 2.0.9 | 2.0.9 |
ext-pcov |
1.0.11 | 1.0.11 | 1.0.11 | 1.0.11 | 1.0.11 | 1.0.11 |
ext-phalcon (4.x) |
4.1.3 | 4.1.3 | - | - | - | - |
ext-phalcon (5.x) |
- | 5.4.0 | 5.8.0 | 5.8.0 | 5.8.0 | 5.8.0 |
ext-pq |
2.2.3 | 2.2.3 | 2.2.3 | 2.2.3 | 2.2.3 | 2.2.3 |
ext-psr |
1.2.0 | 1.2.0 | 1.2.0 | 1.2.0 | 1.2.0 | 1.2.0 |
ext-raphf |
2.0.1 | 2.0.1 | 2.0.1 | 2.0.1 | 2.0.1 | 2.0.1 |
ext-rdkafka (4.x) |
4.1.2 | 4.1.2 | - | - | - | - |
ext-rdkafka (5.x) |
5.0.2 | 5.0.2 | 5.0.2 | 5.0.2[2] | - | - |
ext-rdkafka (6.x) |
6.0.5 | 6.0.5 | 6.0.5 | 6.0.5 | 6.0.5 | 6.0.5 |
ext-redis (5.x) |
5.3.7 | 5.3.7 | 5.3.7 | 5.3.7 | 5.3.7[3] | - |
ext-redis (6.x) |
6.0.2 | 6.1.0 | 6.1.0 | 6.1.0 | 6.1.0 | 6.1.0 |
ext-uuid |
1.2.1 | 1.2.1 | 1.2.1 | 1.2.1 | 1.2.1 | 1.2.1 |
[1]: This extension is not available on the heroku-20 stack.[2]: This extension is not available on the heroku-22 stack.[3]: This extension is not available on the heroku-24 stack.
|
Columns marked up with yellow text and background indicate a PHP release series that is only receiving security updates from the upstream maintainers. Columns marked up with red text and background indicate a PHP release series that is fully end-of-life and no longer receiving updates of any kind from the upstream maintainers and is no longer supported by Heroku.
Using optional extensions
You may declare any optional extensions you want to use via composer.json
using Composer Platform Packages; simply prefix any of the identifiers in the list of extensions above with “ext-
” in the package name.
For example, to enable the optional bundled extensions bcmath and GMP
, as well as the third-party Memcached, and a particular version of the third-party MongoDB:
{
"require": {
"ext-bcmath": "*",
"ext-gmp": "*",
"ext-memcached": "*",
"ext-mongodb": "^1.19.0"
}
}
It is recommended that you use “*
” as the version selector when specifying extensions that are bundled with PHP, as their version numbers can be highly inconsistent (they often report their version as “0”).
Next, ensure that your new requirements are “frozen” to composer.lock
by running:
$ composer update
Finally, don’t forget to git add
and git commit
both files!
If you do not have the desired extension available locally on your computer, the composer update
step would fail because the requirements in composer.json
cannot be satisfied.
If you cannot install the missing extension on your computer using pecl
, brew
, or similar methods (something you absolutely should do in the interest of maintaining dev/prod parity), you can instruct composer to ignore the missing (so-called “platform”) requirements:
$ composer update --ignore-platform-reqs
The same --ignore-platform-reqs
flag may also be used when running composer install
on subsequent dependency installations in new environments, e.g. on other developers’ computers, where the extension is similarly unavailable.
Upon the next push, Heroku will install and enable the corresponding PHP extensions:
-----> Installing platform packages...
- php (8.2.13)
- ext-bcmath (bundled with php)
- ext-mcrypt (bundled with php)
- ext-mongodb (1.17.0)
- ext-memcached (3.2.0)
- apache (2.4.58)
- nginx (1.24.0)
Any PHP extension required by a dependency of a project pushed to Heroku will be installed automatically, as the list of extensions to be installed is read from composer.lock
.
For example, if a project depends on the stripe/stripe-php
PHP SDK for Stripe, the mbstring
extension required by the Stripe SDK will automatically be installed upon deploy, without the ext-mbstring
package having to be listed explicitly in the require
section of your main composer.json
.
Treatment of extensions “provided” by userland packages
Certain Composer packages such as the various Symfony Polyfills declare a native PHP extension as provide
d in their package metadata (and implement the functionality of that extension, partially or completely, purely using PHP code), which causes Composer to consider them a possible replacement of the real, native PHP extension during the resolution of dependencies.
During a build, these “polyfill” declarations are honored by Heroku when installing platform packages, using the exact same rules that Composer applies to the installation of packages.
This means that a requirement of composer.json
(or any dependency) for e.g. ext-mbstring
would not lead to the installation of the native ext-mbstring
extension if the symfony/polyfill-mbstring
package is also present in composer.lock
, as symfony/polyfill-mbstring
declares ext-mbstring
as provide
d:
-----> Installing platform packages...
- php (8.1.2)
- apache (2.4.52)
- composer (2.2.5)
- nginx (1.20.2)
For maximum performance and compatibility, after the initial resolving of platform package dependencies has finished in this manner, Heroku’s PHP Support will then attempt to install native versions of all extensions that userland packages have declared as provide
d:
-----> Installing platform packages...
- php (8.1.2)
- apache (2.4.52)
- composer (2.2.5)
- nginx (1.20.2)
NOTICE: detected userland polyfill packages for PHP extensions
NOTICE: now attempting to install native extension packages
Installing extensions provided by symfony/polyfill-mbstring:
- ext-mbstring (bundled with php)
These installation attempts may not always be successful, as the extension in question may not be available on Heroku, or for the selected PHP version:
-----> Installing platform packages...
- php (8.1.2)
- apache (2.4.52)
- composer (2.2.5)
- nginx (1.20.2)
NOTICE: detected userland polyfill packages for PHP extensions
NOTICE: now attempting to install native extension packages
Installing extensions provided by phpseclib/mcrypt_compat:
NOTICE: no suitable native version of ext-mcrypt available
In particular, no changes will be made to platform packages that have already been resolved: if a native variant is not available for the installed PHP version, no downgrade to a PHP version for which such an extension is available will be performed, even if that lower PHP version would be permitted by all other dependencies.
This behavior ensures that userland polyfill packages can correctly serve their purpose where necessary, such as when acting as a replacement for an extension that is no longer bundled with PHP (like e.g. phpseclib/mcrypt_compat
does for ext-mcrypt
in the example above), while simultaneously ensuring that the native PHP extension in question is installed whenever possible for maximum performance and compatibility.
Customizing settings
PHP
Any .user.ini
file that is placed into a project according to the instructions in the PHP manual will be loaded after the main php.ini
. You can use these to set any directive permitted in PHP_INI_ALL
, PHP_INI_USER
and PHP_INI_PERDIR
contexts.
For additional details on this and other ways of customizing settings for the PHP runtime, please refer to the corresponding Dev Center article.
Build behavior
Installation of dependencies
The following command is run during a deploy to resolve dependencies unless composer.json
is empty and no composer.lock
is present:
$ composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
Heroku will not install development dependencies from the require-dev
section of composer.json
. However, if the require-dev
section contains a PHP runtime version requirement or lists a dependency that contains such a requirement, then the require
section of composer.json
must also contain a PHP runtime version requirement or list a dependency that contains such a requirement. This is to ensure that Heroku does not select a default PHP runtime version that conflicts with what other environments (which include require-dev
dependencies) install.
The installed version of Composer will be printed for your reference before installation begins. Builds are run using the a Composer version (1.x, 2.2.x LTS, or 2.3+) that is compatible with an application’s composer.lock
. The respective version of Composer is available on $PATH
at app runtime under the command name composer
.
An application’s Composer cache directory is persisted between builds to speed up package installation on subsequent deploys.
Available Composer versions
The following Composer versions are currently available:
Composer / series | heroku-20 |
heroku-22 |
heroku-24 |
---|---|---|---|
Composer 1.x | 1.10.27 | - | - |
Composer 2 LTS | 2.2.24 | 2.2.24 | 2.2.24 |
Composer 2.x | 2.8.3 | 2.8.3 | 2.8.3 |
Composer 2.2 is the Long-Term Support (LTS) series of Composer, and will be used for builds of applications with a lock file generated by Composer 2.0, 2.1, or 2.2, or by applications using PHP versions older than 7.2.5.
Custom compile step
For applications that wish to execute an additional compilation step during a build that shouldn’t be part of a standard post-install-cmd
Composer script, for example an asset compilation or cache pre-warming procedure, a compile
custom command, if present in composer.json
, will be executed using the following command:
$ composer compile --no-dev --no-interaction
Any such custom script command defined in composer.json can either be a single string, or an array of multiple commands to execute; example:
{
"scripts": {
"compile": [
"@php app/console assetic:dump --env=prod --no-debug",
"MyVendor\\MyClass::postDeployComposerCallback"
]
}
}
If you need to execute php
or composer
in any Composer script, always reference the executables using the @php
or @composer
shorthand notations. This will always call the correct PHP or Composer executable in any environment, and also ensure any Composer configuration, including the right PHP memory_limit
, is applied.
Composer’s bin-dir
is pushed on top of $PATH
during command execution, making binaries installed by dependencies easily accessible as CLI commands when writing scripts without having to use vendor/bin/
or a similar prefix.
Private repositories
To use private repositories such as Private Packagist, or packages from a source that requires authentication (such as from a private GitHub repository), Composer must be provided with authentication details (typically a token generated by the provider or service).
On a development machine, these are typically stored by Composer in auth.json
, but on Heroku, such secrets are stored as environment variables. The COMPOSER_AUTH
environment variable is automatically read by Composer; its JSON structure is identical to auth.json
.
The following entries are allowed as top-level keys in the JSON document:
Each entry then contains a hash of domains as keys and authentication details as values; the authentication detail structure is specific to each of the sources above and described in the documentation.
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
.
For example, to store authentication details for a Private Packagist, account, you’d set the COMPOSER_AUTH
variable using heroku config:set
with http-basic
details (replacing “YOURTOKEN” with the actual token Private Packagist generated, of course):
$ heroku config:set COMPOSER_AUTH='{"http-basic":{"repo.packagist.com":{"username":"token","password":"YOURTOKEN"}}}'
To give another example, when using code from private GitHub repositories as Composer dependencies, a personal OAuth token can be set for authentication. After creating a new Token, you can set it on Heroku (replacing “YOURTOKEN” with the actual token GitHub generated, of course):
$ heroku config:set COMPOSER_AUTH='{"github-oauth":{"github.com":"YOURTOKEN"}}'
The private repository URL in your composer.json
must use the https://
and not the git://
protocol for Composer to be able to use the OAuth token for authentication.
Several sets of authentication details can of course also be combined into a single document; for example, to use both private GitHub and private BitBucket repositories:
$ heroku config:set COMPOSER_AUTH='{
"github-oauth": {"github.com": "YOURTOKEN"},
"bitbucket-oauth": {"bitbucket.org": {
"consumer-key": "YOURKEY", "consumer-secret": "YOURSECRET"}
}
}'
You may use line breaks within quotes when setting environment variables on Heroku as shown in the example above, but you must ensure that the quoting is correct when running the heroku config:set
command.
Composer configuration
For convenience, the following settings for Composer are automatically set using environment variables:
$COMPOSER_MEMORY_LIMIT
defaults to the available dyno memory;$COMPOSER_MIRROR_PATH_REPOS
defaults to1
;$COMPOSER_NO_INTERACTION
defaults to1
.
Runtime behavior
The $PATH
environment variable contains all necessary paths for an application to function at runtime. The Composer bin-dir
is appended to $PATH
for convenience.
PHP-FPM configuration
PHP-FPM is set up to automatically spawn a suitable number of worker processes depending on dyno size and the configured PHP memory_limit
. Please refer to the Optimizing PHP Application Concurrency article for more details.
Timeouts
When a request reaches the Heroku router’s request timeout, a PHP-FPM process would continue to run, potentially as long as it takes for e.g. an external timeout to occur. This would tie up that PHP-FPM process, which could then no longer respond to other incoming requests.
For applications using PHP 7.4 or later, by default, PHP-FPM will therefor
- log a backtrace of requests that take longer than three seconds (
request_slowlog_timeout
directive), and - terminate requests that have exceeded an execution time of 30 seconds (
request_terminate_timeout
directive) and therefor likely timed out.
You may adjust the settings for PHP-FPM to change these (or other) configuration settings.
Composer configuration
For convenience, the following settings for Composer are automatically set using environment variables:
$COMPOSER_MEMORY_LIMIT
defaults to the available dyno memory;$COMPOSER_MIRROR_PATH_REPOS
defaults to1
;$COMPOSER_NO_INTERACTION
defaults to1
;$COMPOSER_PROCESS_TIMEOUT
defaults to0
.
Web servers
Heroku supports Apache HTTPD 2.4 and Nginx as dedicated Web servers. For testing purposes, users may of course also use PHP’s built-in Web server, although this is not recommended.
In the absence of a Procfile
entry for the “web” dyno type, the Apache Web server will be used together with the PHP runtime.
The following web server versions are supported, and will automatically be installed during a build:
Web Server / series | heroku-20 |
heroku-22 |
heroku-24 |
---|---|---|---|
Apache 2.x | 2.4.62 | 2.4.62 | 2.4.62 |
Nginx 1.x | 1.26.2 | 1.26.2 | 1.26.2 |
Apache
Apache interfaces with PHP-FPM via FastCGI using mod_proxy_fcgi
.
To start Apache together with PHP-FPM and all the correct settings, use the heroku-php-apache2
script:
web: heroku-php-apache2
By default, the root folder of your project will be used as the document root. To use a sub-directory, you may pass the name of a sub-folder as the argument to the boot script, e.g. “public_html”:
web: heroku-php-apache2 public_html
You can use regular .htaccess
files to customize Apache’s behavior, e.g. for URL rewriting. For additional details on this and other options to customize settings for Apache, please refer to the corresponding Dev Center article.
Nginx
Nginx interfaces with PHP-FPM via FastCGI.
To start Nginx together with PHP-FPM and all the correct settings, use the heroku-php-nginx
script:
web: heroku-php-nginx
By default, the root folder of your project will be used as the document root. To use a sub-directory, you may pass the name of a sub-folder as the argument to the boot script, e.g. “public_html”:
web: heroku-php-nginx public_html
For additional details on different ways of customizing settings for Nginx, please refer to the corresponding Dev Center article.
PHP Built-in Web server
For testing purposes, you may start PHP’s built-in Web server by using php -S 0.0.0.0:$PORT
as the entry for “web” in your Procfile
:
web: php -S 0.0.0.0:$PORT
The Procfile
must contain $PORT
in the line shown above. It’s used by Heroku at runtime to dynamically bind the web server instance to the correct port for the dyno.
It is important to bind to all interfaces using 0.0.0.0
, otherwise Heroku’s routing won’t be able to forward requests to the web server!
You may also pass an alternative document root, or use a so called router script to process requests. For details, please refer to the PHP project’s documentation for the built-in Web server.