Skip to content

Instantly share code, notes, and snippets.

@msrose
Last active June 27, 2023 10:33
Show Gist options
  • Save msrose/1ba70b547608d5c0153587f051c20b51 to your computer and use it in GitHub Desktop.
Save msrose/1ba70b547608d5c0153587f051c20b51 to your computer and use it in GitHub Desktop.
Docker cheatsheet

Docker cheatsheet

This gist might help you answer the following questions:

  • How do I run a command inside a docker container?
  • Why isn't my docker container running?
  • Why is my docker container stopping right after I start it?

Most of the time when using docker, I want to get a shell inside a running container, either to see what's installed inside an image, or debug failing commands, or something else similar. I don't think this is the primary use case for the docker CLI, because it always takes a few different commands with flags that I always forget; before long, I find myself confused all over again as to how containers work and how to manipulate them with the CLI.

Here's the easiest CLI-only way I've found to pull an image, create a container from it, and run a terminal in that container. I've done my best to explain every command and flag so I'll never have to wonder again why my container seemed to stop immediately, or stop suddenly, or vanish off the face of the Earth.

Pull your image

For example, from DockerHub:

$ docker pull node:18

You can see the images you have pulled with:

$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
node         18        e390ceb99781   10 days ago   991MB

Run a command in a new container

This is what the docker run command does. So here's the quickest way to get a shell into a container to see what the image has:

$ docker run -it node:18 /bin/bash

-i is interactive mode, which pipes everything correctly between the container and your own terminal stdout/stdin so you can actually see what the commands are doing. -t actually "creates a terminal" in the docker container so you can run commands. The last two args are the image used, and the initial command to run in the container.

The above container will stop if you type exit. You can see that it existed by doing docker ps -a:

CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS                       PORTS     NAMES
2354850cc1e5   node:18   "docker-entrypoint.s…"   4 minutes ago   Exited (129) 3 minutes ago             admiring_noether

Creating a container

$ docker create -it --name foo node:18 /bin/bash

This "creates" a container but doesn't start it. We can use --name to give our container and friendly identifier, so we don't have to reference it by its hash ID every time. Since we haven't started it yet, this container won't show up with docker ps, but will with docker ps -a:

CONTAINER ID                                                       IMAGE     COMMAND                            CREATED              STATUS    PORTS     NAMES
39dbbc0002133298ef3ccecf80daa934a707ace5d73995fd5406fbf6cbdc23de   node:18   "docker-entrypoint.sh /bin/bash"   About a minute ago   Created             foo

(Side note: you can add --no-trunc to view any columns that docker might truncate by default, e.g. docker ps -a --no-trunc lets you see the full COMMAND.)

It will also show up with docker container ls -a.

Starting a created container

Now if we run docker start foo, we can see with docker ps that our container is now running bash:

CONTAINER ID                                                       IMAGE     COMMAND                            CREATED         STATUS          PORTS     NAMES
39dbbc0002133298ef3ccecf80daa934a707ace5d73995fd5406fbf6cbdc23de   node:18   "docker-entrypoint.sh /bin/bash"   5 minutes ago   Up 59 seconds             foo

Note that this only happens because we specified -it when creating the container. Without these options, the container would just exit immediately upon being started, because we didn't tell docker to forward IO streams or create a terminal.

Attaching to a running container

Simply run:

$ docker attach foo

And that drops us into a shell in the container we started:

$ docker attach foo
root@39dbbc000213:/# node -v
v18.12.1

Now if we type exit at this prompt, the container will stop entirely (it won't show up in docker ps output). To detach from the container without stopping it, we can type ^P^Q (Ctrl-P, Ctrl-Q):

root@39dbbc000213:/# <^P^Q> read escape sequence
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
39dbbc000213   node:18   "docker-entrypoint.s…"   7 minutes ago   Up 3 minutes             foo

I need to confirm exactly what the behaviour is, but it seems like the container only remains running if there's at least one process executing inside of it. In this case we ran /bin/bash as our starting command, and attaching directly to the container and typing exit in the bash terminal stops the bash process, which was the only one running in the container. Therefore, the container stops.

Running a command in a container that's already running

We use docker exec:

$ docker exec -it foo /bin/bash

This drops us into a bash terminal in the already-running container (once again, the -it is needed), which means we now have two bash processes running in the container: the initial one from when the container was started, and the one from our most recent exec. We can confirm this:

root@39dbbc000213:/# node -v
v18.12.1
root@39dbbc000213:/# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4164  3220 pts/0    Ss+  22:59   0:00 /bin/bash
root         9  0.1  0.0   4164  3416 pts/1    Ss   23:08   0:00 /bin/bash
root        18  0.0  0.0   6760  2836 pts/1    R+   23:08   0:00 ps aux

So now if we type exit, the container stays alive because that only kills the second bash process (from our exec); the first bash process is still running:

root@39dbbc000213:/# exit
exit
$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
39dbbc000213   node:18   "docker-entrypoint.s…"   16 minutes ago   Up 12 minutes             foo

And now if we use attach again, we can see there's only one bash process in the container:

$ docker attach foo
root@39dbbc000213:/# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4164  3336 pts/0    Ss   22:59   0:00 /bin/bash
root        19  0.0  0.0   6760  2820 pts/0    R+   23:16   0:00 ps aux

And now, as expected, typing exit stops the container entirely:

root@39dbbc000213:/# exit
exit
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                     PORTS     NAMES
39dbbc000213   node:18   "docker-entrypoint.s…"   21 minutes ago   Exited (0) 5 seconds ago             foo

Viewing logs from a stopped container

This use case is for when the container exits immediately or soon after starting, and you want to try and figure out why. First determine the ID of the container that stopped with:

$ docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                     PORTS     NAMES
39dbbc000213   node:18   "docker-entrypoint.s…"   21 minutes ago   Exited (0) 5 seconds ago             foo

The use the docker logs command to view logs from the container before it stopped:

docker logs 39dbbc000213

Docker compose

WIP section

See https://docs.docker.com/compose/gettingstarted/

Example docker-compose.yml file with a simple Flask + Redis application:

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    volumes:
      - .:/code
    environment:
      FLASK_DEBUG: "true"
  redis:
    image: "redis:alpine"

Start the services in docker-compose.yml:

docker compose up

Start the services in detached mode:

docker compose up -d

View running services:

docker compose ps

Run a command in a running service container:

docker compose run <service_name> <command>

e.g. to see environment variables in the container for a service "web"

docker compose run web env

Stop all service containers:

docker compose stop

Remove all service containers:

docker compose down

Remove all service containers including any volumes:

docker compose down --volumes

Rebuild image after changing a Dockerfile:

docker compose up --build

Networking in compose: https://docs.docker.com/compose/networking/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment