writing / docker

How to Create and Run a Docker Container

Learn how to create a Docker container step by step: write a Dockerfile, build an image, and run and manage containers with the exact commands you need.

Atharva Uday UndeAtharva Uday UndeJanuary 15, 202412 min read
Dockerhow to create a docker containercontainer creationDocker image buildingcontainerization guideDocker best practicesDocker for beginnerscontainer deploymentDocker commandsrun docker container

Creating a Docker container comes down to three steps: write a Dockerfile, build it into an image, and run that image as a container. This guide walks through each step with real commands you can copy, plus the container management commands you'll actually use day to day.

If you've never touched Docker before, or you've run a docker run command from a tutorial without really understanding what it did, this is the guide that fills in the gaps.

What You Need Before You Start

  • Docker installed on your machine (Docker Desktop for Mac/Windows, or Docker Engine on Linux)
  • A terminal
  • A small application to containerize (this guide uses a minimal Node.js app, but the same steps apply to Python, Go, Java, or anything else)

Confirm Docker is installed and running:

docker --version
docker ps

If docker ps returns an empty table instead of an error, you're ready.

Step 1: Write a Dockerfile

A Dockerfile is a text file with no extension, named exactly Dockerfile, placed at the root of your project. It's a list of instructions Docker follows, in order, to build an image.

Here's a complete Dockerfile for a Node.js app:

FROM node:24-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]

What each instruction does

FROM node:24-alpine — Sets the base image. Everything in this image (Node.js, npm, a minimal Linux filesystem) is now the foundation your container is built on. Alpine is used here because it keeps the final image small — see the base images guide linked at the end for why that choice matters.

WORKDIR /app — Creates /app inside the container and sets it as the working directory. Every instruction after this runs relative to /app.

COPY package*.json ./ — Copies package.json and package-lock.json into the container, before the rest of the code. This ordering matters — see the caching note below.

RUN npm install — Installs dependencies. This runs once, at build time, not every time the container starts.

COPY . . — Copies everything else in your project into /app.

EXPOSE 3000 — Documents that the app listens on port 3000. This is metadata for humans and tooling; it doesn't actually publish the port (that happens in Step 3).

CMD ["node", "index.js"] — The command that runs when a container starts from this image.

Why copy package*.json before the rest of the code? Docker caches each instruction as a layer. If only your application code changes but package.json doesn't, Docker reuses the cached npm install layer instead of reinstalling every dependency on every build. Skip this ordering and every code change triggers a full reinstall.

Step 2: Build the Image

From the directory containing your Dockerfile, run:

docker build -t my-node-app .
  • -t my-node-app — Tags the image with a name so you can reference it later instead of a long hash
  • . — The build context; tells Docker to look for the Dockerfile in the current directory

Docker pulls the base image (if it's not already cached locally) and executes each instruction in order. You'll see one line of output per instruction as it builds.

Confirm the image exists:

docker images

You should see my-node-app in the list, along with its size and image ID.

Step 3: Create and Run the Container

An image is a template; a container is a running instance of that template. This is the step that actually creates a Docker container from the image you just built:

docker run -d -p 3000:3000 --name my-running-app my-node-app
  • -d — Detached mode; runs in the background instead of tying up your terminal
  • -p 3000:3000 — Maps port 3000 on your machine to port 3000 inside the container (host:container) — this is what actually makes the app reachable
  • --name my-running-app — Gives the container a name you can reference instead of its auto-generated ID

Visit localhost:3000 in your browser. That's a container, created and running from an image you built yourself.

Running without detached mode

If you want to see logs stream live in your terminal instead, drop -d:

docker run -p 3000:3000 --name my-running-app my-node-app

Press Ctrl+C to stop it.

Step 4: Manage the Container

Once a container exists, here's the command set you'll use constantly:

# List running containers
docker ps

# List all containers, including stopped ones
docker ps -a

# View logs
docker logs my-running-app

# Follow logs live
docker logs -f my-running-app

# Stop a running container
docker stop my-running-app

# Start a stopped container
docker start my-running-app

# Restart
docker restart my-running-app

# Open a shell inside a running container
docker exec -it my-running-app sh

# Remove a stopped container
docker rm my-running-app

# Remove a running container (force)
docker rm -f my-running-app

Common mistake: running docker rm on a container that's still running fails with a "container is running" error. Either docker stop it first, or use docker rm -f to force it.

Rebuilding After a Code Change

Editing your source code doesn't update a running container — images are immutable snapshots. After a change, you rebuild and replace:

docker build -t my-node-app .
docker stop my-running-app
docker rm my-running-app
docker run -d -p 3000:3000 --name my-running-app my-node-app

During development, most people skip this cycle entirely and either use docker compose up --build or mount the source directory as a volume with -v $(pwd):/app so changes reflect without a rebuild. That's a topic for another guide — for now, know that the rebuild-stop-rm-run cycle above is what's actually happening under the hood.

Troubleshooting Common Errors

docker: command not found — Docker isn't installed, or Docker Desktop isn't running.

Cannot connect to the Docker daemon — Docker Desktop (or the Docker service on Linux) isn't running. Start it and retry.

port is already allocated — Something else is already using that host port. Either stop whatever's using it, or map to a different host port: -p 3001:3000.

No such container — You're referencing a container name or ID that doesn't exist, or was already removed. Check docker ps -a for the actual name.

Container exits immediately after docker run — Check docker logs <container-name>. This is almost always the application crashing on startup, not a Docker problem.

Why Use Docker at All?

  • Consistency — the same environment in development, staging, and production
  • Isolation — each container runs independently, without polluting your host machine or conflicting with other containers
  • Portability — runs identically on any machine with Docker installed
  • Efficiency — far lighter than a full virtual machine, since containers share the host kernel

Next Steps

You now know how to create, build, run, and manage a Docker container from scratch. From here, the natural next steps in this series are:


Tags: Docker · how to create a docker container · container creation · Docker image building · containerization guide · Docker best practices · Docker for beginners · container deployment · Docker commands · run docker container