Local development using Docker
Fork CMS comes with a Docker PHP development environment that facilitates running your website in a containerized environment.
Included is a
docker-compose-powered stack to get your Fork CMS PHP project up and running in no time, without the hassle of
installing and configuring PHP yourself locally, like in a Vagrant box or in MAMP/WAMP.
By default the containerized development environment comes with:
- Easy to set and start up a project (
- Easily disposable stack (
docker-compose down --volumes).
- Easily switch between PHP versions: 7.1, 7.2, ... by editing the Dockerfile to fit your needs.
- Easy to customize, e.g. add PHP extensions, with a simple edit to the
- Clean and well structured Dockerfile
- Simple configuration (single .env file)
- All containers extend from an official, trusted base image.
- Everything is visible and editable to fit your needs of the project.
- Docker runtime independent (run it on Docker for Mac, VM, AWS...)
- Any team member can quickly boot up your project because the Dockerfile and docker-compose files are added to your version control.
- Easily add new containers to
docker-compose.ymlto fit your project needs (only a few lines to add Redis, MongoDB, RabbitMQ...)
Table of Contents
- What is Docker?
- Running ForkCMS on Docker
- PHPStorm configuration
- Docker cheatsheet
What is Docker?
Docker is a platform that allows us to develop, ship and run applications. With Docker, you can manage your infrastructure in the same ways you manage your application.
Docker containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.
Docker has 3 important advantages:
- Consistency. Everything that is needed for the application to run, gets packaged into 1 or more containers.
- Ease of implementing new technology. Ever wanted to try out a new database (Redis, Elasticsearch, ...)? Adding it to your stack is easy with Docker.
The most important files in our containerized environment are
docker-compose.yml. You can find both
in the Fork CMS root directory.
A very basic example of a
- The Fork CMS
Dockerfile, found in the root, describes that we start from a "base image" (php+apache), typically found on the Docker Store. We add our project-specific configuration to this base image: additional php extensions, custom php.ini settings, Xdebug, installation of the composer dependencies, ... This Dockerfile will be used to build a Docker "image" which is used to boot up the container.
docker-compose.ymlfile, found in the root, describes how our services work together. We only have 2 services: the app and the db service. In the
docker-compose.ymlfile, we specify that the app-service is linked and can communicate with the database, which ports we expose to our host machine, which environment variables are set inside the container, and which volumes get mounted from our host computer to the container.
Benefits over Vagrant and local webservers
So why use Docker and not Vagrant? While Vagrant creates a virtual machine in minutes, Docker creates virtual "containers" in seconds. Instead of providing a full VM like Vagrant does, Docker provides you with lightweight containers that share the same kernel and allow you to safely execute independent processes. This means that Docker uses only what it needs to run your application — nothing more. In addition to speed, Docker gives you a lot of features that cannot be achieved with Vagrant. The most important fact about Docker is that it can be used for development and on production, ensuring a consistent environment everywhere that bundles with the code you write. Vagrant is designed for development only, so a production server still needs to be provisioned every time.
While you can run and develop Fork CMS websites on a local webserver (WAMP, MAMP Pro even has Fork CMS integration), these local webservers are usually good as a "one size fits all" solution. If you occasionally create a website, using shared hosting, then WAMP/MAMP probably fits your needs. Nevertheless, Docker is definitely worth looking into!
Running ForkCMS on Docker
Installing Docker on Mac/Windows
The easiest way to run Docker containers on your computer is by installing Docker for Mac. For Windows-based systems, you can download Docker for Windows. If you're a Linux user, you're lucky because Docker is meant to run on Linux. Find the installation for your Linux distro on docker.com. E.g. Docker for Ubuntu.
After installing, you can run Docker. Make sure to visit the preferences and assign it a decent amount of CPU's and
memory (at least a few GB"s). While Docker is running, you can use commands such as
docker-compose in the terminal.
Run your local Fork CMS website using Docker
If this is an existing project, remove your
app/config/parameters.yml. This ensures that we can install a
fresh copy and it won't try to look for an existing database. You can import a database backup later.
Let's see how easy it is to get Fork CMS up and running on PHP7, Apache and MariaDB.
- Clone Fork CMS
git clone firstname.lastname@example.org:forkcms/forkcms.git
- Spin up the Docker containers:
docker-compose up -d
This command will look at the
docker-compose.yml file and spin up a container for every service we defined (app and db).
Wait for the containers to boot. The first time this can be slow because it needs to build the
Dockerfile container image.
Once that's done, starting and stopping the containers will be very fast. We start the containers with the option
to make sure they start in "detached" mode, which basically means that they'll run in the background and our website won't stop running when we quit
- Check if the containers are up and running now:
docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------------------------- forkcms_app_1 docker-php-entrypoint apac ... Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp forkcms_db_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
You should see something similar appear: two containers are up and running. One container for the webserver with php and one
for the database. Port 80, 443 and 3306 are exposed to our host machine. If one of the containers is down, you can
inspect the logs by running:
docker-compose logs -f app or
docker-compose logs -f db.
- Go to http://localhost and you should see the Fork CMS installer appear.
- Follow the steps of the installer
- Use the following credentials when the installer asks you for database settings:
db(the name of the database Docker service. As PHP runs inside a docker container, we cannot use localhost or 127.0.0.1 here).
You can easily use different credentials by adjusting them in the
docker-compose.yml file, but these are the default.
- Finish the installation process. That's it, enjoy your fresh local Fork CMS installation powered by Docker.
http://localhostto see Fork CMS.
General setup and XDebug
It's easy to set up PHPStorm in combination with Docker and Xdebug, one of the most popular tools to debug your PHP application.
var/docker folder you will find a
.env file containing some defaults for Docker.
- You can edit the
.envfile to add or change in some information for your system and configure XDebug. By default, it contains a special hostname available for Docker for Mac users, but you can change it to point to your own Docker host IP.
- Recreate your Docker containers, because you modified the environment variables.
docker-compose down docker-compose up -d
In PHPStorm's preferences, go to "Build, Execution, Deployment > Docker". Make sure you create a Docker configuration that connects to the Docker daemon "Docker for Mac".
We also need to configure PHPStorm to use the PHP interpreter that resides inside the app container. To do so, go to preferences to "Languages & Frameworks > PHP". Click the "..." icon for the PHP interpreter. Create a new interpreter and use type "Docker". Select the Docker for mac server you created, and the Docker image name for the project you're working on. Make sure PHPStorm detects your php version correctly (see video above). Next, configure the Docker container by clicking the "...", and fill in the Network mode with the value you find when running
docker network ls. Next, go to Volume bindings and make sure the container path is set to
/var/www/html. This is the path where our code lives inside the container.
Let's configure Xdebug now. In PHPStorm's preferences, go to the PHP > Debug section. Make sure Xdebug accepts connections and listens on port 9000 (should be default configuration). Go to DBGp Proxy and enter the following settings:
- IDE key: PHPSTORM
- Host: leave blank
- Port: 9000
Optionally: Go to Languages & Frameworks > PHP > Servers. Make sure you have a server with a name that matches your
.envand make sure to map the project root path with the server correctly. Configuring a server is an optional step as the first time you put a breakpoint, turn on XDebug and visit your website, PHPStorm will detect the incoming connection and set up the server configuration automatically (see video above).
Because we inject
XDEBUG_CONFIGas environment variable (see docker-compose.yml), it's also possible to debug Symfony commands. Simply put a breakpoint in your PHP command and run the command inside Docker:
$ docker-compose ps # View which containers are running $ docker-compose exec app bash # Go inside the Docker app container root@b50c9fefe23d:/var/www/html# bin/console forkcms:cache:clear # Run your command and PHPStorm will help you debug it.
Running tests in PHPStorm with Docker and PHPUnit
- Go to Preferences, "Languages & Frameworks > PHP > Test Frameworks". Create a new configuration and choose "PHPUnit by Remote Interpreter".
- Choose the PHP interpreter you created in the previous steps.
- We need to point our configuration to the PHPUnit executable. Choose "Path to phpunit.phar" and point it to
- Also point PHPUnit to the correct files:
- Configuration file:
- Bootstrap file:
- Configuration file:
- Now run a test in PHPStorm: right-click on the testfile and choose "Run". Do you want to debug a test? Place a breakpoint in the code and right-click on the testfile and choose "Debug".
A few commands that you will definitely need:
|Start docker containers with logs||
|Start docker containers (detached mode)||
|List running containers||
|Close all running containers||
|Stop a single container||
|Delete all existing containers||
|Delete all existing containers and volumes||
|Rebuild the docker containers (after modifying Dockerfile)||
|View the Log files||
|Run bash on a service container (think of SSH'ing)||
|Pull the latest image updates||
|List all the images you have installed||
|Remove unused images from your system. Great for saving space!||
|Remove unused volumes from your system. Great for saving space!||
|Remove unused containers, images, networks and volumes.||
* You can safely delete all containers, because your local PHP files are volume-mounted into the Docker container.
You won't lose your database either, as this data is stored in your project's
/var/docker/db/data folder and gets mounted as a volume.
* We store the
/var/cache folders in their own named volume. This means that the Symfony logs and cache
will not sync from the container to your device, which gives a performance boost and avoids write permission issues.
I want to run a PHP command inside a running container
- First show the current running containers with
docker-compose psand verify that they're up and running.
- Enter your
appcontainer and run the command:
# Bash into the container (think of SSH'ing but not quite like that) $ docker-compose exec app bash root@b50c9fefe23d:/var/www/html# bin/console forkcms:cache:clear # Or run the command with a oneliner $ docker-compose exec app composer install $ docker-compose exec app bin/console forkcms:cache:clear $ docker-compose exec app bin/console doctrine:schema:update --force $ docker-compose exec app composer test
My containers won't start and throw a "Failure EADDRINUSE" error
ERROR: for forkcms_app_1 Cannot start service app: driver failed programming external connectivity on endpoint forkcms_app_1 (39e623f7a1b0f8361f3d14383f7c56af173e71282fae26495340eb1cdfa94de1): Error starting userland proxy: Bind for 0.0.0.0:80: unexpected error (Failure EADDRINUSE)
The Fork CMS containers run on port 80 and 3306. Check on your host machine if you don't have another webserver running on port 80,
or MySQL running on port 3306. Shutdown those services and try bringing your containers up again. If you want to specify a
different port to run the containers, modify the exposed ports in
I want to edit the default container configuration
docker-compose.yml and change anything you want. Examples:
Change MySQL database name:
environment: - MYSQL_DATABASE=myprojectdatabase
Change the default Apache port from 80 to 8080 (format for ports is host:docker).
ports: - "8080:80"
Any change you do in the
docker-compose.yml file, requires a restart of the containers.
You can run
docker-compose up -d again and it will restart the changed container, or just run
Note: you can also create a
docker-compose.override.yml and apply your changes here. Docker automatically reads this file
and applies it as an override of the base Fork CMS docker setup. This way you can avoid merge conflicts.
I want to add more software (docker images) to my project
Imagine that your project also needs a Redis key-value store, or a MongoDB, Elasticsearch, RabbitMQ, ...
With Docker, you can spin up a container with that software in seconds.
Example: for Redis, simply add a new Docker service to your
redis: image: redis:latest ports: - "6379:6379"
And add a link to your
app service so PHP is allowed to communicate with the Redis container:
app: depends_on: - db - redis
Learn more about the docker-compose syntax on the official Docker documentation page
I want to install additional PHP extensions or tweak settings
Edit the php+apache
Dockerfile in the root and add the needed
RUN commands to install the PHP extensions of your choice.
PHP settings can be tweaked inside
Make sure you build the
Dockerfile to ensure that it has no errors, and then start up your containers again:
$ docker-compose build $ docker-compose up -d $ docker-compose exec app php -i # Check the phpinfo configuration to see that your extensions are active.
I want to change my PHP version
Choose a specific PHP version from the Docker store's PHP base container, edit the
and change the
FROM statement to include a specific version of PHP as base image. Make sure to build the docker containers again
I want to connect to the MySQL database from my host machine
You can use a MySQL client app such as TablePlus (free, Mac/Linux/Windows) or Sequel Pro (free) on Mac.
If you're using Docker for Mac/Windows, simply connect to 127.0.0.1 with port 3306. Use the credentials found in
Slow pageloads on Docker for Mac
Sharing code into Docker containers with osxfs has very poor performance compared to Linux. This is especially noticeable in PHP apps as they are not compiled and just interpreted at runtime from the mounted volume. There are some workarounds to speed up things, such as using docker-sync. Fork CMS implements User-guided caching which speeds up performance by 2.7 times.
My container keeps on exiting and won't start
Make sure to take a look at the logs, using e.g.
docker-compose -f --tail=100 logs app for the application container.
It might be that you see an "address already in use" or "port is already allocated" error. In that case, make sure
the ports for the services you are trying to run (80, 3306) are not being used by other programs on the host, such as
the built-in apache/httpd service on Mac, or maybe MAMP server is still running on port 80?
I want to deploy my Docker container to a production environment.
The default Fork CMS docker containers are not recommended to use in production. One would have to remove devtools (Xdebug), tweak PHP.ini settings, etc.
Why is the cache & logs folder in a separate named volume?
var/logs folders are never modified by the user directly. There's no benefit in
having Docker sync those folders back and forth from the container to the host machine. Therefore, it's
a good practice to keep them in a separate volume. It improves the performance, avoids permission errors, ...
If you need to clear the cache, just execute
bin/console cache:clear inside the container as you're used to,
or delete the volumes using
docker-compose down --volumes and restart the environment.