Docker Cheatsheet

Chances are that Docker is new to you. While this isn’t going to be an exhaustive article on what Docker is and what we use it for, here are some helpful hints for getting around how our projects are constructed.

High Level Concepts

There are a few things to know about Docker to do development work inside them. Unless you’re building containers for serving applications in production, this is probably enough:

  • Images - Images are software packaged for your consumption and are the basis for Containers. They should be purpose-built for handling one particular portion of your application, whether it be to serve the application, run a database or cache, or perform some background tasks.
  • Container - Containers are the Image turned into an operational construct. While the Image is a static asset, a Container is the packaged software in an operational state. You will connect to one or more Containers to view and execute your application. We generally orchestrate their creation through docker compose. They are built, executed, and destroyed at will and should be considered ephemeral.
  • Volumes - Volumes are persistent storage that we utilize so that application data can persist outside of the lifecycle of a Container. Data for a database, image and document uploads through the application are typically stored in a volume. They can be removed if you no longer need the data contained therein in order to completely reset an environment.

Basic Commands

Here are a few basic Docker commands that may help you navigate your new project. Note that we generally use Docker Compose to run our projects, and its usage is explained later.

docker ps - Shows all currently running containers

% docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                    NAMES
e276a11d8098   nginx:latest   "/docker-entrypoint.…"   38 seconds ago   Up 37 seconds   0.0.0.0:8080->80/tcp     wordpress-template-web-1
9ad9b547f1d5   php:7-fpm      "docker-php-entrypoi…"   38 seconds ago   Up 37 seconds   9000/tcp, 9003/tcp       wordpress-template-php-1
1e61b569bb53   mariadb        "docker-entrypoint.s…"   39 seconds ago   Up 38 seconds   0.0.0.0:3306->3306/tcp   wordpress-template-database-

docker image ls - Shows all downloaded Docker images

% docker image ls
REPOSITORY                         TAG             IMAGE ID       CREATED         SIZE
php                                7-fpm           72f2a68f9ca2   2 months ago    423MB
nginx                              latest          63eb316dc556   3 months ago    134MB
mariadb                            latest          7eda4c38372f   4 months ago    394MB

docker volume ls - Shows all persistent volumes living outside of the containers.

% docker volume ls
DRIVER    VOLUME NAME
local     wordpress-template_database

At any time you can use docker [subcommand] --help to get help doing things within Docker.

Docker Compose

We try to use Docker Compose to help spin up our development environments, especially since it makes for a nice, self-contained, consistent experience for everyone. It’s also super easy to implode and leave almost nothing behind when it’s done.

Many of our projects come with a docker-compose.yml file, or coordinate with another project which does. Look for one when you’re checking out a new project. At this time, this is mostly our PHP-based projects and a few older Ruby on Rails projects.

Commands

docker compose up - Spins up a full infrastructure stack, as defined in docker-compose.yml for running an application. You can add the command line option -d if you don’t want to watch the logs. We do not recommend this in a development environment. Just open another terminal window.

docker compose down or docker compose kill - Either nicely brings a stack down or violently brings the stack down, depending on your preference. If you wish to reuse the containers and volumes again, the former is preferred over the latter.

docker compose build - If your stack depends on a custom built image (and a few of ours do), you will want to first start with this command to build the images that the containers will use later. Normally a README will direct you to do this if necessary.

Executing Commands within a Container

Sometimes we need to execute one-off tasks or troubleshoot an application and therefore need to be inside the application’s running context (the Container) in order to do it. There are two ways to execute commands under a Docker context and understanding the difference is key:

docker compose run --rm [container service name] [command] - Using run will instantiate a new Container using the image defined by container service name and run your command. This is good for when you need to run a rake task or composer install as the output of those commands are persisted outside of the Container in question to either a database or to a Volume. --rm saves you an extra step by removing the newly instantiated Container for you instead of leaving it out there and cluttering your environment.

docker compose exec -it [container service name] [command] - If you really need to be inside of the current Container running your application for some reason, exec will connect you to the current Container. This is useful if you need to look at an internal application or application server (e.g. Apache or Nginx) log that doesn’t appear in the terminal when you started Docker Compose.

Starting up a Project New

While we recommend checking out each project’s README file first, lacking that, you can use

docker compose up

to start up most new projects. This should roll out an infrastructure, usually in multiple containers, which will get the project rolling.

Sometimes you may need to perform some side tasks like running Composer. This is common in our WordPress stacks. In those cases, look for a composer.json file and run

docker compose run --rm php composer install

This will instantiate a second php Container, run composer install within it, and install all of the PHP dependencies you need. In these cases, run is normally appropriate because Composer will put the installed files in the application’s current working directory which is likely shared to your host OS (macOS) so that you can work with them in your code editor/IDE of choice.

If all else fails…

Assuming the project was stood up with docker compose, you can execute

docker compose down --volumes

This command is extremely destructive and will remove not only the containers, but any persistent volumes instantiated as part of Docker Compose. This should be considered ‘last resort’, or used in a situation where you know you won’t be working on an application for a long time and will likely need to set it up from scratch the next time.

Updated: