# Learn Docker from Scratch: First Container in 6 Steps

> Learn Docker from scratch hands-on: build your first image, run a container, publish a port, read logs and keep data with a volume without guessing at errors.

URL: https://www.matthewswong.com/en/blog/belajar-docker-dari-nol-panduan-pemula/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2026-10-17
- Updated: 2026-10-17
- Category: DevOps

> **Key takeaway:** Learning Docker from zero takes six steps: write a Dockerfile, run docker build to create an image, run docker run with the -p flag to start a container and publish a port, inspect it with docker ps, docker logs and docker exec, then keep data in a volume so it survives when the container is removed.

You installed Docker, typed docker run, and the terminal printed one long line of random characters. You opened the browser and localhost does not answer. You ran the command again and now an error says the container name is already in use. Nearly everyone learning Docker from zero stalls here, not because Docker is hard, but because nobody explained what each command actually does.

This guide builds one small Node.js app and takes it from a Dockerfile to a container whose data survives deletion. I checked every command against the official Docker documentation, and the Dockerfile pattern is the same one this site uses itself. The output shown is only what the code below is certain to print; IDs and timestamps will differ on your machine.

Prefer the animated version? Our free lesson explains layers and the build cache visually:

Docker for beginners: image, container and Dockerfile

. This article is the hands-on side: you type, run and see the result yourself.

## What you need

Not much. Every step below works on Linux, macOS and Windows as long as Docker is running.

- Docker installed and running (Docker Desktop on macOS or Windows, Docker Engine on Linux).
- A terminal and any text editor. You do not need Node.js on your own machine, because Node.js lives inside the image.
- About 30 minutes, and an empty folder named belajar-docker.

Check that Docker is alive first. If the command above fails, fix that before going on; the steps that follow cannot work without a running Docker daemon.

## Step 1: Understand image and container through three commands

An image is a read-only package holding your application and everything it needs. A container is an image that is currently running. One image can become many containers, the same way one installer file can be launched many times. The easiest way to remember it: the command you type tells you which one you are dealing with.

| You want to | Command | Result |
| --- | --- | --- |
| Create an image from a Dockerfile | docker build | An image named belajar-docker:v1 |
| Run an image | docker run | A running container |
| See what is running | docker ps | A list of containers, not images |

If you get lost in the steps ahead, come back to this table. The Dockerfile and the image live on the build side; containers and ports live on the run side.

## Step 2: Write a tiny app and a Dockerfile

Create two files in the belajar-docker folder. The app counts how many times it was visited and keeps the number in /data/count.txt. We use that counter in Step 6 to see why container data can vanish.

Read the Dockerfile top to bottom: start from the node:22-alpine base image, move to the /app folder, copy server.js, note that the app uses port 3000, then set the command that runs when the container starts. The reference stresses that EXPOSE only documents a port and does not publish it; publishing is the job of docker run in Step 4.

This site's own Dockerfile uses the same pattern: WORKDIR /app, port 3000 inside the container, and CMD in JSON array form with double quotes. The difference is that this site's Dockerfile has several stages so the final image stays small. That is a more advanced topic, covered in the best practices article linked at the end.

## Step 3: Build your first image

Run docker build from inside the belajar-docker folder. The dot at the end of the command is not decoration: it is the build context, the folder sent to Docker. Forgetting the dot is one of the first errors beginners hit.

While it builds, you will see one progress step for each Dockerfile instruction. The first time, Docker downloads the node:22-alpine image, so it takes longer. Run the same command again and Docker reuses its cache for the unchanged steps, which is why the second build is much faster.

## Step 4: Run the container and publish its port

docker run turns the image into a container. The three flags below are the ones you will use most. -d runs the container in the background and prints its ID, --name gives you a name that is easy to type, and -p connects a port on your machine to a port inside the container.

The order is always host port first, then container port. Without an IP address, Docker publishes the port on all interfaces, meaning it can be reached from outside your machine. That is why this site on its VPS uses the 127.0.0.1 form in front of the port, so only nginx on the same machine can reach it.

> If your app listens on 127.0.0.1 inside the container, the port you publish will never reach it, because 127.0.0.1 there means the container itself. Listen on 0.0.0.0 inside, then restrict outside access with -p 127.0.0.1:8080:3000. This site's Dockerfile also sets HOSTNAME to 0.0.0.0 for the same reason.

## Step 5: Look inside with ps, logs and exec

These three commands are your everyday debugging tools. docker ps shows running containers, docker logs shows what the app printed, and docker exec runs a new command inside a live container. The -it flags on exec give you an interactive terminal; the docker exec documentation uses sh as its example, and sh is available in Alpine images.

