Containers pin every version in a file you commit, so every developer and the CI server run identical builds. Inside WSL 6 , add Docker 514 's apt repository for Ubuntu 26.04 225 as docs.docker.com shows, then sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin (Docker 29.8.1 and Compose v5.5.1 here). Ubuntu's older docker.io package conflicts with it, and unlike Docker Desktop 514 this needs no Windows app running.
services:
web:
build:
dockerfile_inline: |
FROM php:8.5-apache
RUN docker-php-ext-install pdo_mysql
ports: ["8091:80"]
volumes: ["./src:/var/www/html"]
depends_on: {db: {condition: service_healthy}}
db:
image: mysql:9.7
ports: ["127.0.0.1:3391:3306"]
environment: {MYSQL_ROOT_PASSWORD: rootpw, MYSQL_DATABASE: app, MYSQL_USER: app,
MYSQL_PASSWORD: secret}
volumes: ["dbdata:/var/lib/mysql"]
healthcheck: {test: "mysqladmin ping -h 127.0.0.1 -prootpw", interval: 5s, retries: 20}
volumes: {dbdata: {}}The inline Dockerfile adds the pdo_mysql driver the official image lacks. Ports 8091 and 3391 avoid the native Apache 129 and MySQL 524 , 127.0.0.1 keeps MySQL off the network, the web container waits for a healthy database, and the dbdata volume keeps the data. Inside the network, the database host is db:
mkdir src && cat > src/index.php <<'PHP'
<?php
$pdo = new PDO('mysql:host=db;dbname=app', 'app', 'secret');
echo 'PHP ', PHP_VERSION, ' on ', $_SERVER['SERVER_SOFTWARE'], "\n";
echo 'MySQL ', $pdo->query('SELECT VERSION()')->fetchColumn(), "\n";
PHP
docker compose up -d --build --quiet-build
docker compose ps --format 'table {{.Service}}\t{{.Status}}\t{{.Ports}}'
curl -s http://localhost:8091/... Container lamp-web-1 Started SERVICE STATUS PORTS db Up 21 seconds (healthy) 33060/tcp, 127.0.0.1:3391->3306/tcp web Up Less than a second 0.0.0.0:8091->80/tcp, [::]:8091->80/tcp PHP 8.5.10 on Apache/2.4.68 (Debian) MySQL 9.7.2
The images are newer than Ubuntu's packages, a difference worth writing down. docker compose down stops the stack, down -v also deletes the database, and real passwords belong in an .env file kept out of git.