Moving Symfony files in the Docker container
Tags: experience, docker, php, symfony
Published: 2024-03-16 10:09:39

---
title: Moving Symfony files in the Docker container
published: true
date: 2018-11-22 11:22:48 UTC
tags: Experience,Docker,PHP,Symfony
canonical_url: https://io.serendipityhq.com/experience/moving-symfony-files-in-the-docker-container/
---

Once you have created the [first configuration of your basic Docker stack](https://dev.to/aerendir/how-to-dockerize-a-symfony-application-introduction-and-the-web-server-3303-temp-slug-4506360), it is time to move the Symfony’s files into Docker’s web server service.

This will make us able to run the symfony application from inside the web server container, getting the real benefits of using a system as Docker (same environment on any machine, above all).

So, we need a way to move in the container all the files required to make our project run.

How do we do this?

The answer to our question is a really simple word: “Dockerfile” (never heard of it? Ok, it is a not so simple word! But it is simple to use it anyway! ![🙂](https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png) )

From the Docker documentation [about “Dockerfile”](https://docs.docker.com/engine/reference/builder/):

> Docker can build images automatically by reading the instructions from a Dockerfile
> 
> A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
> 
> Using docker build users can create an automated build that executes several command-line instructions in succession.

So, basically, all what you write in the console to configure your environment has to be put in a Dockerfile to automate the process.

Anytime someone wants to configure the environment to run our app, (s)he will not have to take care of the details as they are all in the Dockerfile: easy and fast!

So, let’s prepare our app to use Dockerfiles.

## Create the folder structure and the `Dockerfile`

First of all we need to create some folders.

So, in the root of your app, create a folder called `docker`, inside it create another folder called `build` and inside it another folder called `apache`, then inside it create a file called `Dockerfile`.

The final folder structure will be like this:

![](https://io.serendipityhq.com/wp-content/uploads/sites/2/2018/08/docker-apache-dockerfile-folder-structure.png)

The next steps are:

1. Put in the `Dockerfile` the commands required to build the `apache` container;
2. Configure `docker-compose.yaml` to use this new `Dockerfile`.

As first thing, instruct the `Dockerfile` about the Apache image we want to use:

```
# docker/build/apache/Dockerfile

FROM httpd:2.4

```

The `httpd:2.4` is exactly the same we used in our `docker-compose.yaml` file.

Now, edit the `docker-compose.yaml` file so it will use this `Dockerfile`: we will do this using the [`build`](https://docs.docker.com/compose/compose-file/#build) key node:

```
version: '3.7'
services:
  # "php" was "language" in previous example
  ...

  # Configure the database
  ...

  # Configure Apache
  apache:
    # image: httpd:2.4 <- Remove this node
    build: docker/build/apache
    ports:
      - "8100:80"

```

Once edited the `docker-compose.yaml` file, first run `docker-compose down` to stop all the containers, then run `docker-compose build`:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose down
Stopping app-aragog-www_mysql_1 ... done
Stopping app-aragog-www_apache_1 ... done
Removing app-aragog-www_php_1 ... done
Removing app-aragog-www_mysql_1 ... done
Removing app-aragog-www_apache_1 ... done
Removing network app-aragog-www_default
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
php uses an image, skipping
mysql uses an image, skipping
Building apache
Step 1/1 : FROM httpd:2.4
 ---> fb2f3851a971

Successfully built fb2f3851a971
Successfully tagged app-aragog-www_apache:latest

```

As you can see, the `php` and `mysql` containers are skipped as they use an image, while the `apache` container is built as it uses a `Dockerfile`.

Try to start the containers with `docker-compose up -d`:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose up -d
Creating network "app-aragog-www_default" with the default driver
Creating app-aragog-www_mysql_1 ... done
Creating app-aragog-www_php_1 ... done
Creating app-aragog-www_apache_1 ... done

```

And access the URL `http://127.0.0.1:8100`: you see the message “It works!”.

![](https://io.serendipityhq.com/wp-content/uploads/sites/2/2018/08/docker-apache-port-binding.png)

Ok, the first step is done: the Apache web server is continuing to work also if we are using a different method to configure it.

Now we need to do the second step: add the Symfony files to the document root of Apache.

To do this, we first need to understand what is the Docker Context as it will be extremely useful.

## The Docker Context: what is it, why we need to understand it and how to use it

Before diving in the files and their copy in the container, we need to understand what is Docker Context.

From the documentation about [`build`](https://docs.docker.com/engine/reference/commandline/build/#extended-description):

> The `docker build` command builds Docker images from a Dockerfile and a “context”.
> 
> A build’s context is the set of files located in the specified `PATH` or `URL`.
> 
> The build process can refer to any of the files in the context.
> 
> For example, your build can use a `COPY` instruction to reference a file in the context.

So, to copy our Symfony files to the container, we need to make them available in the context.

We are using a `docker-compose.yaml` file, so we need to understand how to specify the context when using it.

The solution? The [`context` key of the `build` node](https://docs.docker.com/compose/compose-file/#context).

The documentation says:

> Either a path to a directory containing a Dockerfile, or a url to a git repository.
> 
> When the value supplied is a relative path, it is interpreted as relative to the location of the Compose file.
> 
> This directory is also the build context that is sent to the Docker daemon.

But there is more: we can also set a [specific `Dockerfile`](https://docs.docker.com/compose/compose-file/#dockerfile) to use when setting the context.

This is useful as [because](https://docs.docker.com/engine/reference/commandline/build/#text-files)

> By default the docker build command will look for a Dockerfile at the root of the build context

So, trying to recap what we have read until now in the documentation:

1. We can copy or add files in the container from our machine only if they are in the “context”;
2. Using the `build` key in the `docker-compose.yaml` file with a path (`build: path/to/folder`):
  1. Uses the `Dockerfile` found in this path;
  2. Uses the content of this path as “context” (so we can access only files in it)
3. In our `docker-compose.yaml` file, we can:
  1. Specify a specific “context” to use to build the service/container;
  2. Specify a specific `Dockerfile` to use to build the service/container.

Interesting, isn’t it? Let’s put all those information at work to understand how to copy our Symfony files to the Apache container.

As usual, we will start small.

## Trying to copy only a single file `test.txt`

First, we need a file to copy: create an empty file called `test.txt` and put it in the root directory of the project: we will try to copy it in the container.

[![](https://io.serendipityhq.com/wp-content/uploads/sites/2/2018/08/docker-apache-copy-test-from-context-198x300.png)](https://io.serendipityhq.com/wp-content/uploads/sites/2/2018/08/docker-apache-copy-test-from-context.png)

Now, let’s edit our `docker-compose.yaml` file to specify the desired context and `Dockerfile`:

```
# docker-compose.yaml

version: '3.7'
services:
  # "php"
  ...

  # Configure the database
  ...

  # Configure Apache
  apache:
    # build: docker/build/apache <- Remove this node
    # And recreate it as follows
    build:
      context: .
      dockerfile: docker/build/apache/Dockerfile
    ports:
      - "8100:80"

```

As you can see, we removed the path to the `apache` folder and, instead, added two more keys:

- `context`, that sets the context to the current folder (that is the root folder, as the `docker-compose.yaml` file is in the root);
- `dockerfile`, that specifies which `Dockerfile` to use to build the container/service that runs Apache

This has two effects:

- Sets the Docker Context to our root folder as the `docker-compose.yaml` file is in the root (so we can now use – so, we can copy, too – any file in our root folder);
- Explicitly specifies the `Dockerfile` to use, without relying on the auto-discovery capabilities of Docker.

Before this modification, instead, the context was the folder `docker/build/apache` (and so we were able to copy only files and folders in that folder) and the `Dockerfile` was found thanks to the auto-discovery capabilities of Docker in finding `Dockerfile`s in the provided paths.

Now that we have our entire root folder in the Docker Context, edit the `Dockerfile` that builds Apache to make it able to copy the file `test.txt`:

```
# docker/build/apache/Dockerfile

FROM httpd:2.4

# Copy Symfony files: COPY [FROM_MACHINE] [TO_CONTAINER]
COPY test.txt copied-test.txt

```

We are using the [`COPY`](https://docs.docker.com/engine/reference/builder/#copy) instruction.

As the context is our root folder, we simply use the file name `test.txt` and copy it to the container as `copied-test.txt`.

Now run the command `docker-compose build`:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
php uses an image, skipping
mysql uses an image, skipping
Building apache
Step 1/2 : FROM httpd:2.4
 ---> fb2f3851a971
Step 2/2 : COPY test.txt copied-test.txt
 ---> 287d3e3b6a04

Successfully built 287d3e3b6a04
Successfully tagged app-aragog-www_apache:latest

```

As usual, `php` and `mysql` are skipped, BUT `apache` has now two steps, and the second one is exactly the `COPY` instruction.

Let’s see if the file was really copied.

First, recreate the containers running the `up` command:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose up -d
Recreating app-aragog-www_apache_1 ... done
Starting app-aragog-www_php_1 ... done

```

Then enter the container (use `docker ps` to find the ID of the `apache` container) and list the files in it:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker exec -it e4ed20c531ae bash
root@e4ed20c531ae:/usr/local/apache2# ls
bin build cgi-bin conf copied-test.txt error htdocs icons include logs modules

```

BINGO! Our `test.txt` file is there with the name `copied-test.txt`!

Now that we can use the `context` to copy files in the container from it, we are ready to further explore the copying of Symfony files to the container.

## Moving Symfony’s files into the container

Now that we know how to move files to the container, we are ready to move our entire app to the container.

We can use a simple instruction like this:

```
COPY . ./htdocs

```

This will move the entire context (that is our entire root folder) in the creating container.

WARNING: This is not the best way of doing this. For the moment we make simple things, so it is ok to copy the entire root folder.  
Later in this series of posts, we will learn how to refine the files copied in the container to speed up and optimize the process.

For the moment, let’s try this quick and dirty solution.

Edit the `docker/build/apache/Dockerfile`, adding the above instruction:

```
FROM httpd:2.4

# Copy Symfony files
# COPY test.txt copied-test.txt <- Remove this line (and also the text.txt file!)

COPY . ./htdocs

```

Rebuild the containers:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
php uses an image, skipping
mysql uses an image, skipping
Building apache
Step 1/2 : FROM httpd:2.4
 ---> fb2f3851a971
Step 2/2 : COPY . ./htdocs
 ---> 3f3528537fe9

Successfully built 3f3528537fe9
Successfully tagged app-aragog-www_apache:latest

```

And update the running ones:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose up -d
Recreating app-aragog-www_apache_1 ... 
Recreating app-aragog-www_apache_1 ... done
Starting app-aragog-www_php_1 ... done

```

Now enter the container and check if the files are in it:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b10afca67dd app-aragog-www_apache "httpd-foreground" 8 seconds ago Up 4 seconds 8100->80/tcp app-aragog-www_apache_1
c633398a7d65 mysql:5.7 "docker-entrypoint.s…" 15 hours ago Up 15 hours 3306/tcp app-aragog-www_mysql_1
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker exec -it 8b10afca67dd bash
root@8b10afca67dd:/usr/local/apache2# ls
Docker.md README.md bin build cgi-bin composer.json composer.lock conf config docker docker-compose.yaml docs error htdocs icons include logs modules public src symfony.lock var vendor

```

Are they there? Very good! We have just moved all the Symfony’s files in the container! ![🙂](https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png)

Let’s try to open our Symfony app: go to `http://127.0.0.1:8100`…

The message “It works!”? What’s happening?

Ok, try this: `http://127.0.0.1:8100/public`… A list of files? ? And there is our `index.php` listed ?

Click on it…

What are you seeing? What, the content of the `php` file, not interpreted but instead served as a simple text file? ?

What is damn happening? ?

## Making Apache interpret our PHP files

Technically, we have just created our stack and it works because we are able to make up and running our containers with Apache and MySQL (PHP currently doesn’t work, in fact you have never seen it when running `docker ps`!).

And it works because we can see the Symfony’s `index.php` content.

So, technically it is working, practically it isn’t as we are not able to serve the php files as interpreted webpages.

Why does this happen?

Let’s recap our current configuration:

```
version: '3.7'
services:
  # "php" was "language" in previous example
  php:
    image: php:7.2

  # Configure the database
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}

  # Configure Apache
  apache:
    build:
      context: .
      dockerfile: docker/build/apache/Dockerfile
    ports:
      - "8100:80"

```

As you can see we are building three containers: one for `php`, one for `mysql` and one for `apache`.

Leaving apart for the moment the container for `mysql`, let’s examine deeper what we are doing with `php` and `apache` containers.

We already know that the `php` container doesn’t work and never starts nor is built: maybe it is time to understand why!

To build `php` and `apache` we are using two images:

- PHP: `php:7.2`
- Apache: `httpd:2.4` (this is in the Dockerfile in `docker/build/apache`)

So, the question at this point is: what happens when we build these images?

The first thing we know is that the `php` container is skipped. When running `docker-compose build`, in fact, we can read a clear message:

```
php uses an image, skipping
```

The second thing we know is that the `php` container is never run: using `docker ps`, in fact, we have never seen it in the list:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d109c4b4c431 app-aragog-www_apache "httpd-foreground" 11 minutes ago Up 11 minutes 8100->80/tcp app-aragog-www_apache_1
844167c3154f mysql:5.7 "docker-entrypoint.s…" 12 minutes ago Up 12 minutes 3306/tcp app-aragog-www_mysql_1

```

So, in the end, we have this situation:

1. We have the Apache container running, but without PHP;
2. We have the PHP container NOT running, and we anyway don’t have PHP (but, as seen in the previous post about [how to configure the Apache web server on Docker](https://dev.to/aerendir/how-to-dockerize-a-symfony-application-introduction-and-the-web-server-3303-temp-slug-4506360), if we try to check the version of PHP, we get it, so it seems it is working: very confusing!).

Why?

Let’s see what these images do.

## Introducing Docker images (and containers)

A Docker image is nothing more than a Dockerfile with some instructions to build a container.

More [precisely](https://stackoverflow.com/a/23736802/1399706)

> An instance of an image is called a container. You have an image, which is a set of layers as you describe. If you start this image, you have a running container of this image. You can have many running containers of the same image.

You can read more about images and layers [here](https://docs.docker.com/v17.09/engine/userguide/storagedriver/imagesandcontainers/#images-and-layers).

The relvant part is this:

> A Docker image is built up from a series of layers. Each layer represents an instruction in the image’s Dockerfile. Each layer except the very last one is read-only.
> 
> …
> 
> Each layer is only a set of differences from the layer before it. The layers are stacked on top of each other. When you create a new container, you add a new writable layer on top of the underlying layers. This layer is often called the “container layer”. All changes made to the running container, such as writing new files, modifying existing files, and deleting files, are written to this thin writable container layer. The diagram below shows a container based on the Ubuntu 15.04 image.

And this is the representation of what you’ve read until now:

![](https://docs.docker.com/v17.09/engine/userguide/storagedriver/images/container-layers.jpg)

But from where these images come?

From the [Docker Hub](https://hub.docker.com/)!

So, to understand why our `httpd` image doesn’t interpret correctly PHP files and why our `php` image doesn’t run in a container, we can read the Dockerfiles of them.

And here they are:

- [httpd](https://hub.docker.com/_/httpd/)
- [php](https://hub.docker.com/_/php/)

Let’s start examining first the `httpd` page.

If your read it, there is a section called “How to use this image.” that states this:

> This image only contains Apache httpd with the defaults from upstream. There is no PHP installed, but it should not be hard to extend. On the other hand, if you just want PHP with Apache httpd see the [PHP image](https://registry.hub.docker.com/_/php/) and look at the `-apache` tags.

It is clear, now?

Our service doesn’t interpret PHP files because using the image `httpd` simply we don’t have it!

So, bascially, we are using the wrong image ?.

Let’s use the right one!

## Configuring an Apache web server with PHP (for real!)

So, we need to use the the `php` image: and this is what we were already using.

But reading the description from the `httpd` image, it is not sufficient to use the image `php:7.2` like we did: we need to use an image tagged with `-apache`!

So, go to [the `php` image page on Docker Hub](https://hub.docker.com/_/php/).

If you read the page, there is a section called “With Apache” that says:

> More commonly, you will probably want to run PHP in conjunction with Apache httpd. Conveniently, there’s a version of the PHP container that’s packaged with the Apache web server.

And the instructions say that we need to use the image `php:7.2-apache`: very very well ?

So, what happened was this:

1. We saw the version of PHP because effectively we were building a container with it, so Docker was able to find it and returned its version;
2. We were not able to serve an interpreted PHP file as we were using the Apache container that is not equipped with PHP.

So, what we need to do now is:

- Change the image we use for PHP in `docker-compose.yaml`;
- Use a `Dockerfile` to create the `php` service (to merge the current configuration of the Apache container in the PHP container);
- Copy all files from our machine to the new PHP image (instead of copying them in the Apache container);
- Remove the old service with the `httpd` image (as it will be substituted by the `php:7.2-apache` image);
- Remove the folder `docker/build/apache` as not needed anymore.

This is the resulting `docker-compose.yaml` file:

```
version: '3.7'
services:
  # "php" was "language" in previous example
  php:
    build:
      context: .
      dockerfile: docker/build/php/Dockerfile
    ports:
      - "8100:80"

  # Configure the database
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}

  # Configure Apache <- THIS NODE AS TO BE ENTIRELY REMOVED
  # apache:
  # build:
  # context: .
  # dockerfile: docker/build/apache/Dockerfile
  # ports:
  # - "8100:80"

```

Concretely, we have renamed `apache` to `php` and changed the path to the `Dockerfile`: nothing really complex.

Now the last thing remained is renaming the folder `docker/build/apache` in `docker/build/php`.

We also need to change the folder in which we move the files of our app as per [documentation](https://hub.docker.com/_/php/):

> `COPY . /var/www/html/`
> 
> Where `src/` is the directory containing all your PHP code.

So, in the file `docker/build/php/Dockerfile`, we need to change the `FROM` instruction to use the image `php:7.2-apache`:

```
# FROM httpd:2.4 <- Change this to use the PHP image
FROM php:7.2-apache

# Copy Symfony files
# COPY . ./htdocs # <- Remove this
COPY . /var/www/html/ # <- Keep attention: we have removed the "." (dot) at the beginning of the destination path

```

Last thing to do is building the image:

```
MacBook-Pro-di-Aerendir:app-aragog-www Aerendir$ docker-compose build
Building php
Step 1/2 : FROM php:7.2-apache
7.2-apache: Pulling from library/php
be8881be8156: Already exists
69a25f7e4930: Already exists
65632e89c5f4: Already exists
cd75fa32da8f: Already exists
15bc7736db11: Pull complete
b2c40cef4807: Pull complete
f3507e55e5eb: Pull complete
e6006cdfa16b: Pull complete
a3ed406e3c88: Pull complete
745f1366071d: Pull complete
bdfcada64ad8: Pull complete
86f2b695cc77: Pull complete
5f634a03970a: Pull complete
a329a7ebde19: Pull complete
fb3d2649f534: Pull complete
Digest: sha256:8188b38abe8f3354862845481452cd3b538bc0648e3c5cdef4ef9ee9365fe2d3
Status: Downloaded newer image for php:7.2-apache
 ---> 5e5a59788e34
Step 2/2 : COPY . /var/www/html/
 ---> a0cfd25e4828

Successfully built a0cfd25e4828
Successfully tagged app-aragog-www_php:latest
mysql uses an image, skipping

```

As we have removed a service, we need to run first `docker-compose down --remove-orphans` and then `docker-compose up -d`.

Once containers are created again, we can go to `http://127.0.0.1:8100/public` and we will see the most beautiful page in the world ?:

[![](https://io.serendipityhq.com/wp-content/uploads/sites/2/2018/08/docker-php-apache-symfony-welcome.png)](https://io.serendipityhq.com/wp-content/uploads/sites/2/2018/08/docker-php-apache-symfony-welcome.png)

We are now ready to start building our Symfony based app… or maybe we aren’t?

Mmm, no, we are not still ready ???

What do we need to do now? ?

## Conclusions and next steps

Well, also if we are able to copy the Symfony’s files in the web server container (that now is not `httpd` anymore, but, instead, `php:7.2-apache`) and we are able to make the web server serve interpreted PHP files, we still have some other things to do:

- Understand why we can see Symfony only pointing directly to the `public` folder (SPOILER: `DocumentRoot`?);
- Checking our web server meets all the Symfony’s requirements;
- Maybe, also refine our copying of files as it is currently very dirty and heavy.

But for the moment we have reached some great goals:

- We know how to create a real Apache web server on Docker;
- We know how to deal with images, to understand how to chose and use them (now, for example, you can use MongoDB if you like: go to search for it!);
- We know how to copy files in the containers and in doing this, we have understood what are `Dockerfile`s (one of the foundation of Docker!), what is the Docker Context, how it works and how we can manipulate it (almost: there is at least one other thing you need to know to master it… Next post!).

Those are a lot of things and we are closer to our goal of fully using Symfony on Docker!

The next post will teach you how to solve the problems I mentioned.

In the meantime, remember to “Make. Ideas. Happen.”.

I wish you flocking users!

L'articolo [Moving Symfony files in the Docker container](https://io.serendipityhq.com/experience/moving-symfony-files-in-the-docker-container/) proviene da [ÐΞV Experiences by Serendipity HQ](https://io.serendipityhq.com).