A Story of Laravel, Docker, Xdebug and a Mac
Some weeks ago I decided to ditch the old Mamp Pro that has servantly been part of my development ecosystem for so many years and try one of those fancy alternatives, Docker. I liked the idea of not depending on my local setup to run the projects I work on. Several times before, I encountered issues with different database engines, php version, too many or too few extensions, folder permissions, etc. This seems like a solution to all this.
In another post, I cover how to get local domains working with multiple compose files and all without breaking
xdebugor losing your mind, so you don’t have to use
0.0.0.0:8081and can do
Installing Docker for mac
This is the most straightforward part of the process, we go to docker’s website, download the app, and install it. Why am I writing this in its own paragraph?. There’s another app for mac though! , and it’s the wrong one, so let me just link you guys to the correct one.
Ok, I have the app, what now?
Creating a compose file and a Dockerfile
Our project will be running from something called a compose file, which is a yml definition of the services that will make up our machine. Those services come from the docker repository and they cover pretty much anything we can think of.
There are repositories you can use as a base that are already made for laravel but I chose to use the php repository, specifically
php:7.1-apache which will allow me to further customise my environment.
When you define your compose file, the services defined on the yml file will come from images, either directly or built from a Dockerfile script. In our case, we’ll be defining two services, the database which will run
mariadb and the
httpd one which will run apache.
For the database one we only need to specify some configuration but a stock image will be all we need. For the server one though, we need the above-mentioned dockerfile.
I don’t want to go over this line by line since once you have the example is very easy to locate the pertinent documentation to learn more about it. I added little comments to the file for my own sanity.
You can specify a custom name for a container by using the property
container_name however, it’s better to let the name be autogenerated and use the container folder as the custom part. For instance, in this example the folder holding the compose file is named
dock and the containers when launched are named
dock_db_1. You know what to do from here.
Depending on your docker engine version, the version of the compose file below will need to be changed. This, and other really useful information can be found on the Compose File Reference
#This file is inside a folder named 'dock' at the root of my laravel project
#PHP with apache
#image will be built by processing the Dockerfile in this path
#map ports between host and the service
#map host folders (relative to this file) to service
#mark the db services as a dependency
#give an alias to this, to refer to it without knowing ip
#storing the mysql folder outside allows persistence
#of databases between executions
Essentially, specify which repository it will use as a base for the service, then run all the commands we need to customise it to the particulars of our project. I needed certain libraries and of course that includes
We could have more than one Dockerfile, to build our images, in this case it’s a stock
mariadb image and the one built on top of the
RUN a2enmod rewrite
# Install developer dependencies
RUN apt-get update -yqq && apt-get install -y git libsqlite3-dev nano libxml2-dev libicu-dev libfreetype6-dev libmcrypt-dev libjpeg62-turbo-dev libpng12-dev libcurl4-gnutls-dev libbz2-dev libssl-dev -yqq
# Install xdebug for development
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
#Install other php extensions
RUN docker-php-ext-install sockets \
&& docker-php-ext-install mysqli \
&& docker-php-ext-install pdo_mysql \
&& docker-php-ext-install json \
&& docker-php-ext-install xml \
&& docker-php-ext-install zip \
&& docker-php-ext-install bz2 \
&& docker-php-ext-install mbstring \
&& docker-php-ext-install mcrypt \
&& docker-php-ext-install curl
RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/
RUN docker-php-ext-install gd
# Copy the configuration file into xdebug, if running phpinfo() you see the loaded file is not this one, change the path accordingly.
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
# install composer
RUN curl --silent --show-error https://getcomposer.org/installer | php
You can see that on the
Dockerfile we have a command that will
copy a file named
xdebug.ini to a certain folder. That folder is where the configuration for xdebug is pulled from, I did leave a comment on the Dockerfile but let me insist, since this took me a long time to figure out. If it’s not working make sure you’re loading the correct configuration file for xdebug, different servers and versions will use different folders, so it’s always a good idea to double-check.
Wait, what is 10.254.254.254?
If you’re reading the previous file and wondering if you should replace that with your actual host IP. No. What you need to do is set your docker environment’s IP to that one. There are several methods listed on the internet, including one where you’d use a special Mac-only DNS thingy
docker.for.mac.localhost but I didn’t manage to get this to work.
What we are going to do instead is follow the instructions from here which are in fact just running the following command and rebooting.
sudo curl -o /Library/LaunchDaemons/com.ralphschindler.docker_10254_alias.plist https://gist.githubusercontent.com/ralphschindler/535dc5916ccbd06f53c1b0ee5a868c93/raw/com.ralphschindler.docker_10254_alias.plist
Building the beast
dock-mysql folder at the same level as the one holding
dock, feel free to rename this to anything you like as long as you update the other files to reflect it.
The name you give to the folder will be prepended to the name of the running service
Making sure you have docker for mac running, you need to navigate to the folder we created which contains the three files above. Once there you need to execute
docker-compose build which will attempt to build the services from the yml definition.
After this, we should be ready to try to launch the services, sorry if I’m naming stuff incorrectly. Running
docker-compose up -d will launch them, the
-d flag specifies that we want to run them in a detached mode that returns the names of the newly launched services, a service is an image that runs in a container.
Accessing the services
Another thing we need to get our heads wrapped around is the fact that we no longer navigate to a folder in our system to run commands, we need to do enter the machine that’s running the service to do that. It’s pretty simple and convenient.
First, we need to get a list of the services that are running in order to get the id of the one we want to access. Do this running
Say we want to connect to the apache service to run some artisan command for our laravel project like running migrations with seed. We connect by running
docker exec -it ae40da4193ef /bin/bash where
ae40da4193ef is the id of the container.
Once inside we need to navigate to the right folder, which contains our project and run the command as we would normally.
Housekeeping and useful stuff to know
After this, all you need is to configure your ide to listen for connections and your browser to send the correct tag which we specified in the xdebug.ini file.
How do I stop the running services?
docker-compose down — remove-orphans
How do I force a rebuild?