You cannot embed an entire directory as a single blob using //go:embed, but you can embed all files within a directory as a fs.FS interface by using a wildcard pattern like dir/*. This allows you to access the directory structure and individual files at runtime using the standard io/fs package.
Here is a practical example where we embed a static directory containing HTML and CSS files:
package main
import (
"embed"
"fmt"
"io/fs"
"net/http"
)
//go:embed static/*
var staticFS embed.FS
func main() {
// Serve the embedded directory directly
http.Handle("/static/", http.FileServer(http.FS(staticFS)))
// Or read a specific file
content, err := staticFS.ReadFile("static/index.html")
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Printf("File size: %d bytes\n", len(content))
fmt.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
If you need to iterate over the files or check if the directory exists, use fs.WalkDir on the embedded filesystem. Note that the path in ReadFile or WalkDir must match the pattern used in the directive (e.g., static/index.html, not just index.html):
func listFiles() {
fs.WalkDir(staticFS, "static", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
fmt.Println("Found file:", path)
}
return nil
})
}
Key constraints to remember: the wildcard * only matches files in the immediate directory, not subdirectories recursively. If you need to include subdirectories, use static/** (Go 1.20+ supports this, though static/* is often sufficient for flat structures). Also, the embedded files are compiled into the binary, so changes to the source files require a rebuild of the Go application to take effect. This approach is ideal for serving static assets, configuration files, or templates without needing external file dependencies.