Deploy PHP application

According to this comment on GitHub the openssl extension is not automatically available through the composer require behaviour due to an issue that is yet to be fixed. However, according to this issue there is a workaround whereby you create your own .ini file and enable the extension there:

diff --git a/.php.ini.d/extension.ini b/.php.ini.d/extension.ini
new file mode 100644
index 0000000..cb58b9c
--- /dev/null
+++ b/.php.ini.d/extension.ini
@@ -0,0 +1 @@
+extension=openssl.so

I ran a quick test and it seems to work – /calendar/proxy/vih loads, no error!

1 Like

@shrink. Thank you so much. You totally saved my weekend. Have a really good day.

1 Like

Hello guys, I’m having a problem trying to deploy a PHP application. I use the composer also in the project. I tried to use the example spoken by @shrink. However, it did not work.

The first form I tried to use the configuration in the fly.tom.

[Build]
     Builder = "PakeTobuildPacks/Builder-Jammy-Full"
     buildpacks = ["gcr.io/paketo-buildpacks/php"]

However, it presents the error below.

Paketo Buildpack for Composer Install 0.3.3
  Executing build process
  Building new layer /layers/paketo-buildpacks_composer-install/composer-packages
  Running 'composer config'
  
  Running 'composer install'
exit status 2
    Installing dependencies from lock file
    Verifying lock file contents can be installed on current platform.
    Your lock file does not contain a compatible set of packages. Please run composer update.

      Problem 1
        - dompdf/dompdf is locked to version v0.8.3 and an update of this package was not requested.
        - dompdf/dompdf v0.8.3 requires ext-mbstring * -> it is missing from your system. Install or enable PHP's mbstring extension.
      Problem 2
        - phenx/php-font-lib is locked to version 0.5.4 and an update of this package was not requested.
        - phenx/php-font-lib 0.5.4 requires ext-mbstring * -> it is missing from your system. Install or enable PHP's mbstring extension.

    To enable extensions, verify that they are enabled in your .ini files:
        - /layers/paketo-buildpacks_composer-install/composer-php-ini/composer-php.ini
        - /layers/paketo-buildpacks_php-dist/php/etc/buildpack.ini
        - /layers/paketo-buildpacks_php-dist/php/etc/php.ini
        - /layers/paketo-buildpacks_php-dist/php/etc/buildpack.ini
        - /layers/paketo-buildpacks_php-dist/php/etc/php.ini
    You can also run `php --ini` in a terminal to see which files are used by PHP in CLI mode.
    Alternatively, you can run Composer with `--ignore-platform-req=ext-mbstring` to temporarily ignore these required extensions.
ERROR: failed to build: exit status 1
Error failed to fetch an image or build from source: executing lifecycle: failed with status code: 51

Follow the settings of my Fly.Tom file

# fly.toml file generated for sig-example on 2023-02-16T15:21:58-03:00

app = "sig-example"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []

#[build]
#    builder = "paketobuildpacks/builder-jammy-full"
#    buildpacks = ["gcr.io/paketo-buildpacks/php"]

[env]
APP_URL = "https://sig-example.fly.dev"
PORT = 8080

[[services]]
    http_checks = []
    internal_port = 8080
    processes = ["app"]
    protocol = "tcp"
    script_checks = []
    [services.concurrency]
        hard_limit = 25
        soft_limit = 20
        type = "connections"

    [[services.ports]]
        force_https = true
        handlers = ["http"]
        port = 80
    
    [[services.ports]]
        handlers = ["tls", "http"]
        port = 443
    
    [[services.tcp_checks]]
        grace_period = "1s"
        interval = "15s"
        restart_limit = 0
        timeout = "2s"

Then I tried to deploy configuring a dockerfile file and commenting on the build part in Fly.tom

My dockerfile

# the PHP version from the user (wherever `flyctl launch` is run)
# Valid version values are PHP 8.1+
ARG PHP_VERSION=8.1
#FROM serversideup/php:${PHP_VERSION}-fpm-apache as base
#FROM fideloper/fly-laravel:${PHP_VERSION} as base
FROM serversideup/php:${PHP_VERSION}-fpm-apache as base

# PHP_VERSION needs to be repeated here
# See https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION

LABEL fly_launch_runtime="PHP Application"

# copy application code, skipping files based on .dockerignore
COPY . /var/www/html

RUN composer install --optimize-autoloader --no-dev \
    && chown -R www-data:www-data /var/www/html \
    && cp .fly/entrypoint.sh /entrypoint \
    && chmod +x /entrypoint

RUN mkdir /app

RUN mkdir -p  /app
WORKDIR /app
COPY . .
COPY --from=base /var/www/html/vendor /app/vendor

