This repository is a minimal example to get a PHP + Apache + MariaDB (MySQL-compatible) development stack running with Docker Compose. It's intended for local development, demos, or as a starting point for small projects.
This README documents the project structure, how to run the stack, database initialization behavior, and tips for debugging (Composer, Xdebug, phpMyAdmin).
Dockerfile— builds the PHP/Apache image. It installs common PHP extensions, Composer and Xdebug (via PECL) during the image build.docker-compose.yml— defines three main services:apache: the PHP+Apache webserver container, mounts the project into/var/www/html.db: MariaDB server, uses a named volumedb-datafor persistence and mounts./mysqlto/docker-entrypoint-initdb.dfor initialization scripts.phpmyadmin: optional phpMyAdmin service to manage the database from the browser.
mysql/— place one or more.sqlfiles here (for exampledump.sql) to be automatically executed on the first initialization of the DB volume.config/— lightweight PHP config:env.phploads.envinto$_ENV,db.phpcreates a PDO connection.php.ini— runtime PHP configuration copied into the container.logs/— recommended to containphp_errors.logso PHP can write runtime errors to a persisted file..env.example— example environment variables; copy to.envand edit for local values.
This project can act as a drop-in replacement for MAMP when developing PHP applications locally. Instead of installing Apache/PHP/MySQL on your machine, you run everything in containers. Benefits and caveats:
-
Benefits:
- Reproducible environment across machines and team members.
- No global PHP/Apache/MySQL installation required on the host.
- Easy to change PHP version or add extensions by editing the
Dockerfile. - Clean teardown: remove volumes to return to a fresh state.
-
Caveats:
- Containers run in an isolated environment; when you need to connect to services on the host (e.g., MAMP databases), use
host.docker.internalon macOS. - Port conflicts: ensure
APACHE_PORTandPHPMYADMIN_PORTin.envdo not collide with locally running services. - File permissions may require attention — prefer running Composer as your host user via the
--userflag shown earlier.
- Containers run in an isolated environment; when you need to connect to services on the host (e.g., MAMP databases), use
Quick note: if you prefer to keep using MAMP for the database, set DB_HOST=host.docker.internal and the proper port in .env so the containerized PHP connects to your MAMP database.
- Copy environment variables:
cp .env.example .env
# edit .env to set any values you need (ports, database name, passwords)- Build and start the stack:
docker compose up -d --build- Visit your app at
http://localhost:<APACHE_PORT>(default 8080) and phpMyAdmin athttp://localhost:<PHPMYADMIN_PORT>(default 8081).
Notes:
- If
mysql/contains a SQL dump and the DB volume is empty, MariaDB will automatically run the scripts and import the dump on first startup. - If the DB volume already contains data, initialization scripts are skipped. See "Database import" below for re-import options.
Important variables are included in .env.example. Typical development values:
APACHE_PORT=8080
PHPMYADMIN_PORT=8081
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=my_db
MYSQL_USER=root
MYSQL_PASSWORD=root
DB_HOST=dbKeep .env out of version control (do not commit sensitive values). If you need to ship sample values, update .env.example instead.
Composer is available inside the PHP container. Run:
docker compose run --rm --user $(id -u):$(id -g) apache composer installor
docker compose exec apache composer installUse the first command to ensure installed files are owned by your host user.
Xdebug is installed at image build time. To avoid double-loading the extension, do not include zend_extension=xdebug.so in a mounted ini if the image already enables Xdebug. Recommended approach:
- Mount a configuration-only file with options (no
zend_extension) viadocker-compose. - Alternatively, remove
docker-php-ext-enable xdebugfrom theDockerfileand use a mounted ini that includeszend_extensionto enable Xdebug only when desired.
Example .docker/xdebug.ini (config-only):
xdebug.mode=develop,debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003Mount as:
services:
apache:
volumes:
- ./.docker/xdebug.ini:/usr/local/etc/php/conf.d/zz-xdebug.ini:roVerify inside the container:
docker compose exec apache php -v
docker compose exec apache php --ri xdebug || true- Put SQL files (e.g.
dump.sql) into themysql/folder. On first initialization (emptydb-datavolume), MariaDB executes all scripts in/docker-entrypoint-initdb.din alphabetical order. - Best practice: prepend the dump with a header to create and select the database explicitly:
CREATE DATABASE IF NOT EXISTS `my_db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `my_db`;- Non-destructive re-import (recommended for development): run a manual import using
docker compose exec:
docker compose exec db mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "DROP DATABASE IF EXISTS my_db; CREATE DATABASE my_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
docker compose exec db bash -lc "mysql -uroot -p\"${MYSQL_ROOT_PASSWORD}\" my_db < /docker-entrypoint-initdb.d/dump.sql"- Destructive fresh import: remove the volume then bring the stack up so entrypoint runs init scripts:
docker compose down -v
docker compose up -d --buildphpMyAdmin is included and configured to connect to the db service using credentials from .env. Access it at http://localhost:<PHPMYADMIN_PORT>. Use root credentials from .env to create databases or import dumps via the web UI if desired.
- Dump not imported: likely the
db-datavolume is not empty. Either remove the volume (destructive) or import manually. - Connection refused to external DB (MAMP): ensure host/port are reachable from the container (
host.docker.internalon macOS). You may need to addextra_hoststodocker-compose.ymlfor some Docker setups. - "Cannot load Xdebug - it was already loaded": avoid loading the extension twice. Mount only configuration options or change the
Dockerfileto enable Xdebug only when the ini is present.
Dockerfile— builds the PHP image; installs Composer and Xdebug.docker-compose.yml— service definitions, mounts, and thedb-datavolume.mysql/— initial SQL scripts/dumps for DB bootstrap.config/env.php— loads.envinto PHP runtime.config/db.php— PDO connection wrapper used by the app.php.ini— configuration for PHP runtime.
- Add a
db-inithelper service andscripts/import-dump.shto allow safe, repeatable imports without deleting volumes. - Provide an example
.docker/xdebug.iniin the repo and add it to.gitignore. - Add a small
README.local.mdwith step-by-step developer onboarding.
If you want, I can commit this change now and push it to main.