Containers are designed to be ephemeral, so data written inside them is lost when the container is removed. Docker solves this with volumes, which are persistent storage objects managed by Docker that exist independently of any single container's lifecycle. Volumes can be mounted into multiple containers at once, making it easy to share data between services or to keep state across restarts. You create a named volume with docker volume create volume_name and list existing volumes with docker volume ls.
A common alternative is the bind mount, which maps an arbitrary host directory to a path inside the container. Bind mounts are simple and useful during development because the host filesystem is exposed directly inside the container, but they tie the container to a specific host path and reduce portability. Named volumes are generally preferred for production because Docker handles where they are stored, they work the same way across hosts, and they integrate with the rest of the Docker ecosystem.
Networking follows a similar idea of providing isolation while enabling communication. Each container gets its own network stack, and you choose how containers connect to each other and to the outside world through network drivers such as bridge, host, overlay, or custom networks. The default bridge network lets containers on the same host communicate via IP addresses or automatically assigned names. For more structured setups, docker network create network_name creates a user-defined bridge, while specifying --driver overlay enables multi-host communication used by Swarm clusters.