How to Embed Configuration Files in Go

Use the `embed` package (available in Go 1.16+) to compile configuration files directly into your binary, eliminating the need for external file dependencies at runtime.

Use the embed package (available in Go 1.16+) to compile configuration files directly into your binary, eliminating the need for external file dependencies at runtime. This approach ensures your application is self-contained and prevents missing file errors in production environments.

For simple string-based configs like JSON or YAML, embed the file as a string and unmarshal it directly into your struct. This is the most common pattern for static configurations.

package main

import (
	"encoding/json"
	"embed"
	"fmt"
	"log"
)

//go:embed config.json
var configData []byte

type Config struct {
	ServerPort int    `json:"server_port"`
	Database   string `json:"database"`
}

func main() {
	var cfg Config
	if err := json.Unmarshal(configData, &cfg); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Config loaded: Port %d, DB %s\n", cfg.ServerPort, cfg.Database)
}

If you need to manage multiple files, directories, or require file-like operations (such as reading specific paths or listing contents), embed a FS (File System) instead. This gives you a virtual file system accessible via os-like functions.

package main

import (
	"embed"
	"fmt"
	"io/fs"
	"log"
)

//go:embed configs/*.yaml
var ConfigFS embed.FS

func main() {
	// Read a specific file from the embedded FS
	data, err := ConfigFS.ReadFile("configs/production.yaml")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Loaded %d bytes from production.yaml\n", len(data))

	// Walk through all embedded files
	err = fs.WalkDir(ConfigFS, "configs", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		fmt.Printf("Found embedded file: %s\n", path)
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
}

Remember that the //go:embed directive must be placed immediately before the variable declaration in the same file. The path is relative to the directory containing the Go source file. While this makes distribution easier, it prevents runtime hot-swapping of configuration without rebuilding the binary, so use this primarily for default configurations or immutable settings.