Two habits matter. First, if a container is missing from docker ps, add -a before concluding it vanished; it may simply have stopped. Second, docker exec only works while the container's main process is running, so for a crashed container, read docker logs instead.

## Step 6: Keep data with a volume

Everything the app writes inside a container lands in that container's own writable layer. Remove the container and the data goes with it. Try it yourself: the sequence below removes the container and starts it again, and the counter resets to 1.

The fix is a volume. The Docker documentation says a volume's contents exist outside the lifecycle of a given container, so the data stays even when the container is removed. The form -v volume-name:/path mounts a named volume at a folder inside the container; Docker creates the volume automatically if it does not exist yet.

This is exactly the pattern this site uses in production: its compose file mounts a named volume called next-image-cache so the optimised image cache is not lost every time the container is replaced on deploy. Note also that docker rm does not delete a named volume; you have to run docker volume rm yourself.

## Common errors and how to fix them

These six symptoms eat most of a beginner's time. Match the message in your terminal to the first column.

| Symptom | Cause | Fix |
| --- | --- | --- |
| Message bind failed: port is already allocated | Host port 8080 is already used by another container, including an old one you never removed. | Check docker ps -a, remove the old one with docker rm -f name, or use another host port such as -p 8081:3000. |
| Message Conflict: the container name is already in use | An old container, even a stopped one, still holds the name belajar-app until it is removed. | docker rm -f belajar-app, then run docker run again. |
| curl refuses the connection or it drops | Port mapping swapped (-p 3000:8080), or the app listens on 127.0.0.1 inside the container. | Write -p host-port:container-port, and make the app listen on 0.0.0.0. |
| The container shows Exited right away in docker ps -a | The main process finished or crashed. A container lives only as long as its main process. | Read docker logs name and fix the error; also check the CMD in the Dockerfile. |
| Cannot connect to the Docker daemon | Docker Desktop or the Docker service is not running. | Open Docker Desktop, or on Linux start the Docker service, then try docker --version and docker ps again. |
| Data or the counter resets after the container is removed | The data was written to the container's writable layer, not to a volume. | Add -v volume-name:/path to docker run, as in Step 6. |

The general rule: when a container misbehaves, run docker ps -a and then docker logs name before changing anything. Nearly every answer is in those two commands.

## Checklist before moving on

I can explain that docker build creates an image and docker run creates a container.

I know the order of -p is host port first, then container port.

I can use docker ps -a, docker logs and docker exec -it to look inside a container.

I understand data without a volume is lost when the container is removed.

I cleaned up with docker rm -f belajar-app and docker volume rm belajar-data.

## Go deeper

You now have a first container. To understand why the order of Dockerfile instructions decides build speed, open the free lesson

. After that, read

to tidy the image before it goes to a server, and

once you are tired of rebuilding the image every time you change one line of code.

One rule to carry with you: an image is what you build, a container is what you run, and anything that matters must live in a volume, not inside the container. From here, Docker Compose is the natural next step.

## Sources and references

- Docker docs: docker container run
- Docker docs: Publishing and exposing ports
- Docker docs: Volumes
- Docker docs: Dockerfile reference
- Docker docs: docker container exec

## Source links

- https://docs.docker.com/reference/cli/docker/container/run/
- https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/
- https://docs.docker.com/engine/storage/volumes/
- https://docs.docker.com/reference/dockerfile/
- https://docs.docker.com/reference/cli/docker/container/exec/

## FAQ

### How long does it take to learn Docker from zero?

You can run your first container in about 30 minutes by following the six steps in this guide. Understanding layers, caching and Compose takes longer, and comes from building small projects. Start with one tiny app rather than a large one.

### What is the difference between a Docker image and a container?

An image is a read-only package built by docker build from a Dockerfile. A container is a running instance of an image, created by docker run. One image can start many containers.

### Why does Docker say the port is already allocated?

Another container is already using that host port, and an old stopped container can still hold it until you remove it. Run docker ps -a to find it and docker rm -f to remove it, or publish on a different host port such as 8081.

### Does my data disappear when I delete a Docker container?

Yes, if it was written to the container's own writable layer. Data in a volume survives, because a volume's contents live outside the lifecycle of any single container. Mount one with -v volume-name:/path when you run the container.

### Why does a container exit immediately after I start it?

A container only lives as long as its main process. If that process finishes or crashes, the container shows Exited in docker ps -a. Read docker logs with the container name to see why, and check the CMD in your Dockerfile.

---

- HTML version: https://www.matthewswong.com/en/blog/belajar-docker-dari-nol-panduan-pemula/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/belajar-docker-dari-nol-panduan-pemula/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
