What is Docker Compose and Volumes? What problem do they solve?
I am still learning , so if I am wrong anywhere or if there is something important that I should know, please let me know.
In a real application, we usually have multiple containers like a frontend, backend, database, Redis, etc.
Managing all these containers manually is very difficult. Also, Docker images are immutable, so whenever we change our code, we don't want to rebuild the image and recreate the container every single time during development.
This is where docker-compose.yml
It lets us define everything in one file. We can define which images to build, which ports to expose, environment variables, volumes, networks, and much more.
Then we can start the entire application with just one command:
\- docker compose up
and stop everything with:
\- docker compose down
One thing that confused me a lot was volumes.
Let's say I have a folder named backend on my system, and Docker builds an image where all the code is copied into /app.
Then in docker-compose.yml I write:
volumes:
- ./backend:/app
From what I understood, this bind mount hides (overrides) the /app folder inside the container and mounts my local ./backend folder there instead.
So now the container reads the files directly from my local machine instead of the files that were copied into the image. This is great because whenever I edit my code, I don't have to rebuild the image.
But this creates another problem.
Since the entire backend folder is mounted, it also mounts my local node\_modules.
That is not what we want because my local machine could be Windows or macOS, while the container is running Linux. The dependencies inside node\_modules are installed for the operating system they were built on, so using the host's node\_modules inside a Linux container can cause issues.
This is where a named volume comes in.
We add another volume:
volumes:
- ./backend:/app
- backend_node_modules:/app/node_modules
Here, backend\_node\_modules is just the name of a Docker managed volume.
If this named volume doesn't already exist, Docker creates it. Since the volume is initially empty, Docker copies the existing /app/node\_modules from the image into the named volume.
Now this named volume is mounted at /app/node\_modules. Since we already mounted ./backend:/app, the container was using the node\_modules from my local Windows/macOS machine. This new mount hides those host node\_modules and replaces them with the node\_modules stored in the backend\_node\_modules named volume, which contains the Linux dependencies copied from the image.
So the result is:
My application code comes directly from my local machine, so changes are reflected instantly.
node\_modules comes from the Linux container, so I don't have operating system compatibility issues.