Go by Example

Embed Directive

The //go:embed directive bundles static files and directories into the compiled binary using embed.FS, enabling single-binary deployments.

The //go:embed directive lets you bundle static files and directories into the compiled binary at build time. The embed package provides the embed.FS type for accessing embedded content at runtime.

Import the embed package and place a //go:embed comment immediately before a variable of type string, []byte, or embed.FS. The compiler reads the file at build time and bakes it into the binary.

package main
 
import (
    _ "embed"
    "fmt"
)
 
//go:embed hello.txt
var content string
 
func main() {
    fmt.Print(content)
}

embed.FS can hold multiple files or entire directory trees. Use fs.ReadFile or the methods on embed.FS to read them. The embedded paths must be relative and cannot use ...

package main
 
import (
    "embed"
    "fmt"
)
 
//go:embed templates
var templateFS embed.FS
 
func main() {
    data, err := templateFS.ReadFile("templates/index.html")
    if err != nil {
        panic(err)
    }
    fmt.Printf("template size: %d bytes\n", len(data))
 
    entries, _ := templateFS.ReadDir("templates")
    for _, e := range entries {
        fmt.Println(e.Name())
    }
}

Pass an embed.FS to http.FileServer via http.FS to serve embedded static assets. The server reads from the in-memory filesystem, not the disk.

package main
 
import (
    "embed"
    "net/http"
)
 
//go:embed static
var staticFiles embed.FS
 
func main() {
    http.Handle("/", http.FileServer(http.FS(staticFiles)))
    http.ListenAndServe(":8080", nil)
}

In production

go:embed is the idiomatic way to ship static assets (HTML templates, database migrations, default config files) inside a single binary. The embedded content is read-only at runtime - you cannot write back to it. For large assets, weigh the binary size increase against the deployment simplicity gain; embedding gigabytes of media is a valid use case for dedicated object storage instead. embed.FS implements fs.FS, so any function that accepts fs.FS (including html/template.ParseFS and text/template.ParseFS) works directly with it. In tests, use //go:embed testdata in test files to embed fixtures alongside the test - the testdata directory is the convention for this.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free