Production & Deployment
Laradock's default docker-compose.yml is tuned for local development: your code is bind-mounted, Xdebug is available, opcache re-checks files on every request. Production wants the opposite, code baked into an immutable image, Xdebug gone, opcache frozen.
The production/ folder does exactly that, for any PHP app: Laravel, Symfony, WordPress, Moodle, Drupal, or plain PHP.
The idea in one line
A Docker image is the universal deploy adapter. You build one hardened image of your app, and every target below already knows how to run it, a server, Kamal, Kubernetes, Fly.io. There is no per-provider magic to learn, and Laradock does not try to be a deployment platform.
1. Build the image
./laradock ship
That resolves your app (from APP_CODE_PATH_HOST), writes a safe .dockerignore if you don't have one, and builds a production image. Add --push to push it to a registry, or pass a tag: ./laradock ship registry.example.com/myapp:1.0 --push.
Prefer plain Docker? It's the same thing:
cp laradock/production/dockerignore.sample .dockerignore
docker build -f laradock/production/Dockerfile -t myapp:latest .
What you get versus your dev container: code baked in (no bind mount), composer install --no-dev (skipped automatically if your project has no composer.json, so WordPress and Moodle just work), opcache.validate_timestamps=0, no Xdebug, and a production php.ini. Same PHP version and base extensions as dev.
The image is self-contained (nginx + php-fpm inside) and serves HTTP on $PORT (default 8080), so it runs as a single container on any platform, from Kubernetes to Cloud Run. On Apple Silicon ship builds linux/amd64 by default. Check it locally with docker run -p 8080:8080 myapp:latest.
If you toggled on things like redis, pdo_pgsql or gd, add them at the extensions line in production/Dockerfile, or point --build-arg BASE_IMAGE= at your locally-built laradock php-fpm image for exact parity.
2. Deploy it, the image runs anywhere
Because it's a standard OCI image, every target below just runs it, there is nothing Laradock-specific to install on the server. The only real decision is who runs the infrastructure: a managed platform, or you.
Each platform has its own step-by-step guide.
Managed clouds (push the image, they run everything):
Your own infrastructure (you run it):
| Platform | Guide |
|---|---|
| Kubernetes (EKS / GKE / AKS / DOKS, or your own) | Deploy to Kubernetes |
| Your own servers, no Kubernetes (Kamal) | Deploy with Kamal |
| A single server (Docker Compose) | Deploy to a Server |
The flows in brief (each guide above has the full steps):
Managed platforms: Cloud Run, ECS/Fargate, App Runner, Container Apps
Same shape everywhere: push the image to that cloud's registry, then point its container service at the image and set your env vars. The platform hands you the load balancer, TLS, autoscaling and rollouts, no compose or manifests to maintain.
# example: AWS ECR (same idea for GCP Artifact Registry / Azure ACR)
./laradock ship 1234567890.dkr.ecr.us-east-1.amazonaws.com/myapp:latest --push
# then create an ECS/Fargate service (or App Runner / Cloud Run) from that image
Ready-made configs live in production/providers/, copy the one you need and fill in image + secrets:
| Provider | Config |
|---|---|
| AWS ECS / Fargate | aws-ecs-task-definition.json |
| AWS App Runner | aws-app-runner.json |
| Google Cloud Run | google-cloud-run.yaml |
| Azure Container Apps | azure-container-app.yaml |
| Fly.io | fly.toml |
| Render | render.yaml |
| Railway | railway.json |
| DigitalOcean App Platform | digitalocean-app.yaml |
| Heroku | heroku.yml |
| Kamal (your own servers) | kamal-deploy.yml |
Kubernetes: managed (EKS / GKE / AKS / DOKS) or your own
Push the image, then apply the reference manifests. Managed clusters give you the control plane; the manifests are identical either way.
kubectl create secret generic app-env --from-env-file=laradock/production/.env
kubectl apply -f laradock/production/kubernetes.yaml
kubernetes.yaml is a deliberately plain starting point: a deployment (the self-contained web container, with resource limits and probes), a service, a TLS-ready ingress (cert-manager), an uploads PVC, plus optional worker, scheduler CronJob, and a migrate Job.
Your own servers
One box (the 80% case). One self-contained container serves HTTP; point the database and Redis at managed services:
cp laradock/production/.env.example laradock/production/.env # fill real values
docker compose -f laradock/production/compose.yml up -d
Several boxes, no Kubernetes → Kamal ships your image with zero-downtime rollovers (kamal setup, then kamal deploy per release), or Docker Swarm if you prefer. Don't hand-roll deploy scripts.
3. What your framework needs on top
Every PHP app is php-fpm + web server + database. Frameworks only add optional pieces. Toggle them with compose --profile flags, or by deleting the matching Kubernetes block:
| App | Worker --profile worker | Scheduler/cron --profile scheduler | Persistent volume |
|---|---|---|---|
| Laravel | queue:work | schedule:run | storage/ |
| Symfony | messenger:consume | if you use cron | var/ |
| WordPress | — | wp-cron or system cron | wp-content/uploads |
| Moodle | — | required (admin/cli/cron.php) | moodledata |
| Plain PHP | — | — | as needed |
docker compose -f laradock/production/compose.yml --profile worker --profile scheduler up -d
Anything users upload must live on a mounted volume or object storage (S3), never inside the image. A deploy replaces the image and wipes anything written to its filesystem.
Migrations run once per deploy, before traffic shifts. Compose: docker compose -f laradock/production/compose.yml run --rm app php artisan migrate --force. Kubernetes: the migrate Job.
Security
Docker publishes ports on the host unless told otherwise. The production compose file deliberately does not expose the database. If you add a database container, do not write:
ports:
- "3306:3306"
See Docker and iptables for why.
- Database is managed, not a pod. Run MySQL/Postgres/Redis as managed services (RDS, CloudSQL, ElastiCache). Laradock's database services are for local dev only.
- Secrets come from real env vars or a secret manager at runtime. Never bake
.envinto the image, that's what the.dockerignoreis for.
Pushing to Google Container Registry
gcloud auth configure-docker
gcloud auth login
./laradock ship gcr.io/your-project/myapp:latest --push
Verify it works
A smoke test builds the image against a throwaway app and asserts nginx serves a request:
./laradock/production/smoke-test.sh
These files are a reference, deliberately plain. Copy them into your project and adjust, they're a starting line, not a framework to learn.
Deploy to your platform
Step-by-step guides for every target:
Deploy to Kubernetes
Deploy any PHP app (Laravel, Symfony, WordPress) to Kubernetes with Laradock. Build one image with ./laradock ship, then kubectl apply ready-made manifests. Works on EKS, GKE, AKS, DOKS, or your own cluster.
Deploy to AWS ECS / Fargate
Deploy any PHP app (Laravel, Symfony, WordPress) to AWS ECS Fargate with Laradock. Build one image with ./laradock ship, push to ECR, and run it with a ready-made task definition.
Deploy to AWS App Runner
Deploy any PHP app (Laravel, Symfony, WordPress) to AWS App Runner with Laradock. Build one image with ./laradock ship, push to ECR, and go live with a ready-made service config.
Deploy to Google Cloud Run
Deploy any PHP app (Laravel, Symfony, WordPress) to Google Cloud Run with Laradock. Build one image with ./laradock ship, push to Artifact Registry, and go live with a ready-made service config.
Deploy to Azure Container Apps
Deploy any PHP app (Laravel, Symfony, WordPress) to Azure Container Apps with Laradock. Build one image with ./laradock ship, push to ACR, and go live with a ready-made YAML spec.
Deploy to Fly.io
Deploy any PHP app (Laravel, Symfony, WordPress) to Fly.io with Laradock. Build one image with ./laradock ship and go live worldwide with a ready-made fly.toml.
Deploy to Render
Deploy any PHP app (Laravel, Symfony, WordPress) to Render with Laradock. Build one image with ./laradock ship and go live with a ready-made render.yaml Blueprint.
Deploy to Railway
Deploy any PHP app (Laravel, Symfony, WordPress) to Railway with Laradock. Build one image with ./laradock ship and go live with a ready-made railway.json.
Deploy to DigitalOcean App Platform
Deploy any PHP app (Laravel, Symfony, WordPress) to DigitalOcean App Platform with Laradock. Build one image with ./laradock ship and go live with a ready-made app spec.
Deploy to Heroku
Deploy any PHP app (Laravel, Symfony, WordPress) to Heroku with Laradock using the container stack. Build one image with ./laradock ship and go live with a ready-made heroku.yml.
Deploy with Kamal (your own servers)
Deploy any PHP app (Laravel, Symfony, WordPress) to your own servers with Kamal and Laradock. Build one image with ./laradock ship and get zero-downtime deploys with a ready-made config.
Deploy to a Single Server
Deploy any PHP app (Laravel, Symfony, WordPress) to a single server (VPS, EC2, droplet) with Laradock and Docker Compose. Build one image with ./laradock ship and run it with one command.