What Small PHP Teams Don’t Need

I’ve been deploying PHP applications in small team settings for a while, and one question keeps coming up: should we be using Docker, Kubernetes, or something similar? I wanted to write down how I think about it, because the answer depends on the kind of projects you’re working on.

My deployment tool is rsync. Files go to each server over SSH, a symlink flips, and the new code is live. I’ve tried Docker, Kubernetes, and Docker Swarm over the years, and each time I came back to rsync because it fit my situation better.

How the deploys work

Rsync copies changed files to each server over SSH. Each deploy goes into a timestamped release directory, and once dependencies are installed and migrations have run, a symlink flips to make it live. The previous release is still sitting there, so if something breaks I can flip the symlink back and be running the old code again within seconds.

for server in web1 web2 web3 web4; do
  rsync -avz --delete ./src/ deploy@$server:/var/www/myapp/releases/20260210_1430/
done
# ... composer install, migrations, symlink flip on each server

A typical project has four or five servers behind a load balancer, and the deploy script loops through all of them in under a minute. Because a rollback only changes symlinks, it’s effectively instant. This setup hasn’t held me back so far.

Docker

Docker makes sense when you need consistent environments across a larger team, or when one server has to run several apps with different runtimes, or when you have a handful of services that need to find and talk to each other through Compose’s networking. None of those describe the projects I work on.

When my services need to talk to each other, they go through internal load balancers with keepalived for hot failover. Each service knows one hostname and path for the load balancer, and that’s all it needs. Secrets live in .env files deployed with the code. The servers stay consistent because I provision them the same way, and adding capacity means adding servers to the load balancer backend. Docker’s service networking and Kubernetes’s secrets management solve the same problems in a more dynamic way, but a load balancer and .env files already cover what I need, and I’d rather not take on Dockerfiles, compose files, an image registry, and a separate debugging workflow on top of something that works.

Docker Swarm adds orchestration on top of that: rolling deploys, service scaling, load balancing across machines. My load balancer already distributes traffic, and the deploy script handles the rest.

Kubernetes

Kubernetes is built for a different scale: a complex topology with dozens of services that have to find each other, separate teams deploying on their own schedules, and traffic spiky enough to need auto-scaling. Its service meshes, secrets management, and declarative infrastructure are well suited to that.

For my projects, internal load balancers already handle service communication, and adding capacity means adding servers to the backend. I looked into Kubernetes once out of professional curiosity, and I found myself learning pods, services, deployments, ingresses, persistent volumes, config maps, secrets, Helm charts, and Kustomize overlays before I could deploy anything. It’s all well designed for what Kubernetes does. It’s also a lot of machinery when simpler infrastructure already handles the routing and failover.

How I think about the choice

There’s an implicit progression in the industry where rsync is “beginner,” Docker is “intermediate,” and Kubernetes is “advanced,” as though they were levels on a skill tree. I don’t see it that way. They solve different problems at different scales, and the question is which one fits the project you’re working on.

For now that keeps me spending my time on the product rather than the infrastructure, and I’ll add complexity when a project needs it.

I wrote a book about this. Own Your Stack: PHP for Small Teams covers deployment, server setup, CI/CD, monitoring, and backups for small teams that don’t need the complexity.