Embedding Files & Build Tags
The //go:embed directive bakes files and directories straight into your compiled binary so you can ship a single self-contained executable, while build tags ( //go:build ) decide which source files are compiled for which platform.
Learn Embedding Files & Build Tags in our free Go course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Embedding with //go:embed
Put //go:embed <path> on the line directly above a package-level variable and import embed . A string or []byte variable receives a single file; an embed.FS receives whole directories as an in-memory filesystem you can ReadFile and fs.WalkDir . This example assumes a project with version.txt and a static/ folder next to it.
2️⃣ Serving an embedded site
Because embed.FS implements the standard fs.FS interface, you can serve it directly: wrap it with http.FS and hand it to http.FileServer . Your entire front-end ships inside one binary. We exercise it with httptest so you can see a real response.
3️⃣ Build constraints ( //go:build )
A //go:build line at the top of a file (above package , followed by a blank line) controls when that file is compiled. Split a platform-specific symbol across files: the toolchain includes exactly the ones whose constraints match the target GOOS . Here three files define a single program; on Linux only greet_linux.go is compiled.
🎯 Your Turn
Embed a single file into a string and print it. Fill in the two blanks marked ___ , create banner.txt next to the file, then run it.
❌ Blank line between //go:embed and the var — "directive must be followed by a Go variable".
❌ Embedding without import "embed" — won't compile.
✅ Import it (use _ "embed" for the string/[]byte forms).
❌ Embed path points outside the package directory (e.g. ../assets ) — not allowed.
✅ Embed only files at or below the package's own directory.
❌ //go:build not at the top (or no blank line after it) — the constraint is ignored.
✅ First non-comment lines of the file, then a blank line, then package .
string (or []byte ) for a single file; reach for embed.FS when bundling multiple files or whole directories.
Skipped entirely — it's not compiled into a Windows build, so a matching //go:build !linux file supplies the symbol instead.
Add the //go:embed templates directive over the embed.FS , then ReadFile both templates and print them.
Practice quiz
What does the //go:embed directive do?
- Imports a package
- Runs code at startup
- Copies a file or directory into the binary at build time
- Downloads files at runtime
Answer: Copies a file or directory into the binary at build time. //go:embed bakes the contents of a file or directory into the compiled binary at build time.
Where must //go:embed be placed relative to the variable?
- On the line directly above the variable, with no blank line
- Anywhere in the file
- Inside main
- At the top of the file
Answer: On the line directly above the variable, with no blank line. The directive must sit on the line directly above the package-level variable, with no blank line between.
Which import is required to use //go:embed?
- os
- io/fs
- bytes
- embed
Answer: embed. You must import "embed" (a blank import _ "embed" works for the string/[]byte forms).
Which variable type should you use to embed a SINGLE file's text?
- embed.FS
A string or []byte receives a single file; use embed.FS for multiple files or directories.
What type bundles a whole directory tree of files?
- embed.FS
Answer: embed.FS. embed.FS gives you an in-memory filesystem you can ReadFile, Open, and fs.WalkDir over.
How do you serve an embed.FS over HTTP?
- http.ServeFile(fs)
- fs.Serve()
- http.FileServer(http.FS(staticFiles))
- embed.Serve(fs)
Answer: http.FileServer(http.FS(staticFiles)). Wrap the embed.FS with http.FS to get an http.FileSystem, then pass it to http.FileServer.
What does a //go:build line at the top of a file control?
- The package name
- When (on which platform) that file is compiled
- Runtime behavior
- The import order
Answer: When (on which platform) that file is compiled. A //go:build constraint tells the toolchain when to include the file, e.g. only on a given GOOS.
A file with //go:build linux is compiled into a Windows build...
- Always
- Only with a flag
- Only if it has no errors
- Never — it is skipped
Answer: Never — it is skipped. On Windows the constraint isn't satisfied, so the file is skipped entirely; a //go:build !linux file supplies the symbol.
Can you embed a file outside the package directory, like ../assets?
- Yes
- No — only files at or below the package directory
- Only with embed.FS
- Only on Linux
Answer: No — only files at or below the package directory. Embed paths must be at or below the package's own directory; pointing outside it (e.g. ../assets) is not allowed.
Why must a //go:build line be followed by a blank line before package?
- For readability only
- To separate imports
- Otherwise the constraint is treated as an ordinary comment and ignored
- It is not required
Answer: Otherwise the constraint is treated as an ordinary comment and ignored. The constraint must be at the top followed by a blank line before package, or it is ignored as a regular comment.