Developing Symfony applications with Docker series part I: Getting started

In this series I’m gonna share all that I’ve learned while switching from a Vagrant powered environment – running all required software in a single VirtualBox instance – to a Dockerized setup where every process runs in a separate container. But what exactly is Docker? From the Docker site:

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.

Now that sounds great, doesn’t it? As a matter of fact it does, but you have to get a grip on the concept before it starts paying off. In these blog post series I’ll show you how to create a multi container Symfony application and how to get the full potential out of it. For now I’ll only focus on using Docker as develop environment. Perhaps a new series for Docker in production will follow in the near future :).

I’m a Mac OS X user so some problems I describe are related to the fact that I have to use a virtualisation layer to use Docker. If you’re happen to be on Linux, you can just skip those sections.

Installation

VirtualBox is required to run a Linux virtual machine so make sure you have a recent version installed. On the Docker site follow the installation instructions. When you’re done you should have docker, docker-compose and docker-machine binary available to you.

Create Linux virtual box

With docker-machine it’s fairly easy to create and manage a virtual machine for running Docker in. Let’s create a new instance:

If you need a box with more memory (this can happen when you have a lot of containers) you can create one with more RAM with --virtualbox-memory "2048". You’ll receive a no space left on device error when that happens.

All left to do is setting correct environment variables so Docker daemon knows how to connect our box:

Now let’s try to see if it works by listing the containers:

Create Symfony project

Now we’re ready to create a new Symfony project:

Configuring docker-compose

Docker-compose reads its configuration from a docker-compose.yml file, so create a empty one in the root of your shiny new project.

You should end up with a directory structure like this:

Install php and nginx

We’re almost there, so hang on. Obviously we’re gonna need php and a webserver so let’s install php-fpm and nginx. Never reinvent the wheel, so when you need a service containerized, always search it on Docker hub. As with bundles: there’s a container for that. We’ll use the official php and nginx images for now.

Heads up: When adding more images to your configuration take note from which image they derive. Most images extends from debian:jessie, which you probably want for your images. Docker works with a layered file system, so if all your images derive from the same parent, that will speed up the build process and also consume less space (also during transfer!).

Edit your docker-compose.yml like this:

The root element is the name of the container, you can pick whatever you like. I always try to keep these short so it’s easier (less typing) when running commands against a specific container.
The image field tells docker-compose which image we want to use for our container. The ports field allows us to expose ports on the container and forward port(s) from the container to the host, so we actually connect to the container. The value "8080:80" means we’re exposing port 80 on the container and forward it to port 8080 on the host.

You should start the containers now:

Docker will pull the images from the registry, build and start them. The -d flags tells Docker to run the containers daemonized in the background. I’ll get back on that later. When it’s done, verify if they’re both up and running:

Connecting to the box

We’ve forwarded port 8080 to our host, but connecting to localhost:8080 doesn’t work (you did try the link, didn’t you? :)). Because Docker runs in a virtual machine, we need to figure out its IP so we can connect it. Of course this isn’t very difficult:

Let’s try that IP on port 8080 and you’ll see it works: http://192.168.99.100:8080/. You’ll want to add an entry for that IP in your /etc/hosts file. Let’s pick symfony3.dev for now.

As you’ve probably discovered by now, we’re presented the default nginx page and not our shiny new Symfony application. To fix this we have to link the php container to the nginx container so they can communicate with each other. The php-fpm container needs access to our project’s php files in order to parse and serve them. Also, the nginx container requires a nginx configuration. We have to alter the Docker image and for that we need a Dockerfile.

Custom Dockerfile

The Dockerfile represents every step to be taken before the container is ready to use. Normally you would use a configuration management tool (Ansible, Puppet, Chef) to accomplish this, but in Docker you manage this via the Dockerfile.

It’s import to know that each line should contain one step. Each line creates a new layer and the number of layers is limited. One logical step per line improves the caching mechanism. For more information regarding this topic, refer to the best practices.

To configure nginx we’re going to use the nginx configuration supplied by the Symfony team. We have to copy it into the container. Create a new directory docker/nginx in the project root and add the following Dockerfile:

Create a symfony3.conf file in that docker/nginx directory as well and fill it with the following configuration:

In case you haven’t you should add symfony3.dev to your /etc/hosts file with the IP from docker-machine ip docker-tutorial.

Now let’s put it all together and update our docker-compose.yml accordingly:

Take note of the changes we’ve applied: image under nginx is replaced with build: docker/nginx which refers to the directory where the Dockerfile resides. The nginx container has a links key where we link it to the php container. Both containers have a volumes key where we mount the current directory into the container under /app path. This way the container has access to the project files.

Stop all containers and build them:

Then start them again:

When you visit http://symfony3.dev:8080/app_dev.php in your browser, you’ll see the You are not allowed to access this file. Check app_dev.php for more information. message. Remove the access check from app_dev.php and try again.

Unfortunately another well known error pops up: Failed to write cache file “/app/var/cache/dev/classes.php”..

Permissions

In my opinion the best solution to this problem is to run the console commands and php-fpm process under the same user. Without any modifications, the console commands are run under root and the php-fpm process runs under www-data. To accomplish this we also have to use a Dockerfile for the php container.

Again, stop all containers:

Create a new directory php-fpm under the docker directory. Add the following Dockerfile:

Also, add the following php-fpm.conf file:

Because I suck at naming new users I just use vagrant as my development user. Think of it as a tribute to vagrant :). The docker directory tree should be:

Now build and run the containers:

If you visit http://symfony3.dev:8080/app_dev.php now, you’ll see the Symfony welcome pages smileys at you. With this “hello world” for Symfony working we end this first post.

Next post I’ll show you how to speed up things if you’re on a Mac (the default Symfony app takes ~2000 ms to load in the current situation). Also, I’ll show you the possibilities to store your data when working with containers.

34 thoughts on “Developing Symfony applications with Docker series part I: Getting started

  1. Great post! My team is also in the process of migrating our development environment from Vagrant to Docker, but as of now we haven’t succeed in setting up the XDebug+PHPStorm integration, which is kind of a deal breaker. Do you have any advice?

    We are looking forward to the next posts of the series 🙂

    1. Hi Marcel,

      I’ll try to cover that topic in one of the next posts. It is possible, but a bit hackish at the moment.

    2. Great tutorial, you have a small error though.

      I believe the command “docker-composer up -d” should be “docker-compose up -d”

      1. Sorry – forgot to mention.
        I’m using a Mac. VirtualBox 5.0.12 and Vagrant 1.8.1
        Up to now I use vagrant + puppet for our symfony projects (old 1.4 and 2.x) and debugging / troubleshooting is easy. There is a bash you can ssh into your vm, but with docker the approach is a little bit different I guess.

        1. With docker it is still possible to SSH into the box:
          docker-compose run php bash

          While you’re testing my previous command, could you take a look at the permissions in the /app/var/cache dir?

  2. Hi, file permission 644 / dir permission 755. Strange thing here is, cache folders (dev, prod) and files are created with the user 1000:staff.

    1. This is what I’m using:

      sh
      setfacl -R -m u:www-data:rwX -m u:
      whoami:rwX var/cache var/logs
      setfacl -dR -m u:www-data:rwX -m u:
      whoami:rwX var/cache var/logs

  3. This may be a silly question, but I’ll ask it anyway: Does simply putting the php-fpm.conf file in the docker/php dir do the trick?
    Doesn’t it have to be copied into the container’s PHP config directory?

    1. Indeed – and as a heads up to anyone else using this post. It seems to be full of holes/missed steps… not tested for sure.

  4. In docker/php-fpm/Dockerfile we need to add this line at the bottom:

    COPY php-fpm.conf /etc/php-fpm.conf

    And then the docker-compose.yml should be updated in its lower part to look like:

    php:
    # image: php:7.0-fpm # not needed anymore
    build: docker/php-fpm
    volumes:
    - ./:/app
    working_dir: /app

    1. db:
      image: mysql:5.7
      ports:
      – “3307:3306”
      environment:
      MYSQL_DATABASE: FloSports
      MYSQL_USER: flosports
      MYSQL_PASSWORD: flosports
      MYSQL_ROOT_PASSWORD: flosports
      volumes:
      – ./data:/var/lib/mysql

  5. I would prefer developing a Symfony application on Cloudways platform instead. Since it is a managed hosting platform, I don’t have to worry about manually install the OS and stack or configuring other server packages. I just login to the platform launch a PHP stack and install the Symfony 3 using composer. Don’t you think this method is quicker and easier? Here is the tutorial: https://www.cloudways.com/blog/install-symfony-3-on-cloud/

  6. Hello, I am getting the same problem as Ugur.
    File not found after setting the nginx and symfony configuration files and rebooting.
    Any clues?

  7. Nice Post, I’m looking at deploying symfony with docker. I’m wondering… if you are deploying via docker how are you handling the setting of live DB credentials?

    1. E.g. Storing a live parameters file in your private repo / storing DB credentials as part of your docker files / setting interactively after build with your chosen CI tool such as Jenkins.

  8. Quite unprofessional of the article writer. So many people have asked the same query of not found, so do i. Not even one’s answer is replied to.

  9. If there is lone phobia, you should be doing pro yourself advantageous on occasion – journey by as much help over the extent of yourself as you can. Pakistan reseach repository phd thesis. This means contacting a licensed anonymous publication serving to improve you with your papers. Color critical essay gay homosexuality lesbian research writer. This is near doing yourself a favor and letting us do the between engagements in behalf of you while you join in following and relax.

  10. Thanks for a great article. There’s a small typo:

    docker-composer up -d

    should be

    docker-compose up -d

Leave a Reply

Your email address will not be published. Required fields are marked *