Containerizing Laravel with Docker and orchestrating with Kubernetes gives you consistent environments, easy scaling, and reliable deployments. This is the exact setup I've used in production for European-standard payroll and SaaS applications.
Laravel Dockerfile
A multi-stage Dockerfile keeps the final image lean:
FROM php:8.2-fpm-alpine AS base
RUN apk add --no-cache git curl libpng-dev oniguruma-dev libxml2-dev zip unzip
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
COPY . .
RUN composer install --no-dev --optimize-autoloader
RUN chown -R www-data:www-data /var/www
EXPOSE 9000
CMD ["php-fpm"]
Docker Compose for Local Development
version: '3.8'
services:
app:
build: .
container_name: laravel_app
volumes:
- .:/var/www
networks:
- laravel
nginx:
image: nginx:alpine
container_name: laravel_nginx
ports:
- "8000:80"
volumes:
- .:/var/www
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- laravel
mysql:
image: mysql:8.0
container_name: laravel_mysql
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: password
ports:
- "3306:3306"
networks:
- laravel
networks:
laravel:
driver: bridge
Kubernetes Deployment
Deployment Config
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-app
spec:
replicas: 3
selector:
matchLabels:
app: laravel-app
template:
metadata:
labels:
app: laravel-app
spec:
containers:
- name: laravel-app
image: your-registry/laravel-app:latest
ports:
- containerPort: 9000
env:
- name: DB_HOST
value: "mysql-service"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
Service Config
apiVersion: v1
kind: Service
metadata:
name: laravel-service
spec:
selector:
app: laravel-app
ports:
- port: 80
targetPort: 9000
type: LoadBalancer
CI/CD with GitHub Actions
name: Deploy to Kubernetes
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build Docker image
run: docker build -t laravel-app:${{ github.sha }} .
- name: Push to registry
run: |
docker tag laravel-app:${{ github.sha }} your-registry/laravel-app:${{ github.sha }}
docker push your-registry/laravel-app:${{ github.sha }}
- name: Deploy to Kubernetes
run: kubectl set image deployment/laravel-app laravel-app=your-registry/laravel-app:${{ github.sha }}
Horizontal Pod Autoscaling
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: laravel-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: laravel-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
Production tip: Always use specific image tags (like the git SHA) instead of
latest. It makes rollbacks trivial and deployments fully reproducible.
Conclusion
Start with Docker Compose for local development to ensure environment consistency across your team. When you're ready for production, Kubernetes gives you autoscaling, zero-downtime deployments, and easy rollbacks. The GitHub Actions pipeline automates the whole process on every push to main.