# From our base container created above, we
# create our final image, adding in static
# assets that we generated above
FROM base

EXPOSE 8080

ENTRYPOINT ["/entrypoint"]

This is the error when I try to deploy using Dockerfile.

Error failed to fetch an image or build from source: error building: failed to solve with frontend dockerfile.v0: failed to solve with frontend gateway.v0: rpc error: code = Unknown desc
 = failed to create LLB definition: circular dependency detected on stage: base

I tried to deploy these two ways, but neither funned. Can anybody help me?

That’s the key problem that we’ll need to resolve: you do not have (ext-)mbstring enabled. Fortunately, it’s included with the buildpack so you should be able to enable it using your composer.json by adding it to the require block. For example…

"require": {
    "ext-mbstring": "*"
 }

If you continue to experience problems after making that change, please share a link to your repository or share a copy of your composer.json here :slight_smile:

Thank you for your attention @shrink. I did what you told me, but the mistake still persisted. Follow the link from the repository, it has the composer file.

{
  "require": {
    "php": "~8.1",
    "phpmailer/phpmailer": "^6.0",
    "tburry/pquery": "^1.1",
    "picqer/php-barcode-generator": "^2.0.1",
    "dompdf/dompdf": "^1.0",
    "bacon/bacon-qr-code": "^2.0",
    "phprtflite/phprtflite": "^1.3",
    "firebase/php-jwt": "^5.0",
    "linfo/linfo": "^4.0",
    "adianti/plugins": "dev-master",
    "adianti/pdfdesigner": "dev-master",
    "pablodalloglio/ole": "dev-master",
    "pablodalloglio/spreadsheet_excel_writer": "dev-master",
    "pablodalloglio/fpdf": "dev-master",
    "ext-mbstring": "*",
    "ext-pdo_sqlite": "*",
    "ext-pdo_mysql": "*"
  },
  "config": {
    "preferred-install": {
      "*": "dist"
    }
  }
}

Hi @lordjackson, welcome to Fly.io!

Fly.io’s scanner already creates the necessary files for Laravel applications( a framework for PHP applications ) to get up and running in Fly! So one quick way you may get your PHP application running with Fly.io is to allow Fly.io to auto-generate “config” files for you, by making it think you have a Laravel application setup. To do so, you’ll have to have an artisan file in your root directory–its fine even if it’s empty–You can read more about the reason why you’d need the artisan file in our docs here.

Try out the steps below, and see if you can get your application deployed!

  1. Create an artisan file in your project directory with touch artisan
  2. Run the command fly launch- this should generate several files like fly.toml, and Laravel specific config file/folder: Dockerfile and a.fly directory
  3. Then, simply update your Dockerfile to just cater to PHP application, likeso:
# syntax = docker/dockerfile:experimental

# Default to PHP 8.2, but we attempt to match
# the PHP version from the user (wherever `flyctl launch` is run)
# Valid version values are PHP 7.4+
ARG PHP_VERSION=8.2
FROM fideloper/fly-laravel:${PHP_VERSION} 

# PHP_VERSION needs to be repeated here
# See https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION

LABEL fly_launch_runtime="laravel"

# copy application code, skipping files based on .dockerignore
COPY . /var/www/html

RUN composer install --optimize-autoloader --no-dev \
    && mkdir -p storage/logs \
    && php artisan optimize:clear \
    && chown -R www-data:www-data /var/www/html \
    && cp .fly/entrypoint.sh /entrypoint \
    && chmod +x /entrypoint


EXPOSE 8080

ENTRYPOINT ["/entrypoint"]
  1. Finally, deploy! fly deploy
1 Like

Thanks for your attention @kathrynannetan, I took the test you referred to me. Presented the following error below.

Unfortunately it didn’t work

WARN Failed to start remote builder heartbeat: failed building options: failed probing "personal": context deadline exceeded
Error failed to fetch an image or build from source: failed building options: failed probing "personal": context deadline exceeded

Hi @lordjackson ! Can you check the health of your fly environment with the following command: fly doctor? This thread might also help: Eror "Failed to start remote builder heartbeat: failed building options: failed probing "personal": context deadline exceeded"

Hi @lordjackson. @kathrynannetan is a Fly employee and I’m not, so I would trust their recommendations over mine, but if you decide to give the Paketo buildpack approach another try, the example in Deploy PHP application - #7 by shrink is not limited to just openssl.so. You can add other extensions into .php.ini.d/extension.ini as well. For example, if you want both openssl and mbstring, that ini file file could contain:

extension=openssl.so
extension=mbstring.so

Hi @Alex21!

There’s more than one way to deploy a PHP application with Fly.io, just as there is more than one path to a solution to this thread—Your recommendation is 200% just as valid as mine :raised_hands: That’s a great point out!

