How to Create a Docker Container

How to Create a Docker Container

Reading time1 min
#Docker#Containers#DevOps#CommandLine

How to Create a Docker Container

A container is created from an image. The image is a read-only template (filesystem plus default command), and the container adds a writable layer and its own settings: name, ports, environment, mounts, network. There are two commands that create containers:

  • docker run creates a container and starts it. This is what you use most of the time.
  • docker create creates a container without starting it. docker start starts it later.

The usual way: docker run

docker run -d --name web -p 8080:80 nginx:stable

This one line pulls nginx:stable if it is not in the local image cache, creates a container named web, publishes container port 80 on host port 8080 and starts it in the background. Check it:

docker ps
curl -I http://localhost:8080

The general form is:

docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]

Options go before the image name. Everything after the image name is passed to the container as its command, so docker run nginx:stable -p 8080:80 does not publish a port, it tries to run -p inside the container.

Options you set at creation time

docker run -d \
  --name my-api \
  -p 127.0.0.1:3000:3000 \
  -e NODE_ENV=production \
  --env-file ./my-api.env \
  -v my-api-data:/app/data \
  --memory 512m --cpus 1 \
  --restart unless-stopped \
  my-api:1.0
  • --name: a fixed name instead of a random one like quirky_hopper. Names must be unique per host.
  • -p host:container: publish a port. Prefixing 127.0.0.1: keeps it off public interfaces. Without an IP, Docker publishes on all interfaces and its firewall rules bypass ufw.
  • -e and --env-file: environment variables. Keep secrets in the env file, not in shell history.
  • -v name:/path: a named volume for data that must survive the container. -v "$(pwd)"/config:/app/config (or --mount type=bind,...) bind mounts a host directory instead.
  • --memory, --cpus: resource limits. Without them a container can use all of the host's memory and CPU.
  • --restart unless-stopped: start it again after a crash or a reboot, unless you stopped it yourself.

For an interactive container that is gone when you exit:

docker run -it --rm python:3.13-slim python

-it gives you a terminal, --rm deletes the container on exit.

Create now, start later: docker create

docker create takes the same options as docker run:

docker create --name web -p 8080:80 nginx:stable
docker ps -a --filter name=web     # STATUS shows "Created"
docker start web

Why create without starting? To prepare containers in a script and start them in a specific order, to copy a config file in with docker cp before the first start, or to extract files from an image without running it:

docker create --name tmp my-api:1.0
docker cp tmp:/app/config/default.yaml ./default.yaml
docker rm tmp

For an interactive container created ahead of time, pass -it at creation and attach when starting:

docker create -it --name sandbox ubuntu:24.04 bash
docker start -ai sandbox

docker container create and docker container run are the same commands under their newer, grouped names.

Containers from your own image

A container for your own application needs an image first. A minimal Dockerfile for a Node.js app:

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]

Add a .dockerignore with at least node_modules, .git and .env so they are not copied into the image. Then build and create the container:

docker build -t my-api:1.0 .
docker run -d --name my-api -p 3000:3000 my-api:1.0
docker logs -f my-api

Tag images with a version rather than relying on latest. With a version tag you know exactly what a container runs and can roll back by starting the previous tag.

Changing a container after creation

Most settings are fixed when the container is created. Ports, environment variables, mounts and the image cannot be changed on an existing container. The way to change them is to remove the container and create a new one:

docker rm -f my-api
docker run -d --name my-api -p 3000:3000 -e LOG_LEVEL=debug my-api:1.0

That is why data belongs in volumes: the container is disposable, the volume is not.

A few things can be changed in place. docker update adjusts resource limits and the restart policy, and docker network connect attaches a running container to another network:

docker update --memory 1g --memory-swap 1g --restart always my-api
docker network connect my-net my-api

When a container needs more than a handful of options, put them in a compose.yaml and let docker compose up -d create and recreate containers for you. Compose notices changed settings and recreates only the affected containers.

Inspect what you created

docker ps -a                     # all containers and their state
docker inspect my-api            # full configuration as JSON
docker logs my-api               # output of the main process

If a new container shows Exited right away, docker logs usually says why: a missing environment variable, a wrong command or a port already in use.

Quick reference

docker run -d --name NAME -p HOST:CONTAINER IMAGE:TAG   # create and start
docker create --name NAME IMAGE:TAG                     # create only
docker start NAME                                       # start a created container
docker run -it --rm IMAGE:TAG sh                        # throwaway interactive container
docker build -t my-api:1.0 .                            # image from a Dockerfile
docker rm -f NAME                                       # remove, then recreate to change settings
docker update --memory 1g --restart always NAME         # the few settings you can change in place