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:
Features:
- Easy to set and start up a project (
docker-compose up
). - 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
Dockerfile
. - 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.yml
to fit your project needs (only a few lines to add Redis, MongoDB, RabbitMQ...)
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.
- Cross-platform.
The most important files in our containerized environment are Dockerfile
and docker-compose.yml
. You can find both
in the Fork CMS root directory.
A very basic example of a Dockerfile
and docker-compose.yml
:
- 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. - Our
docker-compose.yml
file, found in the root, describes how our services work together. We only have 2 services: the app and the db service. In thedocker-compose.yml
file, 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.
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.
Take a quick look at the differences between lightweight Docker containers and virtual machines:
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!
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
and docker-compose
in the terminal.
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 git@github.com: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 -d
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
the terminal.
- 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:
- Hostname:
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). - Port:
3306
- Username:
forkcms
- Password:
forkcms
- Database:
forkcms
- Hostname:
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.
Go to
http://localhost
to see Fork CMS.
Video - watch how to setup PHPStorm 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.
In the var/docker
folder you will find a .env
file containing some defaults for Docker.
- You can edit the
.env
file 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
PHP_IDE_CONFIG
key in.env
and 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). -
Place a breakpoint in your code, start listening for incoming debug connections by clicking the telephone icon
. Now, open your browser and visit the page you want to debug and start debugging!
-
Because we inject
XDEBUG_CONFIG
as 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.
Video - watch how to setup PHPStorm with 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
/var/www/html/bin/simple-phpunit
. - Also point PHPUnit to the correct files:
- Configuration file:
/var/www/html/phpunit.xml.dist
- Bootstrap file:
/var/www/html/autoload.php
- 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:
Purpose | Command |
---|---|
Start docker containers with logs | docker-compose up |
Start docker containers (detached mode) | docker-compose up -d |
List running containers | docker-compose ps |
Close all running containers | docker-compose stop |
Stop a single container | docker-compose {service} stop |
Delete all existing containers |
docker-compose down * |
Delete all existing containers and volumes |
docker-compose down -v * |
Rebuild the docker containers (after modifying Dockerfile) | docker-compose build |
View the Log files | docker-compose logs -f -t {container-name} |
Run bash on a service container (think of SSH'ing) | docker-compose exec {service} bash |
Pull the latest image updates | docker-compose pull |
List all the images you have installed | docker-compose images |
Remove unused images from your system. Great for saving space! | docker image prune |
Remove unused volumes from your system. Great for saving space! | docker volume prune |
Remove unused containers, images, networks and volumes. | docker system prune |
* 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/logs
and /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.
- First show the current running containers with
docker-compose ps
and verify that they're up and running. - Enter your
app
container 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
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 docker-compose.yml
.
Open the 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 docker-compose restart
.
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.
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 docker-compose.yml
or docker-compose.override.yml
file:
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
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 var/docker/php/php.ini
.
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.
Choose a specific PHP version from the Docker store's PHP base container, edit the Dockerfile
and change the FROM
statement to include a specific version of PHP as base image. Make sure to build the docker containers again
using docker-compose build
!
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 docker-compose.yml
.
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.
More info about the issue here and here.
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?
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.
The var/cache
and 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.