This is a crosspost from adventofdocker.com
Yesterday we learned that containers are ephemeral - any changes made inside a container are lost when it stops. But what if you need to persist data? That's where volumes come in!
Let's modify our HTTP server to save some data:
package main
import (
"fmt"
"net/http"
"os"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Record new visit
timestamp := time.Now().Format(time.RFC3339) + "\n"
os.MkdirAll("data", 0755)
f, _ := os.OpenFile("data/visits.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
f.WriteString(timestamp)
f.Close()
// Read and display all visits
data, err := os.ReadFile("data/visits.txt")
if err != nil {
fmt.Fprintf(w, "Error reading visits!")
return
}
fmt.Fprintf(w, "New visit recorded at %s\n\nAll visits:\n%s", timestamp, string(data))
})
fmt.Println("Listening on port 8080")
http.ListenAndServe(":8080", nil)
}
Our Dockerfile stays the same:
FROM golang
COPY . .
RUN go build -o main main.go
EXPOSE 8080
CMD ["./main"]
Let's build and run it:
$ docker build -t hello-world-go .
$ docker run -p 8080:8080 hello-world-go
When you visit localhost:8080, each visit gets recorded and displayed. However, if you stop and restart the container, all your visits are gone! This is because containers are ephemeral - any changes made inside them are lost when they stop.
Solving with Docker Volumes
We can fix this by using volumes. First, create a named volume:
$ docker volume create mydata
Now run the container with the volume mounted:
$ docker run -p 8080:8080 -v mydata:/data hello-world-go
The -v mydata:/data
flag mounts our volume named mydata
to the /data
directory inside the container. Try it out:
- Visit localhost:8080 a few times - you'll see the list grow with each visit
- Stop the container with
docker stop <container_id>
- Start a new container with the same volume:
$ docker run -p 8080:8080 -v mydata:/data hello-world-go
Your visits are still there! The data persists even when containers are stopped or removed.
Managing Volumes
List all volumes:
$ docker volume ls
DRIVER VOLUME NAME
local mydata
Inspect a volume:
$ docker volume inspect mydata
[
{
"CreatedAt": "2024-12-07T...",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/mydata/_data",
"Name": "mydata",
"Options": {},
"Scope": "local"
}
]
Remove a volume:
$ docker volume rm mydata
You can also use anonymous volumes by omitting the volume name:
$ docker run -v /data hello-world-go
Docker will create a random volume name for you. These are harder to manage but useful for temporary data.
Bind Mounts
Instead of using a Docker-managed volume, you can mount a directory from your host directly:
$ docker run -v $(pwd)/data:/data hello-world-go
This mounts the ./data
directory from your current location into the container. Great for development, I recommend reading more about bind mounts here.
That's it for today! Tomorrow we'll have our first quiz to test what you've learned this week. Make sure you're comfortable with:
- Basic Docker commands
- Building images
- Running containers
- Port mapping
- Layers
- Volumes
Until then, happy coding! 🐳🎄
Jonas
Top comments (0)