Skip to content

Local environment with Docker4PHP

Docker4PHP is an open-source project (GitHub page) that provides pre-configured docker-compose.yml file with images to spin up local environment on Linux, Mac OS X and Windows.



Database data persistence

By default Docker will create a persistent volume for your DB data and unless you explicitly remove volumes the files will not be deleted. However, if you run docker-compose down (it's ok to use stop though) these volumes will not be reattached when you run docker-compose up. If you want to have your DB data all-time persistent and attached, we recommend using a bind mount. To use a bind mount uncomment to corresponding line under db server's volumes: in your docker-compose.yml and update the host path to your data directory.

  1. Download docker4php.tar.gz from the latest stable release and unpack to your PHP project root
  2. Make sure NGINX_SERVER_ROOT (or APACHE_SERVER_ROOT) is set to your project public directory with index.php (by default /var/www/html/public)
  3. Ensure database credentials match in your database config and .env files
  4. Configure domains
  5. Optional: import existing database
  6. Optional: uncomment lines in the compose file to run redis, elasticsearch, kibana, etc
  7. Optional: macOS users please read this
  8. Optional: Windows users please read this
  9. Run containers: make up or docker-compose up -d
  10. Your php application should be up and running at http://php.docker.localhost:8000
  11. You can see status of your containers and their logs via portainer: http://portainer.php.docker.localhost:8000

You can stop containers by executing make stop or docker-compose stop.

Optional files

If you don't need to run multiple projects and don't use docker-sync to improve volumes performance on macOS feel free to delete traefik.yml and docker-sync.yml that come with the docker4php.tar.gz

Get updates

We release updates to images from time to time, you can find detailed changelog and update instructions on GitHub under releases page


Docker4PHP uses traefik container for routing. By default, we use port 8000 to avoid potential conflicts but if port 80 is free on your host machine just replace traefik's ports definition in the compose file.

By default BASE_URL set to php.docker.localhost, you can change it in .env file.

Add php.docker.localhost to your /etc/hosts file (some browsers like Chrome may work without it). Do the same for other default domains you might need from listed below:

Service Domain
nginx/apache http://php.docker.localhost:8000
pma http://pma.php.docker.localhost:8000
adminer http://adminer.php.docker.localhost:8000
mailhog http://mailhog.php.docker.localhost:8000
solr http://solr.php.docker.localhost:8000
kibana http://kibana.php.docker.localhost:8000
node http://front.php.docker.localhost:8000
varnish http://varnish.php.docker.localhost:8000
portainer http://portainer.php.docker.localhost:8000
webgrind http://webgrind.php.docker.localhost:8000

Database import and export


See MariaDB stack documentation


See PostgreSQL stack documentation

Make commands

We provide Makefile that contains commands to simplify the work with your local environment. You can run make [COMMAND] to execute the following commands:

Usage: make COMMAND

    up              Start up all container from the current docker-compose.yml 
    stop            Stop all containers for the current docker-compose.yml (docker-compose stop) 
    down            Same as stop
    prune           Stop and remove containers, networks, images, and volumes (docker-compose down)
    ps              List container for the current project (docker ps with filter by name)
    shell           Access PHP container via shell as a default user  (docker exec -ti $CID sh)
    logs [service]  Show containers logs, use [service] to show logs of specific service

Docker for mac

There two major problems macOS users face with when using Docker for mac:

macOS permissions issues

To avoid any permissions issues caused by different user id (uid), group id (gid) between your host and a container use -dev-macos version of php image (uncomment the environment variables in .env files) where the default user wodby has 501:20 uid/gid that matches default macOS user.

Bind mounts performance

Out of the box Docker for mac bind mounts (volumes from host) have poor performance on sync. There are 2 ways how it can be improved.

User-guided caching

Since Docker for Mac 17.06 there's a new native :cached option available for bind mounts. You can find more information about this in docker blog.

Replace codebase volume definition of php and nginx/apache services with the option below marked as "User-guided caching".


Performance tests 2017 Docker-sync vs Native.

The core idea of this project is to use an external volume that will sync your files with a file synchronizer tool.

gem install docker-sync
  1. Download docker-sync.yml file (inside of docker4x.tar.gz archive) from the latest stable release
  2. Uncomment docker-sync volume definition in your compose file
  3. Replace volumes definition of php and nginx/apache services with the option below marked as "Docker-sync".
  4. Start docker-sync: docker-sync start
  5. In a new shell run after you started docker-sync docker-compose up -d

Now when you change your code on the host machine docker-sync will sync your data to php and nginx/apache containers.

For more information visit docker-sync project page.

Permissions issues

You might have permissions issues caused by non-matching uid/gid on your host machine and the default user in php container.


Since version 5.0 the default php container user wodby has uid/gid 1000 that matches the default uid/gid for most popular Linux distributions.


Use -dev-macos version of php image where default wodby user has 501:20 uid/gid that matches default macOS user.


Since you can't change owner of mounted volumes in Docker for Win, the only solution is to run everything as root, add the following options to php service in your docker-compose file:

    user: root
    command: "php-fpm -R"
      PHP_FPM_USER: root
      PHP_FPM_GROUP: root

Different uid/gid?

You can rebuild the base image wodby/php with custom user/group ids by using docker build arguments WODBY_USER_ID, WODBY_GROUP_ID (both 1000 by default)

Running multiple Projects

Træfik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. To understand the basics of Traefik it is suggested to check Træfik's documentation page:

Image: Multi-domain set-up example (Source:

Steps to set up two projects on one host:

  1. Create two dirs where you will host two projects. Let's name them site1 and site2
  2. Copy docker-compose.yml file to both dirs (site1 and site2)
  3. Download traefik.yml file (inside of tar.gz archive) from the latest stable release to the parent dir where site1 and site2 dirs are
  4. Edit traefik.yml and change project1-dir_default to site1_default and project2-dir_default to site2_default. Those are docker networks names that are created automatically from the dir name where docker-compose.yml is located
  5. Edit site1's docker-compose.yml file. There are 3 main things that need to be done there:
    • In nginx service, under labels, change traefik.backend=nginx to traefik.backend=site1_nginx_1. This is the name of the container. You can see that under NAMES when your have the containers running by executing docker ps
    • Change traefik.frontend.rule from Host:php.docker.localhost to Host:site1.docker.localhost
    • Comment out all lines of traefik service at the bottom of the file
  6. Make similar 3 changes in site2's docker-compose.yml file:
    • traefik.backend=nginx to traefik.backend=site2_nginx_1
    • Host:php.docker.localhost to Host:site2.docker.localhost
    • Comment out all lines of traefik service at the bottom of the file
  7. Run docker-compose up -d in site1 and site2 dirs to spin up containers for both projects
  8. Run stand-alone traefik docker-compose -f traefik.yml up -d to spin up traefik reverse proxy
  9. Visit http://site1.docker.localhost and http://site2.docker.localhost in your browser

This set up also works for any Docker projects. You can replace nginx-proxy config with Traefik and get other projects all routed with on traefik container.

For macOS users with docker-sync

Make sure names of syncs in docker-sync.yml are unique per project. The recommended way is to run a stand-alone docker-sync with syncs definition for all projects. Do not forget to update src paths for projects

In case of issues:

  • Check docker ps to see which containers are running and check if you have set up all names correctly.
  • Check docker network ls to check if the network names are matching.
  • Run docker-compose logs -f in site1 or site2 to see the log of each project.