Optimizing PHP Apps in Dokku


I run all my webapps in a single Digital Ocean (link with referral) droplet (their fancy word for VPS).

When I started I had some issues: with builds requiring more memory and how to monitor a Dokku server to diagnose instabilities.

At some point my server randomly froze (and as such all my apps became suddenly unavailable). This is what I did to fix it.

The Problem

The problem was that my apps were spawning hundreds of PHP processes per app. I have a few of them, so the total number of processes skyrocketed very easily to thousands.

Many more processes than my humble VPS server could handle.

The root cause? you might ask. The Heroku Buildpack for PHP I use automatically calculates the number of php-fpm workers based on “available resources”.

By default Dokku makes all resources available to any of your apps. So in my case all of the apps thought the whole RAM was available for them so the algorithm added a bunch of processes for each app.

First attempt

After reading on Resource Management for Dokku the straightforward solution was obvious.

I ran dokku resource:limit --memory 250 <the-app> followed by a restart with dokku ps:restart <the-app> for each of my apps.

I could confirm that now most of my apps went down from hundreds of php-fpm workers to just one. Sweet. Plenty of resources available now!

❓ You might be thinking why the 250? And is 1 worker enough? If that’s the case: keep reading.

Understanding the numbers

Doing the above was enough for a while. In general my apps are very modest. The one with the most traffic is probably this blog, and it’s less than a hundred real visitors (not counting bots) per week.

But being honest I didn’t pay too much attention to how everything works. And, as I said, it was fine.

Fast forward a few months (or a year) and suddenly I see something odd on a new project I was working on:

My new app could potentially require attending two requests at the same time.

It was not working, that was because I had only one php-fpm worker available. I realized I didn’t check how the actual number was calculated so I did.

This calculation is done by dividing the “memory limit” reported by PHP (each app has their own configuration) and the RAM available. If you want to check the actual code it is here.

My app had the memory limit set to 128MB. And I had added a limit of 250MB to the app. So that’s where the 1 worker number is coming.

I only needed two workers. And changing the limit from 250 to 256 (128*2) with dokku resource:limit --memory 256 <the-app> did the trick.

After that, and a restart of the app (dokku ps:restart <the-app>). I could successfully process two simultaneous requests at the same time.

Conclusion

Sometimes the shortest path is enough (and the most pragmatic thing you can do). But take a note of that, and be ready to come back and dig deeper to fill the missing gaps.

Learning is always exciting.