FFMpeg/FFProbe cannot load binaries ("Unable to load FFProbe")

Hi everyone!

I just got started with Fly.io recently (after moving from Heroku). I know it’s not officially supported, but I migrated a small Symfony app. It mostly works fine, except when it come to file conversion.

I create an instance of FFMpeg, passing it the correct path of the installed binaries (I checked the paths by printing which ffmpeg/ffprobe here):

FFMpeg::create([
            'ffmpeg.binaries' => $this->ffmpegBinaryPath,
            'ffprobe.binaries' => $this->ffprobeBinaryPath,
            'timeout' => 3600,
            'ffmpeg.threads' => 0,
        ]);

However, this is met with the following exception:

"message": "Uncaught PHP Exception FFMpeg\\Exception\\ExecutableNotFoundException: \"Unable to load FFProbe\" at /var/www/html/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Driver/FFProbeDriver.php line 50",
"context": {
  "exception": {
    "class": "FFMpeg\\Exception\\ExecutableNotFoundException",
    "message": "Unable to load FFProbe",
    "code": 0,
    "file": "/var/www/html/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Driver/FFProbeDriver.php:50",
    "previous": {
      "class": "Alchemy\\BinaryDriver\\Exception\\ExecutableNotFoundException",
      "message": "Executable not found, proposed : /usr/bin/ffprobe",
      "code": 0,
      "file": "/var/www/html/vendor/php-ffmpeg/php-ffmpeg/src/Alchemy/BinaryDriver/AbstractBinary.php:159"
    }
  }
},

As for the installation, my Dockerfile is very basic:

ARG PHP_VERSION=8.1
FROM serversideup/php:${PHP_VERSION}-fpm-nginx-v1.5.0 as base

ARG PHP_VERSION

LABEL fly_launch_runtime="symfony"

RUN apt-get update && apt-get install -y \
    git curl zip unzip rsync ca-certificates vim htop cron ffmpeg \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
...

It’s worth noting that I can do the file conversion using system('ffmpeg ...'), meaning ffmpeg is definitely installed correctly and the binaries should have the required executable permissions.

I’ve spent around 2 days combing the internet, trying to find a solution but coming up short. Does anyone here perhaps have any ideas? I would really appreciate any help!

Hi,

Just to clarify, you say you can do the file conversion using ffmpeg, however … that specific exception references ffprobe .e.g “Unable to load FFProbe”. That’s what it is complaining about, from what I can see :thinking:. It should respect the binary paths you have provided, but would it be worth editing your Dockerfile to mv the ffmpeg and ffprobe binaries to e.g /usr/bin/ffprobe and /usr/bin/ffmpeg? That appears to be where it is defaulting to expect them, based on the exception message at least. You can check that by SSH-ing into the vm and running which to double-check. If that works, then you can move to putting them in a custom place. Or not and just leave them there.

1 Like

Hi @greg! Thanks for the response. Yes, that is correct, it’s complaining about not finding FFProbe. However, running both which ffmpeg and which ffprobe via ssh returns the correct paths:

# which ffmpeg
/usr/bin/ffmpeg
# which ffprobe
/usr/bin/ffprobe
# 

Logging which ffprobe in the PHP application also returns the correct paths (as mentioned, running the conversion using system('ffmpeg...') also works), so the binaries are definitely there. For some reason, it’s only the php-ffmpeg library that cannot load FFProbe, and I have no idea why :person_shrugging:

Do you think moving/copying the binaries to a different directory might work?

Hi,

Well that’s odd!

If they are in the default place, you could try commenting out the paths in the config and so leave it to find ffmpeg/ffprobe itself. Which it should be able to as they are not in a custom place. Can’t hurt to let it see.

If it still can’t, and it’s only PHP that can’t find/run them, two possible thoughts (total guesses!):

  1. If in your php.ini the open_basedir is not commented out, that would enforce the directories it can use, and so that may need the paths adding to it e.g open_basedir="/tmp:/usr/share/php:/usr/bin/ffmpeg:/usr/bin/ffprobe". If it is commented out, it’s not that.

  2. Maybe permissions? PHP will likely be run as a different user and so it may be that it does not have permission to execute ffmpeg/ffprobe, which may be reporting/appearing as a “not found” issue.

Again, thanks for the quick response!

