Docker is becoming increasingly popular among software developers as a container platform. Containers package software in a format that can run isolated on a host operating system. Bundled with only essential libraries and settings, Docker renders lightweight, efficient, self-contained systems that run identically wherever deployed. In this cheat sheet, we'll take a use-case oriented approach: building the image, starting it, and in the end, stopping it and cleaning up after ourselves.
Creating your container
To follow along with the commands, I've borrowed an application with its Dockerfile from my repository representing an ideal Node.js Docker workflow.
# Dockerfile.short
FROM node:12-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD npm start
The application is an Express web server that responds to incoming HTTP requests.
const express = require('express');
const port = process.env.PORT || 3000;
const app = express();
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, () => console.log(`App listening on port ${port}!`));
We'll build and run this file with the upcoming commands.
docker build -f <docker filename> -t <image name> <build context>
Pulls the base image, builds the image inside Dockerfile.short
, and names it as express
. We can use this name when running the image. The name can contain a tag (express:1
): by default, it gets the latest
tag. Specifying the name of the Dockerfile
is only necessary when it defers from Dockerfile
. The dot at the end tells that the build context is the current directory.
docker build -f Dockerfile.short -t express .
Sending build context to Docker daemon 180.7kB
Step 1/7 : FROM node:12-alpine
12-alpine: Pulling from library/node
c9b1b535fdd9: Pull complete
750cdd924064: Pull complete
2078ab7cf9df: Pull complete
02f523899354: Pull complete
Digest: sha256:e280e51eaa6e626e4df58a5c1f141e453807c30596179330992c55a0bf4114ca
Status: Downloaded newer image for node:12-alpine
---> afd897e3184b
Step 2/7 : WORKDIR /app
---> Running in c8f379e36c32
Removing intermediate container c8f379e36c32
---> a11ced1bd480
Step 3/7 : COPY package*.json ./
---> e811deacf584
Step 4/7 : RUN npm ci --only=production
---> Running in 401bdc088d44
added 50 packages in 1.395s
Removing intermediate container 401bdc088d44
---> 644c8661eff7
Step 5/7 : COPY . .
---> 270057bb701a
Step 6/7 : EXPOSE 3000
---> Running in cd9d70daad58
Removing intermediate container cd9d70daad58
---> 4c6eb54071d1
Step 7/7 : CMD npm start
---> Running in fc2a7b3e7e11
Removing intermediate container fc2a7b3e7e11
---> d85b87f880e3
Successfully built d85b87f880e3
Successfully tagged express:latest
docker images
Shows all the runnable images on the host machine. We'll see our application with the name express
.
REPOSITORY TAG IMAGE ID CREATED SIZE
express latest d85b87f880e3 3 minutes ago 87.6MB
node 12-alpine afd897e3184b 3 days ago 85.2MB
docker run -p <host port>:<container port> <image name>
Runs the built image and connects the containers port with the host machines port (-p 3000:3000
). The result is the application is available on http://localhost:3000
. If we don't want to block the terminal, we can run the image in detached mode -d
. If we're going to have live-reload on file changes, for example, with Nodemon, we can attach the local folder as a volume -v $(pwd):/app
.
docker run -p 3000:3000 -v $(pwd):/app express
docker ps -a
Lists the running containers. After docker run
, we'll see the application here.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec40c8347a43 express "docker-entrypoint.s…" 6 seconds ago Up 5 seconds 0.0.0.0:3000->3000/tcp hardcore_vaughan
The -a
flag shows all the containers, not just the running ones.
docker logs -f -t <image id>
Shows the standard output with timestamps from the running container.
docker logs -f -t ec40c8347a43
2020-02-10T18:22:07.672710300Z
2020-02-10T18:22:07.672788300Z > node-docker-workflow@1.0.0 start /app
2020-02-10T18:22:07.672825700Z > node src/index.js
2020-02-10T18:22:07.672850600Z
2020-02-10T18:22:07.917358100Z App listening on port 3000!
The -f
flag tells the command to listen for new logs. If all the records are too much, you can restrict it to only some lines --tail 100
.
Publishing the image
Our application is running fine on our local computer, but we can't deploy it anywhere. For deployment, we have to publish it to a repository. For demonstration purposes, we'll use the default DockerHub repository.
docker login
Logs in to the repository, by default to Dockerhub. We need a Dockerhub account before this to work. You can do the registration here.
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: blacksonic
Password:
Login Succeeded
docker tag <source image name>:<target tag name> <target image name>:<target tag name>
Names the image with a tag that we will use when uploading the image.
docker tag express:latest blacksonic/express:latest
We have to prefix the image name before uploading it to Dockerhub (blacksonic is my username).
docker push <image name>:<tag name>
Uploads the image to the repository. You can see the uploaded image now on the site https://hub.docker.com/r/blacksonic/express.
docker push blacksonic/express:latest
The push refers to repository [docker.io/blacksonic/express]
d93ac2ab321f: Pushed
5216338b40a7: Pushed
latest: digest: sha256:8b418f814535e24906fcb412f8e564ced393e4976586d710bbff60b5fdb2d11c size: 1993
Cleaning it up
We've started and pushed our container successfully. It's time to clean up after ourselves.
docker stop <container id>
Stops the running container. The container won't be deleted.
docker stop ec40c8347a43
docker rm <container id>
Deletes the container, you won't be able to start it again.
docker rm ec40c8347a43
docker rmi <image id>
Deletes the image: you'll have to build it again to start it as a container.
docker rmi d85b87f880e3
docker stop $(docker ps -a -q)
Stops all processes from running containers. After this, docker ps -a
will return empty results.
docker rm $(docker ps -a -q)
Removes all the containers.
docker rmi $(docker images -q)
Removes all the images. After this, docker images
will return empty results.
docker image prune --all
Removes all unused images. Unused means no running containers exist based on the image.
Summary
Encountering containerization can be hard for the first time, but can be simplified by knowing the right commands which can tell what's happening. With an example configuration, you can try them, and you realize that developing with containers can be as easy as doing it on your local machine.
Top comments (2)
Awesome, thanks. It's is nice overview for the most used commands.
Just want to mention that some commands are actually the old syntax. Yes, they are shorter but I prefer the newer syntax, since they make more sense to me and are more descriptive. Examples:
Maybe you want to mention that 🆒
Thanks for the feedback, obviously more descriptive than the old ones 👍