CONTAINER IMAGES / PRACTICEFree access

Container images practice

Build two images, verify the application, inspect its configuration and clean up. Allow about 25 minutes.

Review the container images lesson

Prepare your workspace

You need a running Docker engine with Linux containers, internet access, curl and a free local port 8083. No Kubernetes cluster or registry account is needed. Commands run in your terminal, not in this page. Keep all five files below in a new directory and run the commands there. Use the reserved exercise names only for this lab; if they already exist, choose new names consistently.

Starter files (5)
go.mod
module example.com/academy-web

go 1.26.0
main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello from Proxy Academy")
    })
    server := &http.Server{
        Addr: ":8080",
        Handler: mux,
        ReadHeaderTimeout: 5 * time.Second,
    }
    log.Fatal(server.ListenAndServe())
}
Dockerfile
FROM golang:1.26-alpine
WORKDIR /src
COPY go.mod main.go ./
RUN CGO_ENABLED=0 go build -trimpath -o /server .
USER 65532:65532
EXPOSE 8080
ENTRYPOINT ["/server"]
.dockerignore
.git
.env
.env.*
*.pem
*.key
bin/
coverage/
Dockerfile.multistage
FROM golang:1.26-alpine AS build
WORKDIR /src
COPY go.mod main.go ./
RUN CGO_ENABLED=0 go build -trimpath -o /server .

FROM scratch AS runtime
COPY --from=build /server /server
USER 65532:65532
EXPOSE 8080
ENTRYPOINT ["/server"]
Build both variantsUse the starter files above in one empty directory. Predict which image will be smaller before building.
docker build -t academy-container-lab:single .
docker build -f Dockerfile.multistage -t academy-container-lab:multi .
docker image ls academy-container-lab
Expected observation
Both tags should exist. Compare their SIZE values; the multistage image should be smaller. Exact sizes vary.
Run and verify HTTPUse a free local port 8083. Run curl after startup; if it fails, inspect docker logs academy-container-lab before retrying.
docker run -d --name academy-container-lab -p 127.0.0.1:8083:8080 academy-container-lab:multi
curl --fail http://127.0.0.1:8083
Expected observation
Hello from Proxy Academy
Inspect the configured userInspect the image configuration without needing a shell inside the scratch container.
docker image inspect academy-container-lab:multi --format '{{.Config.User}}'
Expected observation
65532:65532
Create a second local referenceTag the same image and compare IDs. This step does not upload anything to a registry.
docker tag academy-container-lab:multi academy-container-lab:release
docker image inspect academy-container-lab:multi academy-container-lab:release --format '{{.Id}}'
Expected observation
Two identical image IDs. Their exact value depends on your build.
Clean up this exerciseStop and remove the named exercise container, then remove only its image tags. Do not use system-wide prune commands.
docker stop academy-container-lab
docker rm academy-container-lab
docker image rm academy-container-lab:single academy-container-lab:multi academy-container-lab:release
Expected observation
The exercise container and image references are removed. Build cache may remain.

Explain your result

Why can both images serve the same response while having different sizes? Why does the second tag keep the same image ID? What would be required to make this image available to a different machine?

Compare your explanation

Both contain the server executable; the single-stage image also retains the build tools. A tag adds a reference without rebuilding. Another machine needs access to the image, typically through a registry after a push, with credentials for private images and a compatible platform.

Docker: container run · Docker: multistage builds