DEV Community

Cover image for Day 7: Docker Volumes
Jonas Scholz
Jonas Scholz Subscriber

Posted on • Originally published at adventofdocker.com

12 3 3 3 3

Day 7: Docker Volumes

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)
}
Enter fullscreen mode Exit fullscreen mode

Our Dockerfile stays the same:

FROM golang
COPY . .
RUN go build -o main main.go
EXPOSE 8080
CMD ["./main"]
Enter fullscreen mode Exit fullscreen mode

Let's build and run it:

$ docker build -t hello-world-go .
$ docker run -p 8080:8080 hello-world-go
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now run the container with the volume mounted:

$ docker run -p 8080:8080 -v mydata:/data hello-world-go
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
    }
]
Enter fullscreen mode Exit fullscreen mode

Remove a volume:

$ docker volume rm mydata
Enter fullscreen mode Exit fullscreen mode

You can also use anonymous volumes by omitting the volume name:

$ docker run -v /data hello-world-go
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Image of Datadog

Measure and Advance Your DevSecOps Maturity

In this white paper, we lay out a DevSecOps maturity model based on our experience helping thousands of organizations advance their DevSecOps practices. Learn the key competencies and practices across four distinct levels of maturity.

Get The White Paper

Top comments (1)

Collapse
 
kawasthi2889 profile image
Kaustubh • Edited

Hey man! Nice tutorial. Just a suggestion, don't use relative path "data/visits.txt", instead use "/data/visits. I dont know why this is happening. my default workdir is /go but data directory is created in root (/), and yet the volume is not able to read it like it is inside that go directory.
I actually struggled for an hour on this. I made the path absolute and it worked.
To test this i made my WORKDIR / and my RUN go build -o main go/main.go when the paths are relative in main.go and it worked.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs