Use nginx with php-fpm. It takes 5 minutes to configure.
Edit: I'm gonna give this thing a try too but I've never had any bottlenecks with either nginx or Apache. They both take a few minutes to get going at most.
Few seconds with docker. There are fully loaded compose ones that include everything so you don’t need to add anything. It’s lovely. For prod it’s better to only leave the needed extensions etc of course but for dev it’s so easy to get going. And to be honest, for small personal, company internal and less than $1000/mo rev saas, we just use the fully loaded in prod as well.
I was a long-time PHP dev and then moved on to C#. I was close to writing this nginx/fpm setup lookss very complex compared to "dotnet run". But then I remembered, in ASP.NET you have a Program with 50 lines of code that must have an exact order, otherwise thinks work except they don't.
So I would say, knowing these two languages, PHP really is that easy to setup.
You won’t find this answer very satisfying, so feel free to ask questions.
But it depends on what you’re trying to accomplish. If you’re sticking with defaults, it’s very easy - it’s less than ten minutes of work or seconds if you’re comfortable working with Docker.
On the other hand, let’s say that you’re running into problems scaling to handle more requests. You’ll end up having to tweak the max_workers setting. That takes a bit of experience or it can be a lot of ‘fun’ (in the Dwarf Fortress sense).
So yes and no. But a lot of people have been through this, have all the trauma you would receive and tend to be quite helpful.
I just have the right config for both Apache2 and nginx from hundreds of sites. The config itself is only 50 lines. You can find a good one and stick with it.
You can have a a ~20 line docker-compose.yml file that only requires you to `docker compose up` and you're up and running. It's gotten insanely easy to run php-fpm + nginx + [whateverdb] in a set of docker services that require virtually no configuration.
The original comment was about newbies. Everyone is a newbie at one point.
Just last year I tried a blog engine written in PHP. 24 years programming experience, but I haven't touched PHP for 10 years, and I haven't touched configuring it for 15+ years.
What blog engine? Usually with PHP applications you clone the repo, copy .env boilerplate, enter database credentials, run Composer to fetch dependencies and do some setup tasks, then boot the application.
I think it would be roughly that easy for anyone with basic nginx experience. Nginx includes a php fast-cgi snippet that you just `include` from your server block.
It takes 5 minutes to configure with the defaults. But once you need to optimize PHP-FPM to handle more requests, you now find yourself fiddling with pm.max_children etc settings and need to know what you are doing. I love working with PHP and Go both btw. But PHP configurations can be a pain if you do anything other than defaults.
There are a lot of strange assumptions on that first page:
- "for example, if you are using PHP to access a database, unless that database has built-in access control" - well, of course I will use a database with access control like MySQL
- "It's entirely possible that a web spider could stumble across a database administrator's web page, and drop all of your databases". Of course I won't run PHPMySQLAdmin or Adminer on a production server, nor will I expose it to the public.
- also none of the above has anything to do with the PHP execution model
The second page might raise some valid issues but for instance the bullet point "mod_php is loaded into every httpd process all the time. Even when httpd is serving static/non php content, that memory is in use" - doesn't matter to me when I am running everything through index.php routing anyway.
"mod_php is not thread safe and forces you to stick with the prefork mpm (multi process, no threads), which is the slowest possible configuration" might be true but also a premature optimization.
Maybe mod_php is really bad but the arguments presented here are not very convincing to me.
It's simple and there is less overhead. Since PHP runs directly within the Apache process, there is no need for inter-process communication (no TCP, no sockets), reducing the overhead. This can lead to lower latency for individual requests.
mod_php does give you better response times for individual requests, but at the expense of being able to handle a higher load of traffic; you'll run out of memory and/or experience timeouts on mod_php way before you do with php-fpm.
With mod_php, every Apache process has the PHP engine embedded in it, even if PHP isn't needed, e.g., to serve a request for a .css file. When Apache gets a bunch of requests for flat files, it forks all those processes and fills up RAM with copies of the PHP engine that aren't used. That's not only wasteful, but it dramatically increases the chances that you'll run out of memory. You can limit the number of Apache children of course, but you'll see timeouts sooner when you get a traffic spike.
By having Apache proxy over to php-fpm for PHP requests, you can configure Apache to use mpm_event for serving static files, which allows for much leaner Apache workers (memory-wise) since they aren't carrying PHP around on their backs.
While you're at it, you can use haproxy on the same machine for TLS termination, then you can disable mod_ssl thus making Apache workers even lighter.
> With mod_php, every Apache process has the PHP engine embedded in it, even if PHP isn't needed, e.g., to serve a request for a .css file. When Apache gets a bunch of requests for flat files, it forks all those processes and fills up RAM with copies of the PHP engine that aren't used. That's not only wasteful, but it dramatically increases the chances that you'll run out of memory. You can limit the number of Apache children of course, but you'll see timeouts sooner when you get a traffic spike.
Yes, that is true. But most high-traffic websites will cache static files such as CSS files and images, using a reverse proxy (e.g. Varnish, a CDN, or usually both). So I don't think this is a real problem, most of the time (99.9%?), a request for a static file will not hit Apache.
I'm not saying mod_php is better for all scenarios, of course, but I think it can be ok.
I tend to agree with you - using in "default" setup with mod_php and mpm_prefork is known to be far from optimal (still fine for blog about you and your cat).
With reverse proxy in front of such setup is - much better in terms of performance. For shared hosting - yet again, may be not optimal if one needs to support multiple system users.
Just as a reminder, you can use Caddy + php-fpm as well (with vanilla Caddy, no plugins).
What this does is give you a way to run your webserver + PHP as a single process, or single Docker container (instead of the traditional 2-container approach), and it also unlocks the ability to run PHP in a worker mode, where you have long-running PHP processes that have your framework loaded in memory ready to serve requests, instead of booting the framework on every request (so, much lower request latency).
Caddy uses more RAM and has no inherent benefit when running a project in production professionally. Caddy is easier for self hosted cause it automatically handles HTTPS where Nginx does not.
I'm a pretty hardcore nginx fan with a decent chunk of rather low level professional experience with it in complex setups. The reason I'd still pick nginx over Caddy for prod is that I know really well how a broader tool set will work together, e.g. keepalived and whatnot.
Caddy kind of feels like a cute toy in comparison with nginx, but it's not, by now it's been seriously battle tested and has proven itself to be quite resource efficient and resilient.
You might use it to implement redundancy in the load balancer layer of your system, perhaps your firewall round-robins incoming connections between two IP:s where you have nginx proxying to share load between two mirrored clusters, and those IP:s are virtual and handled by keepalived that will shuffle in a backup virtual server if the one currently serving becomes unhealthy or needs to be switched out due to a config rollout or something.
It's a really neat way to be able to just throw more virtual servers at the problems in availability, redundancy, load balancing and so on. I think it does some ARP messaging to achieve this.
Edit: I've applied it with ProxySQL in one case, it was an application on a trajectory from a simple rig with one virtual web server, one virtual database server to a highly available and resilient system. When I left we had a master-master-cluster with ProxySQL in front, with three ProxySQL-machines keepalived, so in case one went out for some reason there where two more in the stack to fill in. When you aren't sure what kind of peak load you're going to handle it's nice to know that when the alarm comes you have one fresh machine buying you some time while you figure out what you need to do to the third before it is shuffled into service.
I think that OP really meant that in his/her mental model of things, answers already known and battle tested for numerous cases [by using Nginx] - making HA with keepalived? Check. Making logging to be buffered to save iops? Check. Implementing ratelimits and custom logic? Check.
Keepalived is not really bound to Nginx by any means and should perfectly fine work with Caddy too.
Caddy “uses” more ram because Go is a garbage collected language. It can free tons of it, but that costs cpu cycles. It generally won’t spend too much time freeing memory until the system is under pressure.
Because it’s Go, you can tune garbage collection to be super aggressive, at the expense of speed.
Citing that particular blog post isn't making the point you think it makes. To quote:
> The most striking piece of new knowledge for me was learning about failure modes. Nginx will fail by refusing or dropping connections, Caddy will fail by slowing everything down.
Do you want your clients failing to load your website at all? Is this the best approach to serving users?
> Do you want your clients failing to load your website at all? Is this the best approach to serving users?
There are good reasons for picking either. Large services under sudden load sometimes implement queueing, which is just failing but stylish.
For my blog posts, I'd rather throw an error than have people wait for thirty seconds. The contents aren't that important and the end result will probably look bad because of missing CSS anyway.
For API services, I'd want things to slow down rather than fail, unless failure is explicitly documented in the API and can be handled somewhat gracefully.