Also, all recommendations that lead to solving our community members’ post is as valid as any other, it builds our community, and are all very much appreciated. Thank you for sharing your solution!

1 Like

I’m trying to deploy a simple hello world PHP app :

<?php
echo 'Hello World';
?>

Dockerfile

FROM php:7.4-cli
RUN mkdir myProject
WORKDIR myProject
COPY . .
EXPOSE 8080
ENTRYPOINT ["php", "index.php"]
[error]Health check on port 8080 has failed. Your app is not responding properly. Services exposed on ports [80, 443] will have intermittent failures until the health check passes
curl --head https://myProject.fly.dev
curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to myProject.fly.dev:443 

Hey @anjanesh !

Not sure if you’ve gotten anywhere since you posted this, but the container you’re using (php:7.4-cli) doesn’t have a web server in it, so it won’t run PHP in a way that can respond to web requests.

You may have better luck using the official php image that includes apache. (php:7.4-apache - Docker )

That container will expect code to exist in /var/www/html (IIRC). You won’t need to use the EXPOSE keyword, but note that Apache will run on port 80, so the [services] portion of your fly.toml will need to use internal port 80 instead of 8080.

Ok, let me try using php:7.4-apache - but for the time being, on some other posts I found that this works.

[build]
  builder = "paketobuildpacks/builder-jammy-full"
  buildpacks = ["gcr.io/paketo-buildpacks/php"]

Is there an updated PHP build pack that you can suggest @shrink?

Tried to use this against a new deployment and running into a timeout error:

WARNING: image with reference "[gcr.io/paketo-buildpacks/php](https://gcr.io/paketo-buildpacks/php)" was found but does not match the specified platform: wanted linux/amd64, actual: linux

failed to fetch an image or build from source: failed to write image to the following tags: [pack.local/builder/7a6f7162756361636f66:latest: loading image "pack.local/builder/7a6f7162756361636f66:latest". first error: embedded daemon response: max depth exceeded]

That’s a weird one, usually you see that when wanting an amd64 container but running on a arm64 machine (or visa-versa). Is that error message cut off at all? Or is actual: linux the whole thing?

I wonder if max depth exceeded is the true issue tho, that sometimes happens with some unintended recursion (you usually see it when docker-compose.yml is used and tries to name a built image to the same name of an existing image on docker hub). Perhaps your fly.toml config is off.

@fideloper-fly

Here is what my fly.toml file looks like:

# fly.toml app configuration file generated for yova on 2024-02-01T16:31:22Z
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = 'yova'
primary_region = 'iad'
kill_signal = 'SIGINT'
kill_timeout = '5s'

[experimental]
  auto_rollback = true

[build]
  builder = 'paketobuildpacks/builder-jammy-full'
  buildpacks = ['gcr.io/paketo-buildpacks/php']

[env]
  APP_URL = 'https://yova.fly.dev'
  PORT = '8080'

[[services]]
  protocol = 'tcp'
  internal_port = 8080
  processes = ['app']

[[services.ports]]
    port = 8080
    handlers = ['http']
    force_https = true

[[services.ports]]
    port = 443
    handlers = ['tls', 'http']

  [services.concurrency]
    type = 'connections'
    hard_limit = 25
    soft_limit = 20

[[services.tcp_checks]]
    interval = '15s'
    timeout = '2s'
    grace_period = '1s'

A note to Fly.io staff: I wonder how many Fly.io customers are struggling with PHP buildpack quirks, and if a simpler-to-Dockerize way of running a web server with PHP, such as FrankenPHP (docker docs) would be a better default recommendation. I think that would make for a good blog post on Laravel Bytes if you have anyone available to write up the details such as how to best configure the Caddyfile, etc. Just my 2 cents.

3 Likes

Hey there!

Are you deploying a Laravel app or something else?

We have a lot of tooling around Laravel that can be re-used for other PHP apps, which creates a Dockerfile rather than using a buildpack.

One “hack” to see this is to create an empty artisan file, and running fly launch to see the Dockerfile (etc) that it creates. You might want to do this in a separate branch of your code so you don’t mess up the current (working?) configuration.

You’ll end up (I believe) with a Dockerfile and some files in a .fly directory. It assumes the web root is /var/www/html/public. It also may attempt to run some artisan commands (specific to Laravel) that you can remove in the .fly/scripts directory, and in 2 spots in the generated Dockerfile.

You can review the files created here: flyctl/scanner/templates/laravel at master · superfly/flyctl · GitHub

1 Like

(Also I do have plans to play with FrankenPHP, especially as an Octane driver, but perhaps also as just a general Docker setup on Fly.io)

This is a legacy PHP application. I’m not using Larvel but will try this out and see if there is a migration path forward.