QoddiDocumentation
Browse documentation

QODDI DOCUMENTATION

Laravel

Last updated

Qoddi automatically builds your Laravel app from your code.
In order to do that, Qoddi uses buildpacks to detect and build your app automatically.

Before Building your code

To be able to build your app, Qoddi needs a composer.json and a composer.lock files committed in your repository. You should remove the vendor folder from your repository before building your app.

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

You can create a new laravel project locally by using composer create :

composer create-project laravel/laravel --prefer-dist test_qoddi

Then, initialize a git repository and commit your code :

git init
Initialized empty Git repository in ~/test_qoddi/.git/
git add .
git commit -m "new laravel project"

Procfile

Serve Laravel from public/ so configuration and source files are outside the document root. With the upstream Heroku PHP launch scripts:

web: heroku-php-apache2 public/

Or use Nginx:

web: heroku-php-nginx public/

Older Qoddi builders may expose qoddi-php-* aliases; use the command supplied by your builder. See PHP buildpack configuration for web servers and custom settings.

Setting a Laravel encryption Key

The application’s encryption key is used by Laravel to encrypt user sessions and other information. Its value will be read from the APP_KEY environment variable.

You can run php artisan key:generate --show locally, this will display a key you can add to your app environment variable BEFORE building your application for the first time. Environment variables can be set on the App settings page.

Configuring reverse proxy

Qoddi routes the request to your app through a layer of Nginx reverse proxies. This proxy is responsible, among other things, to route the SSL traffic and negotiate the SSL certificates.
This means that requests received by an app will always be made using the HTTP protocol, even if the original request was made over HTTPS.

Qoddi provides the original request information in X-Forwarded-… headers and since Laravel uses components from the Symfony framework for HTTP request handling, it can easily be configured to trust such headers.

Do not make Laravel trusting the Forwarded and X-Forwarded-Host headers, because Qoddi doesn’t set those headers.

PHP Version

As of November 2, 2026, Laravel 13 supports PHP 8.3–8.5; Laravel 12 supports PHP 8.2–8.5 and receives security fixes until February 24, 2027. Laravel 11 and earlier are past their published security-support window. Check Laravel’s support policy before upgrading.

For a Laravel application tested on PHP 8.5, change the php entry in your existing composer.json requirements to ~8.5.0. Keep all Laravel dependencies and scripts, update the lockfile locally, and test before rebuilding. See the PHP guide for runtime availability and extensions.

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_LIMIT defaults to the available app memory;
  • $COMPOSER_MIRROR_PATH_REPOS defaults to 1;
  • $COMPOSER_NO_INTERACTION defaults to 1.

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

Heroku-compatible PHP buildpacks can run a Composer compile script after dependency installation. Use commands appropriate to your Laravel version. A scripts fragment for view caching is:

{
  "scripts": {
    "compile": ["php artisan view:cache"]
  }
}

Merge it into the existing scripts object rather than replacing Laravel’s hooks. Build frontend assets with the Node buildpack when needed. Avoid database-dependent work during compilation; run migrations from the deployed application environment. See Laravel deployment.

Default Behavior

Keep the Laravel document root at public/ for both Apache and Nginx. Set APP_ENV=production, APP_DEBUG=false, and a persistent APP_KEY through Qoddi environment variables. Serve only public assets and route requests through Laravel’s front controller.

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 public/

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 = 60

Only 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] = 100M

Load 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 = 300s

For Apache, set apache_app.conf:

TimeOut 300
ProxyTimeout 300

Load 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’s 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 cluster will automatically inject the data when the app starts 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 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.

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.