I have tried the default paths (with the same results, unfortunately).

  1. I have checked the php.ini, and open_basedir is indeed commented out (;open_basedir =)
  2. Running whoami in my PHP application displays webuser. Out of desperation, I have used chmod 777 /usr/bin/ffmpeg and chmod 777 /usr/bin/ffprobe which did not work either.

This is a really strange issue indeed…

1 Like

No problem :slightly_smiling_face:

Not open_basedir then.

Looking at FFProbeDriver, it in turn uses AbstractBinary.php, and so this seems to be where the issue lies:

PHP (when run as your webuser) is either getting false for file_exists or for is_executable. Or both. Resulting in null, resulting in that particular exception. The question is why if the file is there and is executable :thinking:.

Perhaps try making a copy (either manually or in your Dockerfile) sticking the ffmpeg and ffprobe binaries in some other place, as you suggest. Like /usr/local/bin/ffmpeg and /usr/local/bin/ffprobe.

Then sudo chmod +x /usr/local/bin/ffmpeg and sudo chmod +x /usr/local/bin/ffprobe.

So then they are there,and are executable (and edit the paths in your Config to be those absolute ones instead).

Hi @greg!

Alright, I gave you suggestion a try, but still no luck :frowning:

My Dockerfile:

RUN apt-get update && apt-get install -y \
    git curl zip unzip rsync ca-certificates vim htop cron ffmpeg \
    && cp /usr/bin/ffmpeg /usr/local/bin/ffmpeg \
    && cp /usr/bin/ffprobe /usr/local/bin/ffprobe \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

And then I manually checked that the binaries are in the new directory location (/usr/local/bin) and changed the permissions. The same exception with a different proposed directory:

"message": "Uncaught PHP Exception FFMpeg\\Exception\\ExecutableNotFoundException: \"Unable to load FFProbe\" at /var/www/html/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Driver/FFProbeDriver.php line 50",
"context": {
  "exception": {
    "class": "FFMpeg\\Exception\\ExecutableNotFoundException",
    "message": "Unable to load FFProbe",
    "code": 0,
    "file": "/var/www/html/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Driver/FFProbeDriver.php:50",
    "previous": {
      "class": "Alchemy\\BinaryDriver\\Exception\\ExecutableNotFoundException",
      "message": "Executable not found, proposed : /usr/local/bin/ffprobe",
      "code": 0,
      "file": "/var/www/html/vendor/php-ffmpeg/php-ffmpeg/src/Alchemy/BinaryDriver/AbstractBinary.php:159"
    }
  }
}
1 Like
  1. Can you share your full dockerfile? Maybe something else there is relevant.
  2. Have you tried removing the ffmpeg.binaries and ffprobe.binaries options and letting php-ffmpeg autodetect the location?
  3. Have you tried logging the PATH right before you load ffmpeg to make sure it’s correct? Something like echo getenv("PATH");
  4. If you run your docker image locally, do you have the same issue?
1 Like

@angelomelonas Strange! I was going to say there is an open issue about this very problem, but noticed a familiar name at the bottom :slightly_smiling_face: So you know about that: Executable not found, proposed : avprobe, ffprobe · Issue #172 · PHP-FFMpeg/PHP-FFMpeg · GitHub

Hopefully @ben-io can assist. I think you did (2) above, but definitely try the others.

1 Like

Hi @ben-io,

Yeah sure. There might be a mistake in my Dockerfile. Please let me know if this is the case.

  1. My Dockerfile is basically a copy of a Dockerfile suggested in your documentation for a Symfony setup (minus the webapp, as my application is currently purely backend):
# syntax = docker/dockerfile:experimental

# Default to PHP 8.1, 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.1
#ARG NODE_VERSION=14
FROM serversideup/php:${PHP_VERSION}-fpm-nginx-v1.5.0 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="symfony"

RUN apt-get update && apt-get install -y \
    git curl zip unzip rsync ca-certificates vim htop cron ffmpeg \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

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

