It works on your computer
You write an application. You run it. The process starts, it listens, and the page loads. On this computer, that is the end of the story.
The same application, three results
You send it to a teammate. It breaks.
You deploy it to a server. It breaks differently. Their error is not your error. A missing library on one machine. A runtime one version behind on the other. A tool you use every day that the server has never had installed.
Nothing in the application changed between those three.
The environment
The application does not run alone. Before the process can start, the machine already has to have what that process expects: a specific operating system, specific library versions, specific configuration files, specific environment variables, and specific system tools.
Your computer has that set. You built it up over months, and you stopped seeing it. Your teammate’s computer has a different set. The server has another one again.
One unit
Matching every computer to yours does not hold. Someone upgrades a library. A server is rebuilt and a package you never wrote down is gone. The next failure looks like a bug in the application. It is a difference in the machine around it.
So you stop sending the application by itself. You take the application and every dependency it needs, and you put them together as one unit. You send that unit. The machine that runs it uses the environment inside the unit, the one you packaged, the one that already worked.
The copy you save, and the copy you start
You will want to keep that unit, and you will want to start it more than once. Those are not the same thing, and mixing them up is how people lose work.
The unit you built and stored is not running. It sits there, the same every time you come back to it. That saved copy is an image.
Starting it creates a container. One image can start three containers. They begin from the same image. After that they are separate. What one writes, the others do not have. Remove a container and its writes go with it. The image stays stored, ready to start again.
What builds it and what starts it
Something on the machine has to build that image and start that container. That tool is Docker.
You install Docker. Build produces the image. Run starts a container from the image. The other computer does not need your libraries and your tools installed by hand. It needs Docker, and it needs the image.
The data is gone
A database is running in a container. It has rows. You remove the container to put up a new version, and you start a fresh one from the same image. The database is empty.
The rows were inside the container. The new container came from the image, and the image never had those rows.
The files have to live somewhere the container does not own. A volume is that place. Docker keeps it outside the container and mounts it in, so the process reads and writes a normal directory. You remove the container, start another from the same image, and mount the same volume. The rows are still there.
Both are running
The application container is up. The database container is up. The application still cannot connect. Nothing is crashed. They are on the same computer, and there is still no path between them.
A network is that path. You put both containers on it. The application reaches the database by name. Before the network, that name goes nowhere. After it, the name lands on the database container.
Someone else has to build it again
The image exists on your machine. A new server does not have it. If the only way to produce it is a list of steps you remember, the next person will miss one, and you are back to a different environment.
You write the steps in a file. Docker reads the file and builds the image. That file is a Dockerfile. Each step adds a layer. Change the file, build again, and the next container gets the new image. A container that is already running keeps the one it started from until you replace it.
When you need the application and the database together, one more file names both containers, the volume, and the network, and starts them as a group.
Where that leaves you
The application broke because the environment around it was different. A container runs the application inside the environment you packaged. An image is that package, saved. A container is one running copy. A volume keeps the files when the container is removed. A network lets containers reach each other by name. A Dockerfile is how the image gets built, the same way, the next time.