Skip to content

Day 7 - Docker Volumes

Published:Β atΒ 12:00 AM

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:

  1. Visit localhost:8080 a few times - you’ll see the list grow with each visit
  2. Stop the container with docker stop <container_id>
  3. 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:

Until then, happy coding! πŸ³πŸŽ„

Jonas


Previous Post
Day 6 - Docker Image Layers
Next Post
Day 8 - Quiz
Sponsor logo

Sliplane

Deploy your Docker Apps straight from your Github repository in less than 2 minutes with sliplane.io

Learn More β†’