Docker: Run TCP Services locally

Most of the dynamic web applications depend on databases like mysql, postgres, mongodb etc. However, setting up an environment (or a server) to work with those services can be a pain. And if you add periodic upgrades, version management and OS-specific package manager issues, it could really be a nightmare!

One solution to this problem is Containerization. This might not be the best solution for high-volume production apps, but for most of the apps (and at least for dev environment), it can be a life saver.

In this post, we will be using docker as our Containerization Engine to set up a Postgres Service.

For instructions on installing docker, check out this page

Pull Postgres Image

In order to run postgres inside a Docker container, you will need to use an image. There are two ways to get that image:

  • Pull a trusted, maintained and official postgres image from DockerHub.
  • Pull a base OS image (say CentOS) and run a series of commands on it to work with postgres.

In order pull an image from DockerHub, Here, I will be pulling the official postgres image from DockerHub:

Linux Users must use sudo with docker

$ docker pull postgres:9.3.23

This will take a couple minutes to pull postgres image with tag, 9.3.23 from DockerHub.

It’s important to use tags here, because docker pulls the latest image if a tag isn’t specified. You don’t want to use an updated postgres without knowledge.

Running the Postgres Container

Now that we have the postgres image, we can create and run a container using that image. For a container which is expected to be “long running”, we typically use docker run with -d (detached) option. This allows us to create a stopped container which can be ran in the background using docker start.

Moreover, we want to be able to access the container’s port from host machine which is why we also provide -p option, which allows us to do port forwarding from a docker container to the host. Since postgres runs on the port 5432, we want to expose that port from the container and forward it to host’s 5432 port.

We’re also providing a name to the container which will allow us to perform operations on that container without having to know it’s hashed id.

Optionally, we can also provide an environment variable to the container POSTGRES_PASSWORD which sets the password to the user postgres.

So, to launch the postgres container, use the following command:

$ docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=******* -d postgres:9.3.23

We can now start this container by typing:

$ docker start postgres

To test if postgres container is running as expected we can launch psql:

$ docker exec -it postgres psql -Upostgres -W
Password for user postgres: ********
psql (9.3.23)
Type "help" for help.

postgres=#

Now, we have a working containerized postgres service.

Data Persistence

When running a postgres container, we most likely want to persist the data. In order to do so across multiple container instances, we can use Volumes.

First, let’s stop and remove the current container:

$ docker stop postgres && docker rm postgres

Now, we can create a container which uses a location on the host machine to use as a volume which will be mounted in the postgres container to a location where it stores data (by default: /var/lib/postgresql/data). This can be done by using the -v option:

$ docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=******* -d -v /tmp/postgresdata:/var/lib/postgresql/data postgres:9.3.23

This command will create a directory named postgresdata in /tmp folder.

Now, we have a container which persists data to a host volume, that can be used to create another container.

Start postgres on System Reboot

Docker provides a neat way of restarting a container with different policies. Check out this page for more details.

I like keeping my postgres container running unless I have explicitly stopped it. So, I use restart unless-stopped policy.

So, our final command should look something like this:

$ docker run --name postgres --restart unless-stopped -p 5432:5432 -e POSTGRES_PASSWORD=******* -d -v /tmp/postgresdata:/var/lib/postgresql/data postgres:9.3.23

Conclusion

This kind of a set up can be used for mysql, phpadmin and many other services. I use docker for all my tools that use tcp. It makes managing these tools a lot easier as all I have to maintain is docker, and everything else is handled by the maintainers of the image that I’m using. It also works on multiple operating systems (as long as they support docker).

Don’t forget to alias the final run command :)