Heroku PHP Support
Last updated 08 January 2021
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 master
-----> 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 runtimes
Heroku’s PHP support extends to applications using PHP 7.3, PHP 7.4 and PHP 8.0.
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 a year of security updates.
Stack-specific notes
- the Argon2 password hashing algorithm (supported by PHP 7.2 and later) is available on stack
heroku-18
and later only; - the
sodium
extension (bundled with PHP 7.2 and later) is not available on the deprecatedcedar-14
stack.
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
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 a configuration optimized for the specific characteristics of Heroku’s dynos:
opcache.enable_cli
is set to1
opcache.validate_timestamps
is set to0
opcache.memory_consumption
is set to128
(for PHP 5)opcache.interned_strings_buffer
is set to8
(for PHP 5)opcache.max_accelerated_files
is set to4000
(for PHP 5)
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, 7.3 and 7.4. 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 PHP 5 on the heroku-16
stack and the (deprecated) cedar-14
stack, and the latest possible version of PHP 7 on the heroku-18
and later stacks.
Applications that have suitably permissive version selectors specified for package php
in composer.json
and all dependencies in composer.lock
will use PHP 7 if all required extensions are also available for that version (see below).
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 7 greater or equal to 7.2.0, but not PHP 8 (once that would be released):
{
"require": {
"php": "^7.2.0"
}
}
Never specify an exact version like “7.4.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. “~7.3.0
” will always get you the latest 7.3.x release, which will be fully compatible with other releases from the 7.3 series (but may contain security or performance updates), but not 7.4.0 or later.
To get the latest PHP 7.4 or later, but not PHP 8, you’d specify “^7.4.0
”.
Heroku will print the versions that were resolved and will be installed:
-----> Installing platform packages...
- php (7.4.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 7.4 or later:
{
"require": {
"php": "^7.4.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 7.4.0 or later, including newer versions in the 7.x series, but not PHP 8 (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!
Supported versions
PHP
The following PHP versions are available on Heroku and receive regular updates:
- 7.3.26
- 7.4.14 (not available on the deprecated
cedar-14
stack) - 8.0.1 (available on the
heroku-18
andheroku-20
stacks)
PHP 7.3 is in security-only maintenance mode and will be fully end-of-life at the end of 2021. 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 7.4 or later at their earliest convenience. For more information on support timelines for PHP releases, refer to the Supported Versions page on the official PHP website.
Legacy versions
PHP
The following legacy PHP versions are still available for builds on Heroku, but no longer receive updates of any kind:
- 5.5.38 (deprecated
cedar-14
stack only) - 5.6.39 (deprecated
cedar-14
stack andheroku-16
stack only) - 7.0.33 (deprecated
cedar-14
stack andheroku-16
stack only) - 7.1.33 (not available on the
heroku-20
stack) - 7.2.34 (not available on the
heroku-20
stack)
PHP 5.5, 5.6, 7.0, 7.1 and 7.2 are end-of-life. No more bugfixes, including critical security fixes, will be provided by the PHP maintainers for these versions. Users are strongly encouraged to update their applications to PHP 7.4 or later at their earliest convenience. For more information on support timelines for PHP releases, refer to the Supported Versions page on the official PHP website.
Upgrades
If you deploy an application that does not declare a runtime version dependency, the then-latest version of PHP 7 will be used. Your application will be upgraded to more recent versions of PHP 7 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
PHP 7.3
The following built-in extensions are enabled automatically on Heroku (this list does not include extensions that PHP enables by default, such as DOM, JSON, PCRE or PDO):
- Bzip2
- cURL
- FPM
- MySQL (PDO) (uses mysqlnd)
- MySQLi (uses mysqlnd)
- OPcache
- OpenSSL
- PostgreSQL
- PostgreSQL (PDO)
- Readline
- Sockets
- Zip
- Zlib
The following built-in extensions have been built “shared” and can be enabled through composer.json
(internal identifier names given in parentheses):
- BCMath (
bcmath
) - Calendar (
calendar
) - Exif (
exif
) - FTP (
ftp
) - GD (
gd
; with PNG, JPEG and FreeType support) - GMP (
gmp
) - gettext (
gettext
) - IMAP (
imap
; with SASL support) - intl (
intl
) - LDAP (
ldap
; with SASL support) - mbstring (
mbstring
) - PCNTL (
pcntl
) - Shmop (
shmop
) - SOAP (
soap
) - Sodium (
sodium
; available on theheroku-16
and later stacks only) - SQLite3 (
sqlite3
) - SQLite (PDO) (
pdo_sqlite
) - XMLRPC (
xmlrpc
) - XSL (
xsl
)
The following third-party extensions can be enabled through composer.json
(internal identifier names given in parentheses):
- AMQP (
amqp
; available on theheroku-16
and later stacks only) - APCu (
apcu
; provides anapc
extension for compatibility with legacy software) - Blackfire (
blackfire
) - Cassandra (
cassandra
) - ev (
ev
) - event (
event
) - ImageMagick (
imagick
) - memcached (
memcached
; built against a version of libmemcached with SASL support) - MongoDB (
mongodb
) - New Relic (
newrelic
; will automatically be enabled when the New Relic Add-On is detected during a build) - OAuth (
oauth
) - pcov (
pcov
) - Phalcon (
phalcon
) - pq (
pq
) - psr (
psr
) - rdkafka (
rdkafka
) - PHPRedis (
redis
) - UUID (
uuid
)
PHP 7.4
The following built-in extensions are enabled automatically on Heroku (this list does not include extensions that PHP enables by default, such as DOM, JSON, PCRE or PDO):
- Bzip2
- cURL
- FPM
- MySQL (PDO) (uses mysqlnd)
- MySQLi (uses mysqlnd)
- OPcache
- OpenSSL
- PostgreSQL
- PostgreSQL (PDO)
- Readline
- Sockets
- Zip
- Zlib
The following built-in extensions have been built “shared” and can be enabled through composer.json
(internal identifier names given in parentheses):
- BCMath (
bcmath
) - Calendar (
calendar
) - Exif (
exif
) - FTP (
ftp
) - GD (
gd
; with PNG, JPEG and FreeType support) - GMP (
gmp
) - gettext (
gettext
) - IMAP (
imap
; with SASL support) - intl (
intl
) - LDAP (
ldap
; with SASL support) - mbstring (
mbstring
) - PCNTL (
pcntl
) - Shmop (
shmop
) - SOAP (
soap
) - Sodium (
sodium
; available on theheroku-16
and later stacks only) - SQLite3 (
sqlite3
) - SQLite (PDO) (
pdo_sqlite
) - XMLRPC (
xmlrpc
) - XSL (
xsl
)
The following third-party extensions can be enabled through composer.json
(internal identifier names given in parentheses):
- AMQP (
amqp
; available on theheroku-16
and later stacks only) - APCu (
apcu
; provides anapc
extension for compatibility with legacy software) - Blackfire (
blackfire
) - ev (
ev
) - event (
event
) - ImageMagick (
imagick
) - memcached (
memcached
; built against a version of libmemcached with SASL support) - MongoDB (
mongodb
) - New Relic (
newrelic
; will automatically be enabled when the New Relic Add-On is detected during a build) - OAuth (
oauth
) - pcov (
pcov
) - Phalcon (
phalcon
) - pq (
pq
) - psr (
psr
) - rdkafka (
rdkafka
) - PHPRedis (
redis
) - UUID (
uuid
)
PHP 8.0
The following built-in extensions are enabled automatically on Heroku (this list does not include extensions that PHP enables by default, such as DOM, JSON, PCRE or PDO):
- Bzip2
- cURL
- FPM
- MySQL (PDO) (uses mysqlnd)
- MySQLi (uses mysqlnd)
- OPcache
- OpenSSL
- PostgreSQL
- PostgreSQL (PDO)
- Readline
- Sockets
- Zip
- Zlib
The following built-in extensions have been built “shared” and can be enabled through composer.json
(internal identifier names given in parentheses):
- BCMath (
bcmath
) - Calendar (
calendar
) - Exif (
exif
) - FTP (
ftp
) - GD (
gd
; with PNG, JPEG and FreeType support) - GMP (
gmp
) - gettext (
gettext
) - IMAP (
imap
; with SASL support) - intl (
intl
) - LDAP (
ldap
; with SASL support) - mbstring (
mbstring
) - PCNTL (
pcntl
) - Shmop (
shmop
) - SOAP (
soap
) - Sodium (
sodium
; available on theheroku-16
and later stacks only) - SQLite3 (
sqlite3
) - SQLite (PDO) (
pdo_sqlite
) - XSL (
xsl
)
The following third-party extensions can be enabled through composer.json
(internal identifier names given in parentheses):
- APCu (
apcu
; provides anapc
extension for compatibility with legacy software) - Blackfire (
blackfire
) - event (
event
) - MongoDB (
mongodb
) - OAuth (
oauth
) - pcov (
pcov
) - pq (
pq
) - psr (
psr
) - PHPRedis (
redis
) - UUID (
uuid
)
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 bcmath, MCrypt, Memcached, and the third-party MongoDB:
{
"require": {
"ext-bcmath": "*",
"ext-mcrypt": "*",
"ext-memcached": "*",
"ext-mongodb": "^1.1.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 (7.4.13)
- ext-bcmath (bundled with php)
- ext-mcrypt (bundled with php)
- ext-mongodb (1.9.0)
- ext-memcached (3.1.5)
- apache (2.4.46)
- nginx (1.18.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
.
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 currently run using either Composer version 1.10.19 or Composer version 2.0.8, depending on whether composer.lock
was generated using Composer 1 or Composer 2.
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.
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"
]
}
}
Composer’s bin-dir
is pushed on top of $PATH
during command execution, so binaries installed by dependencies are 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 2.4 (2.4.43) and Nginx 1.18 (1.18.0) 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.
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: vendor/bin/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: vendor/bin/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 documentation for the built-in Web server.