RUN composer install --optimize-autoloader --no-dev --no-scripts \
    && chown -R webuser:webgroup /var/www/html \
    && rm -rf /etc/cont-init.d/* \
    && cp .fly/nginx-default /etc/nginx/sites-available/default \
    && cp .fly/entrypoint.sh /entrypoint \
    && cp .fly/FlySymfonyRuntime.php /var/www/html/src/FlySymfonyRuntime.php \
    && chmod +x /entrypoint

EXPOSE 8080

ENTRYPOINT ["/entrypoint"]
  1. As @greg mentioned, yup, I have indeed tried that
  2. Yes, the output is "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" (it includes /usr/bin)
  3. That is one I have not tried, because I use a local PHP setup and not a Docker environment

It’s worth noting that running file_exists() and is_executable() for both '/usr/bin/ffmpeg' and ‘/usr/bin/ffprobe’ returns false. However, when I use an interactive PHP shell from the same src directory, both commands return true. I’m by no means an expert with Ubuntu/Linux, but does that not indicate it’s related to the PHP user (webuser) not having appropriate permissions?

Do you perhaps have any other suggestions that I could try, @ben-io? :slight_smile:

On the off-chance that the issue is related to the base container and how it runs/configures PHP, you can try the newer docker base images that no longer use the serversideup base images.

That’s found here.

You’d need to adjust it for Symfony again (we documented that here).

1 Like

Hi @fideloper-fly, I’ll give that a try and report back. Thank you!

1 Like

Hi @fideloper-fly,

Thanks for the idea to use the newer Docker image. I started from scratch, generating a project by creating an artisan file and running fly launch. I then configured everything as per the documentation you linked (albeit with a few extra changes to the Dockerfile that I can note for you if you like, as it might be helpful for others in the future) and got the application deployed.

Upon testing, I now get the following, much more clear, exception:

{"message":"Warning: file_exists(): open_basedir restriction in effect. File(/usr/bin/ffmpeg) is not within the allowed path(s): (/var/www/html:/dev/stdout:/tmp)","context":{"exception":{"class":"ErrorException","message":"Warning: file_exists(): open_basedir restriction in effect. File(/usr/bin/ffmpeg) is not within the allowed path(s): (/var/www/html:/dev/stdout:/tmp)","code":0,"file":"/var/www/html/src/Controller/WebhookController.php:57"}},"level":300,"level_name":"WARNING","channel":"php","datetime":"2023-03-30T16:28:13.642328+00:00","extra":{}}

This seemed very much related to a post you commented on, so I checked the /etc/php/8.2/fpm/pool.d/www.conf file and indeed it contains these lines:

; @fly settings:
; Security measures
php_admin_value[open_basedir] = /var/www/html:/dev/stdout:/tmp
php_admin_flag[session.cookie_secure] = true

Since this seems like an intentional security measure, how do you suggest that I continue? :slight_smile:

Hi!

So, you’re definitely free to remove the open_basedir line there completely. The security concern there is if malicious code ended up there, it could start exec()ing commands.

However if you’re environment isn’t one that is open to code injection (doesn’t allow file uploads, forms are generally secured from that kind of stuff), you are failry safe-ish removing that. It’s a very wordpress-y problem.

Another way to go about that: move/copy the ffmpeg (and related) commands to a location that can be reached - perhaps /var/www/html/bin. Then call /var/www/html/bin/ffmpeg from the PHP code (the full path, unless you also add /var/www/html/bin to $PATH, but using the full path is easier).

1 Like

Amazing, it finally works! I made the following changes to my Dockerfile to get this to work (as per your suggestion):

# Create a directory to store binaries
RUN mkdir /var/www/html/bin

RUN apt-get update && apt-get install -y ffmpeg \
    && cp /usr/bin/ffmpeg /var/www/html/bin/ffmpeg \
    && cp /usr/bin/ffprobe /var/www/html/bin/ffprobe \
    ...

I’m not sure if this is the better approach, since it means anything additional I install, I would need to copy to that same directory (/var/www/html/bin/). Is this the case for other users of the fideloper/fly-laravel package as well (meaning, do they also need a “workaround” like this to be able to call installed binaries from their PHP code)?

Lastly, thank you so much! I really appreciate your help and swift response (this goes to @greg and @ben-io too!) :raised_hands:

2 Likes

Great, glad that’s working!

Yeah in the case of installing binaries for PHP to call directly, you’d need to keep doing that. One other idea is making a symlink instead of copying them over, but I’m not sure that’ll work if PHP can’t “see” the real file that’s being symlinked.

You can always remove the open_basedir restriction if it gets too crazy!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.