What we're building
We'll package a PHP application into an image. Everything here uses instructions you
already learned - FROM, WORKDIR, RUN, COPY, CMD - just applied to a real
stack.
The PHP Dockerfile
In your PHP project's root, create a Dockerfile:
FROM php:8.4-cli
WORKDIR /app
RUN apt-get update && apt-get install -y \
unzip libpq-dev \
&& docker-php-ext-install pdo pdo_mysql
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
RUN composer install --no-scripts --no-interaction
COPY . .
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
Let's walk through it with what you already know:
-
FROM php:8.4-cli- start from the official PHP 8.4 image. -
WORKDIR /app- work inside/app. -
RUN apt-get ... docker-php-ext-install pdo pdo_mysql- install the system packages and PHP database extensions the app needs.docker-php-ext-installis a helper that ships with the official PHP image. -
COPY --from=composer:latest ...- grab the Composer tool from the officialcomposerimage. (You'll learn more about copying from another image in the multi-stage lesson - for now, this just gives uscomposer.) -
COPY composer.json composer.lock ./thenRUN composer install- remember the caching lesson: we copy the dependency files and install before copying the rest of the code, so dependencies stay cached when only source files change. -
COPY . .- copy the rest of the application. -
CMD [...]- run Laravel's built-in server, bound to0.0.0.0so it's reachable from outside the container (binding tolocalhostinside a container would only reach the container itself).
A .dockerignore
Add a .dockerignore so local junk and secrets don't get copied in:
.git
vendor
node_modules
.env
storage/logs/*.log
This is the same idea from the
.dockerignore lesson, tuned for a PHP project.
Next, we'll pair this app image with a database using Compose.
The extension mistake that wastes an afternoon
The error that eats the most time when dockerizing PHP is a missing extension. Your
app boots, then dies with something like "could not find driver" - which really means
the pdo_mysql extension isn't installed in the image. PHP images don't include every
extension by default; you add them with docker-php-ext-install, as in the Dockerfile
above. When a PHP app fails only inside Docker, a missing extension is the first thing
to check.
FAQ
How do I install PHP extensions in a Docker image?
Use the docker-php-ext-install helper that ships with the official PHP images, for
example docker-php-ext-install pdo pdo_mysql. Some extensions also need system
packages installed first via apt-get.
Why do I get "could not find driver" in a Dockerized PHP app?
The database PHP extension is missing from the image. Install it in your Dockerfile -
for MySQL that's docker-php-ext-install pdo_mysql - then rebuild.
Why install Composer dependencies before copying all the code?
For build caching. Copy composer.json/composer.lock and run composer install
first, then copy the rest. That way the slow install layer stays cached when you change
only application code.