Moving from Vagrant to Docker in an easy way.

Docker - An open platform for distributed applications for developers and sysadmins.





Wow it has been a long time since I did a blogpost. However I had this one coming as I got asked about how I used Docker in my current workflow. I used to be – and still am – a big Vagrant fan, however Vagrant is not very suited for development. It is great for managing and provisioning vboxes. But a vbox is sluggish and a resource hog. Vboxes weren’t meant for development, they were meant to run a complete OS on top of yours in a contained fashion.
If you need to run your software on something other than linux, then I still hearty recommend Vagrant for managing those boxes.
If however your website is running on a linux system, then you can leverage the power of Docker.

This blogpost will show you how to quickly setup a Debian environment running Apache, PHP and a database (Postgresql).

Docker Debian Images

head over to and download and extract the repo.
Alternatively for those who work with git you can

git clone

Once you have the package extracted or cloned just step right into the directory and follow the on how to build the images we need for this post.

Using the Docker images

You should now be the owner of 3 images.

nickbelhomme/postgres latest 3d09510dab44 2 hours ago 271.3 MB
nickbelhomme/apache latest f6eb10d32f1c 2 hours ago 223.6 MB
debian wheezy 29853cd4f422 42 hours ago 85.19 MB

You can ignore the hashes, these are random at build time.

So how do we use it?

The first thing we need is 1 additional project or projects if you are a busy bee. In this blog post I have included one demo project.

issue the following command:

cd application
docker build -t nickbelhomme/application .

you now end up with 4 images including a

nickbelhomme/application latest 7d4f26227028 33 minutes ago 223.6 MB

If you take a look at the Dockerfile you just issued a build on, you notice we use our previous nickbelhomme/apache image
as a start.
The only thing we apply on top of this image is an extra vhost setting and we enable it.

The Magic of Apache + PHP

Now that you can spawn as much images for each project you have, it is time to at least use one.

docker run --name test1 -P -d nickbelhomme/application /usr/sbin/apachectl -D FOREGROUND

This will start a Docker container with the ports exposed and made public, in this case port 80.
Exposing a port makes it available for inter-container communication and making it public allows your host system to contact the container processes within. We have exposed port 80 in our main apache Dockerfile.

The -d tells the container to run daemonized, which just starts the process – in our case “/usr/sbin/apachectl -D FOREGROUND”
and puts it in the background. Docker -d itself needs to have foreground processes (not deamonized), hence us starting apache in the foreground.

You should just get back a hash. This means your docker container is running. Issue a

docker ps

to see on which port you can access your website.


f718f791dd5e nickbelhomme/application:latest "/usr/sbin/apachectl 2 seconds ago Up 2 seconds>80/tcp


this tells us the container its apache port is available through our host on 49159.

Before heading to the browser and issuing a we need to add it to our /etc/hosts file.

to get the proper ip address to link to use a ifconfig and you will see the
Docker0 adapter with its IP address. Use that instead of

On Mac or Windows you are using Boot2Docker, so you will need to use the IP of the virtualbox adapter.

Now head over to the browser and you will see something such as:

Not Found

The requested URL / was not found on this server.

Apache/2.2.22 (Debian) Server at Port 49159

Success!!! you reached Debian server.

But wait this is not what we wanted. Where is my frigging site???!!!!

Patience is the mother of all things.
Let’s issue ourselves a new container this time mounted with our site:

docker run --name test2 -d -v /home/nick/vhosts/Docker/application/app:/var/www/vhosts/ -P nickbelhomme/application /usr/sbin/apachectl -D FOREGROUND

With the -v option we mount a local directory inside the container. You must provide absolute paths. The syntax is [localDir:containerDir]. In our application.conf we have set the document root directory to /var/www/vhosts/ so we mount it one level up.

Issuing a docker ps again will show us on which port to connect this time:

You will see:

SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host "localhost" (::1) and accepting TCP/IP connections on port 5432? could not connect to server: Connection refused Is the server running on host "localhost" ( and accepting TCP/IP connections on port 5432?

This is normal as we haven’t spawned our database container yet.

Lets do that now!

The magic of the Database Postgresql

docker run -d -P --name postgres nickbelhomme/postgres

As you saw we didn’t have to specify a foregound process with this docker run. That is because we have specified the default command to run when starting the container in our postgres Dockerfile.

once this is up and running we can use it to connect it to our application container

docker run --name application -d --link postgres:postgres -v /home/nick/vhosts/Docker/application/app:/var/www/vhosts/ -P nickbelhomme/application /usr/sbin/apachectl -D FOREGROUND


We connect 2 containers with the –link param. [containerName:alias]
Now you see why we use the –name param when doing a docker run. More use cases you’ll see later.

Going to the browser again on the new port we will see:

SQLSTATE[08006] [7] FATAL: password authentication failed for user "nick" FATAL: password authentication failed for user "nick"


Which is rather awesome because that means you made a connection. We didn’t setup the user nor the database yet but that we can arrange soon.

Let me first explain why the connection worked out of the box.
Docker uses exposed ports for inter communication it also uses ip addresses between containers. These settings are made available through the –link [containerName:alias] system. The linked container now has access to ALIAS ENVS such as:


and you can use them in your application. You can find such envs by issuing a

docker run --rm --link postgres:postgres -P nickbelhomme/application env

here you are not running a container in deamonized mode but just issue an env command against the container and when finished you
throw the container away (–rm)

We however want to go inside a postgres container so we can create a user and database.


docker run --rm -t -i --link postgres:postgres nickbelhomme/postgres /bin/bash


This opens an interactive (-i) / pseudo-tty (-t) bash in container throw away mode

Inside we can issue postgresql specific commands

createdb nickdb -h $POSTGRES_PORT_5432_TCP_ADDR -p $POSTGRES_PORT_5432_TCP_PORT -U docker


psql -h $POSTGRES_PORT_5432_TCP_ADDR -p $POSTGRES_PORT_5432_TCP_PORT -d docker -U docker --password


ALTER database nickdb owner to nick;

Again here you can see the env variables being used. You can also type them yourself, but as long as you use the postgres alias the environment variables naming will remain the same.

The postgresql password for the user docker is docker. We have set this up in our postgres Dockerfile.

You can leave this terminal by leaving psql by typing


and then the terminal by



After you have created the DB and granted the user access it is time to visit the application again.
go to your browser tab of the container named application and refresh.

“no couples table” is displayed on the screen.

Lets populate it.
head over to[application PORT]/install.php
and then go back to[application PORT]

you should see:

male: nick woman: chanie
male: pixel woman: cookie

Stop working

You do not need to remove the images nor the containers.

To stop working we simply stop the running containers to safe resources.

docker stop postgres
docker stop application

or simply

docker stop postgres application

Start working on the project again

docker start postgres
docker start application

or simply

docker start postgres application

Environment vars

each time you stop and start a container you will get new values for POSTGRES_PORT_5432_* so
if you hardcoded the ip instead of using the getenv technique you have to use the trick I showed you on how to get the env variables.


Removing Images and Containers

When you do not need a project or container anymore you can remove containers by name or hash

docker rm postgres
docker rm application

Or the image

docker rmi nickbelhomme/application
docker rmi nickbelhomme/apache
docker rmi nickbelhomme/debian
docker rmi nickbelhomme/postgresql

when you remove all the postgres containers you also remove all database data. To see where the data is stored issue a docker inspect postgres and see the volumes part.


Creating a clean slate


remove all containers and all images

docker rm $(docker ps -aq)
docker rmi $(docker images -q)


Windows or OSX

Mac and windows use boot2docker.

As stated above in the post, you need to use instead of the docker0 adapter the ip used for the virtualbox machine which is So put that in your hosts file.

Ever since 8 September 2014 guest box additions are included in the Boot2Docker machine so you can find your users folder in /Users for mac and /c/Users for windows.
If you want to mount different folders as well you can use the virtualbox gui to mount additional folders. (vbox/settings/shared folders). Once you have mounted the folders you need you can use the vbox path (docker run -v /c/Users:somePath) to mount it to the container.


Happy Dockering ,

Nick Belhomme


  1. Toni says:

    Hey Nick,

    Thanks for taking the time explaining this. It helped grasping the basics.

    However I do have a practical question. Assume you do this use case with a composer project. And you build/run your docker to view the website (configured as in you example with a shared volume). What if you update the composer file, and you need to do a composer update? How do you do that on that running docker? Do you have to rebuild it each time? Or is there another approach?

    Thanks in advance


  2. Hi Toni,

    If you have setup the project as I outlined your project dir is mounted as a volume in your container. So from your host machine you just issue a “composer update [package]” statement and your project now runs with that update. Exactly the same as if you would manually edit a project file such as the index.php. There is no need to stop and start a container after any code change.

Leave a Reply

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