Last Updated: 2023-02-18

High level comparison

docker crictl podman
Build images Yes No Yes
Run / Inspect Yes Yes Yes
Talk to daemon Yes (dockerd) Yes (CRI-compatible runtimes) NO (daemonless)
Privilege root or rootless rootless
Run Pods No Yes Yes
Used by k8s kind


  • All compatible with OCI specs.
  • podman build uses code sourced from the Buildah project to build container images. Both podman and Buildah are from RedHat.
  • Other tools:
    • buildx is a Docker CLI plugin for extended build capabilities with BuildKit.
    • runc: the low-level runtime, usually we do not need to work with directly.


Inpect Images / Containers:

docker crictl
List images docker images crictl images
List containers docker ps crictl ps
Attach to a running container docker attach crictl attach
Run a command in a container docker exec crictl exec
Show system-wide info docker info crictl info
Show container, image info docker inspect crictl inspect
Get logs of a container docker logs crictl logs
Show resource usage stats docker stats crictl stats
Show rutime version docker version crictl version

Image / Container Lifecycle:

docker crictl
Pull an image docker pull crictl pull
Create a container docker create crictl create
Run commands docker run crictl run
Start containers docker start crictl start
Stop containers docker stop crictl stop
Update configs docker update crictl update
Remove containers docker rm crictl rm
Remove images docker rmi crictl rmi
Kill containers docker kill crictl stop

Pod Lifecycle:

List pods crictl pods
Run a pod crictl runp
Remove pods crictl rmp
Stop pods crictl stopp
Show status of the pods crictl inspectp
Forward local port to a pod crictl port-forward

Docker examples

# Run Docker Engine
$ sudo dockerd

# List all containers
$ docker ps -a

# List running containers including CPU/memory size
$ docker ps -s

# Specify format
$ docker ps -a --format "{{.ID}},{{.Names}},{{.Status}},{{.Image}},{{.Ports}}"

# Go inside the container; `-i` for `interactive, `t` for `terminal.
$ docker exec -it <container> bash

# Start a Ubuntu 20.04
$ docker run -it --entrypoint "/bin/bash" ubuntu:20.04

# Export a container’s filesystem as a tar archive
$ docker export --output="latest.tar" <container>

# port forwarding
$ docker run -p 8080:8080 myserver


$ docker login <registry_address>

# pull
$ docker pull <registry_address>/library/ubuntu:22.04

# push
$ docker tag ubuntu:22.04 <registry_address>/demo/ubuntu:22.04
$ docker push <registry_address>/demo/ubuntu:22.04


credHelpers can be set in ~/.docker/config.json

"credHelpers": {
    "gcr.io": "gcloud"

It means any image pull from gcr.io will use the binary docker-credential-gcloud to get the username and secret. (The binary = docker-credential- + suffix.)

$ echo gcr.io | docker-credential-gcloud get

To login:

$ docker login -u admin -p ${REGISTRY_PASSWORD}


Choose image format

# Build OCI images (default for podman)
$ podman build --format=oci

# Build Docker images instead
$ podman build --format=docker

# Similarly in `docker buildx`
$ docker buildx build --output type=oci
$ docker buildx build --output type=docker


Both CMD and ENTRYPOINT instructions define what command gets executed when running a container. There are few rules that describe their co-operation.

  • Dockerfile should specify at least one of CMD or ENTRYPOINT commands.
  • ENTRYPOINT should be defined when using the container as an executable.
  • CMD should be used as a way of defining default arguments for an ENTRYPOINT command or for executing an ad-hoc command in a container.
  • CMD will be overridden when running the container with alternative arguments.

Example: Containerize a Node app

Use this Dockerfile template:

# node
FROM node:12-slim

# the path inside the container
WORKDIR /usr/src/app

# copy the package.json and package-lock.json, and install dependencies
COPY package*.json ./
RUN npm install

# copy all the source code
COPY . .

# port

# run the command inside the container
CMD [ "node", "app.js" ]

And add a .dockerignore file:



# List networks
$ docker network ls


  • null: the container does not have external network interfaces, only a local loopback interface.
  • host: the container is attached to the host's network, the configs inside the container matches the configs outside the container.
  • bridge: containers connected to the same bridge network can communicate; containers on different bridge networks cannot communicate directly with each other.

To get more details:

$ docker network inspect bridge


Most likely you do not need to use runc directly, but you can get more info about the running containers:

$ runc list

$ ls /run/runc

$ cat /run/runc/<name>/state.json


# containerd view logs
$ journalctl -u containerd

ctr vs crictl:

  • ctr: containerd CLI, not related to k8s.
  • crictl: CRI Compatible container runtime command line interface, related to k8s.

crictl image = ctr -n=k8s.io images ls

kind load uses "ctr", "--namespace=k8s.io", "images", "import", "--digests", "--snapshotter="+snapshotter, "-"


The new docker sbom CLI command displays the SBOM (Software Bill Of Materials) of any Docker image. This feature outputs the SBOM in a table or can be exported into SPDX and CycloneDX formats.

Check docker info: docker info

Check if dockerd is running: sudo systemctl status docker