docker runCreate and start a container from an image
docker run creates a new container from an image and starts it: pulling the image if needed, giving it its own filesystem, network and process tree, and running the image's command as PID 1. Its flags decide ports, env, volumes, limits and restart behaviour.
docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]You want to see what a container really is: a process with its own hostname and filesystem that ends when its command ends.
docker runCreate a new container and start it. It is docker create plus docker start (plus attach unless -d).
The docker CLI is only a client: it sends REST calls to dockerd over /var/run/docker.sock. POST /containers/create fails with 404 if the image is missing, so the CLI pulls it and retries. dockerd then prepares the container: a writable overlayfs layer on top of the read-only image layers, a veth pair plugged into the docker0 bridge, NAT rules for every -p, and the mounts. It hands the spec to containerd, which starts a containerd-shim and calls runc. runc clone()s a process into new pid, mount, net, uts, ipc and cgroup namespaces, puts it in a cgroup with your -m and --cpus limits, drops capabilities, applies seccomp and AppArmor, pivots into the rootfs and execs ENTRYPOINT + CMD as PID 1. runc exits; the container lives exactly as long as that PID 1 does.
The CLI only sends API calls; dockerd, containerd and runc build namespaces and cgroups around one process.
docker is just a client. It parses your flags into a JSON container spec and POSTs it to dockerd over the unix socket /var/run/docker.sock.
-d, --detachRun in the background and print the container ID-itInteractive with a TTY: keep STDIN open and allocate a terminal (for shells)--rmDelete the container automatically when it exits--name NAMEGive the container a fixed, unique name-p HOST:CONTAINERPublish a container port on the host (add an IP to limit the interface)-e KEY=VAL, --env-file FILESet environment variables inside the container-v SRC:DEST[:ro]Mount a named volume or host path into the container--network NETAttach to a network (bridge, host, none or a user-defined one)-m, --memory 512mHard memory limit; going over it gets PID 1 OOM-killed (exit 137)--cpus 1.5CPU quota: at most 1.5 CPUs' worth of time per period--restart POLICYno, on-failure[:N], always or unless-stopped-u, --user UID[:GID]Run the process as this user instead of the image's default--entrypoint CMDOverride the image's ENTRYPOINT (handy for debugging)--read-onlyMount the container's root filesystem read-onlydocker run -e DB_PASSWORD=s3cret shop-api:1.4.0The secret lands in shell history and in docker inspect output, readable by anyone in the docker group.
docker run --env-file /etc/shop/api.env shop-api:1.4.0 (file mode 600), or a real secret storedocker run -d -p 5432:5432 postgres:16 on a public VMPublished ports bypass ufw on Ubuntu (docker writes its own iptables rules), so the database is open to the internet.
docker run -d -p 127.0.0.1:5432:5432 postgres:16, or no -p at all and a shared user-defined networkdocker run nginx:1.27-alpine nginx -g 'daemon on;'If the main process forks into the background and exits, PID 1 is gone and the container stops immediately.
Keep the app in the foreground: nginx -g 'daemon off;' (the image default)Running docker run again after editing the app and expecting the changerun uses the image as it was built. Code changes need a rebuild and a new container (or a bind mount during development).
docker build -t shop-api:1.4.1 . && docker rm -f api && docker run ... shop-api:1.4.1Try these on a Linux machine of your own. Each task has a hint and a solution.
Run nginx:1.27-alpine detached, named web2, serving container port 80 on host port 8081 only on localhost, then check it answers with curl.
Start a throwaway interactive Ubuntu shell that is deleted when you exit. Inside, check the OS release and which processes exist, then exit.
A container shows 'Exited (137)'. Compute what signal killed it, then check your answer in the sandbox shell with kill -l.
Write a docker run for the API that limits it to 512 MB and 1 CPU, restarts it on failure at most 5 times, and loads env vars from /etc/shop/api.env. Then try it for real in the CrackOps AWS Lab as an ECS task definition.
docker runCreate and start a container from an image
docker run creates a new container from an image and starts it: pulling the image if needed, giving it its own filesystem, network and process tree, and running the image's command as PID 1. Its flags decide ports, env, volumes, limits and restart behaviour.
docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]You want to see what a container really is: a process with its own hostname and filesystem that ends when its command ends.
docker runCreate a new container and start it. It is docker create plus docker start (plus attach unless -d).
The docker CLI is only a client: it sends REST calls to dockerd over /var/run/docker.sock. POST /containers/create fails with 404 if the image is missing, so the CLI pulls it and retries. dockerd then prepares the container: a writable overlayfs layer on top of the read-only image layers, a veth pair plugged into the docker0 bridge, NAT rules for every -p, and the mounts. It hands the spec to containerd, which starts a containerd-shim and calls runc. runc clone()s a process into new pid, mount, net, uts, ipc and cgroup namespaces, puts it in a cgroup with your -m and --cpus limits, drops capabilities, applies seccomp and AppArmor, pivots into the rootfs and execs ENTRYPOINT + CMD as PID 1. runc exits; the container lives exactly as long as that PID 1 does.
The CLI only sends API calls; dockerd, containerd and runc build namespaces and cgroups around one process.
docker is just a client. It parses your flags into a JSON container spec and POSTs it to dockerd over the unix socket /var/run/docker.sock.
-d, --detachRun in the background and print the container ID-itInteractive with a TTY: keep STDIN open and allocate a terminal (for shells)--rmDelete the container automatically when it exits--name NAMEGive the container a fixed, unique name-p HOST:CONTAINERPublish a container port on the host (add an IP to limit the interface)-e KEY=VAL, --env-file FILESet environment variables inside the container-v SRC:DEST[:ro]Mount a named volume or host path into the container--network NETAttach to a network (bridge, host, none or a user-defined one)-m, --memory 512mHard memory limit; going over it gets PID 1 OOM-killed (exit 137)--cpus 1.5CPU quota: at most 1.5 CPUs' worth of time per period--restart POLICYno, on-failure[:N], always or unless-stopped-u, --user UID[:GID]Run the process as this user instead of the image's default--entrypoint CMDOverride the image's ENTRYPOINT (handy for debugging)--read-onlyMount the container's root filesystem read-onlydocker run -e DB_PASSWORD=s3cret shop-api:1.4.0The secret lands in shell history and in docker inspect output, readable by anyone in the docker group.
docker run --env-file /etc/shop/api.env shop-api:1.4.0 (file mode 600), or a real secret storedocker run -d -p 5432:5432 postgres:16 on a public VMPublished ports bypass ufw on Ubuntu (docker writes its own iptables rules), so the database is open to the internet.
docker run -d -p 127.0.0.1:5432:5432 postgres:16, or no -p at all and a shared user-defined networkdocker run nginx:1.27-alpine nginx -g 'daemon on;'If the main process forks into the background and exits, PID 1 is gone and the container stops immediately.
Keep the app in the foreground: nginx -g 'daemon off;' (the image default)Running docker run again after editing the app and expecting the changerun uses the image as it was built. Code changes need a rebuild and a new container (or a bind mount during development).
docker build -t shop-api:1.4.1 . && docker rm -f api && docker run ... shop-api:1.4.1Try these on a Linux machine of your own. Each task has a hint and a solution.
Run nginx:1.27-alpine detached, named web2, serving container port 80 on host port 8081 only on localhost, then check it answers with curl.
Start a throwaway interactive Ubuntu shell that is deleted when you exit. Inside, check the OS release and which processes exist, then exit.
A container shows 'Exited (137)'. Compute what signal killed it, then check your answer in the sandbox shell with kill -l.
Write a docker run for the API that limits it to 512 MB and 1 CPU, restarts it on failure at most 5 times, and loads env vars from /etc/shop/api.env. Then try it for real in the CrackOps AWS Lab as an ECS task definition.