---
title: "Go Embed: Embutindo Arquivos no Binário"
url: "https://golang.com.br/blog/go-embed-embutir-arquivos-binario/"
markdown_url: "https://golang.com.br/blog/go-embed-embutir-arquivos-binario.MD"
description: "Aprenda a usar go:embed para embutir arquivos, templates e assets estáticos no binário Go. Exemplos práticos com embed.FS, net/http e html/template."
date: "2026-05-12"
author: "Golang Brasil"
---

# Go Embed: Embutindo Arquivos no Binário

Aprenda a usar go:embed para embutir arquivos, templates e assets estáticos no binário Go. Exemplos práticos com embed.FS, net/http e html/template.


Desde o [Go 1.16](/blog/go-1-16/), a diretiva `//go:embed` permite embutir arquivos e diretórios inteiros dentro do binário compilado. Isso significa que seu deploy se resume a **um único arquivo executável** — sem se preocupar com caminhos de templates, arquivos de configuração ou assets estáticos que podem estar faltando em produção.

Neste guia, você vai aprender a usar `embed.FS`, embutir templates HTML, servir assets estáticos via HTTP e combinar tudo em aplicações reais.

## Por que Embutir Arquivos no Binário

Em Go, o binário compilado já é autocontido — sem runtime, sem dependências. Mas quando sua aplicação precisa de templates HTML, arquivos SQL de migração, configurações padrão ou assets de frontend, você volta a depender de arquivos externos no filesystem. A diretiva `//go:embed` resolve isso:

- **Deploy simplificado**: um binário, zero arquivos avulsos
- **Reprodutibilidade**: o binário sempre inclui a versão correta dos arquivos
- **Imagens [Docker](/blog/go-docker-imagens-minimas-multi-stage-builds/) menores**: sem copiar diretórios extras para a imagem final
- **Segurança**: arquivos embutidos não podem ser modificados em produção

## Embutindo um Arquivo Simples

O caso mais básico — embutir um único arquivo como string ou `[]byte`:

```go
package main

import (
	_ "embed"
	"fmt"
)

//go:embed version.txt
var version string

//go:embed config-default.json
var defaultConfig []byte

func main() {
	fmt.Println("Versão:", version)
	fmt.Printf("Config padrão: %s\n", defaultConfig)
}
```

Crie o arquivo `version.txt` com o conteúdo `1.0.0` e `config-default.json` com um JSON qualquer. Ao compilar, os arquivos são incorporados ao binário:

```bash
go build -o myapp .
rm version.txt config-default.json  # binário já contém os dados
./myapp  # funciona normalmente
```

Note o import `_ "embed"` — ele é obrigatório quando você usa `//go:embed` com tipos primitivos (`string` ou `[]byte`). Sem ele, o compilador retorna erro.

## Embutindo Diretórios com embed.FS

Para múltiplos arquivos ou diretórios inteiros, use o tipo `embed.FS`:

```go
package main

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

//go:embed templates/*
var templateFiles embed.FS

//go:embed static/*
var staticFiles embed.FS

func main() {
	// Listar arquivos embutidos
	fs.WalkDir(templateFiles, ".", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if !d.IsDir() {
			fmt.Println("Embutido:", path)
		}
		return nil
	})

	// Ler um arquivo específico
	data, err := templateFiles.ReadFile("templates/index.html")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Template: %s\n", data)
}
```

O padrão `templates/*` embute todos os arquivos no diretório `templates/`. Você pode usar múltiplos padrões:

```go
//go:embed static/css/* static/js/* static/images/*
var assets embed.FS
```

## Servindo Assets Estáticos via HTTP

Combinar `embed.FS` com o [net/http](/blog/como-criar-api-rest-go-net-http-sem-framework/) da standard library é uma das aplicações mais práticas. Perfeito para aplicações [HTMX](/blog/go-htmx-aplicacoes-web-modernas/) ou SPAs com frontend embutido:

```go
package main

import (
	"embed"
	"io/fs"
	"log"
	"net/http"
)

//go:embed static/*
var staticFiles embed.FS

func main() {
	// Remover o prefixo "static/" para servir na raiz
	staticFS, err := fs.Sub(staticFiles, "static")
	if err != nil {
		log.Fatal(err)
	}

	mux := http.NewServeMux()
	mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
	mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("API Go com assets embutidos!"))
	})

	log.Println("Servidor rodando em :8080")
	log.Fatal(http.ListenAndServe(":8080", mux))
}
```

A função `fs.Sub()` cria um sub-filesystem que remove o prefixo do diretório. Sem ela, os caminhos dos arquivos incluiriam `static/` — e as URLs ficariam como `/static/static/style.css`.

## Templates HTML com embed.FS

Para aplicações web que usam [templates](/glossario/template/) server-side, combine `embed.FS` com `html/template`. Se você trabalha com templates type-safe, veja também o [templ](/blog/go-templ-html-templates-type-safe/):

```go
package main

import (
	"embed"
	"html/template"
	"log"
	"net/http"
)

//go:embed templates/*.html
var templateFiles embed.FS

var tmpl *template.Template

func init() {
	tmpl = template.Must(template.ParseFS(templateFiles, "templates/*.html"))
}

type PageData struct {
	Title string
	Items []string
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	data := PageData{
		Title: "Minha App Go",
		Items: []string{"Item 1", "Item 2", "Item 3"},
	}
	if err := tmpl.ExecuteTemplate(w, "index.html", data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /", homeHandler)
	log.Fatal(http.ListenAndServe(":8080", mux))
}
```

A [função](/glossario/func/) `template.ParseFS()` aceita `embed.FS` diretamente — sem precisar ler arquivos do disco. Os templates são parseados uma vez no [init](/glossario/init/) e reutilizados em cada requisição.

## Migrações SQL Embutidas

Outro caso de uso popular é embutir arquivos SQL para migrações de banco de dados. Perfeito para projetos que usam [PostgreSQL](/aprenda/golang-postgresql/) ou outros bancos relacionais com [sqlc](/blog/go-sqlc-banco-dados-type-safe-sql/):

```go
package migrations

import (
	"embed"
	"io/fs"
	"sort"
	"strings"
)

//go:embed sql/*.sql
var migrationFiles embed.FS

func GetMigrations() ([]string, error) {
	entries, err := fs.ReadDir(migrationFiles, "sql")
	if err != nil {
		return nil, err
	}

	var migrations []string
	for _, e := range entries {
		if !e.IsDir() && strings.HasSuffix(e.Name(), ".sql") {
			migrations = append(migrations, e.Name())
		}
	}
	sort.Strings(migrations)
	return migrations, nil
}

func ReadMigration(name string) (string, error) {
	data, err := migrationFiles.ReadFile("sql/" + name)
	if err != nil {
		return "", err
	}
	return string(data), nil
}
```

Organize os arquivos com prefixo numérico (`001_create_users.sql`, `002_add_index.sql`) para garantir a ordem correta.

## CLI com Help Embutido

Para ferramentas de [linha de comando](/blog/go-cli-cobra-viper-linha-comando/), embutir textos de ajuda e exemplos mantém o binário autocontido:

```go
package main

import (
	_ "embed"
	"fmt"
	"os"
)

//go:embed help.txt
var helpText string

//go:embed examples/basic.yaml
var basicExample string

func main() {
	if len(os.Args) > 1 && os.Args[1] == "--help" {
		fmt.Print(helpText)
		return
	}
	if len(os.Args) > 1 && os.Args[1] == "--example" {
		fmt.Print(basicExample)
		return
	}
	fmt.Println("Executando aplicação...")
}
```

## Regras e Limitações do go:embed

Antes de usar `//go:embed`, conheça as regras:

1. **A diretiva deve estar imediatamente acima da declaração da variável** — sem linhas em branco entre elas
2. **Variáveis devem ser do tipo `string`, `[]byte` ou `embed.FS`** — outros tipos não são aceitos
3. **Apenas arquivos no [módulo](/glossario/module/) atual** — não é possível embutir arquivos de fora do módulo
4. **Arquivos ocultos (começando com `.` ou `_`) são ignorados por padrão** — use `all:` para incluí-los: `//go:embed all:templates`
5. **O padrão `*` não inclui subdiretórios** — use `**` ou liste explicitamente: `//go:embed templates/**`
6. **Caminhos são relativos ao package**, não ao módulo raiz

```go
// CORRETO: diretiva seguida da variável
//go:embed data.json
var data []byte

// ERRADO: linha em branco entre diretiva e variável
//go:embed data.json

var data []byte  // erro de compilação
```

## Performance: Embed vs Leitura em Disco

Arquivos embutidos são mapeados na memória do binário — a leitura é instantânea, sem syscalls de I/O. Mas considere os trade-offs:

| Aspecto | embed.FS | Leitura em disco |
|---------|----------|-----------------|
| **Velocidade de leitura** | Instantânea (memória) | Depende do disco |
| **Tamanho do binário** | Aumenta | Não afeta |
| **Atualização** | Requer recompilação | Editar arquivo basta |
| **Deploy** | Um arquivo | Binário + diretórios |

**Quando usar embed**: templates, migrações SQL, assets de frontend, arquivos de configuração padrão, textos de ajuda de CLI.

**Quando usar disco**: arquivos grandes (vídeos, datasets), conteúdo que muda sem redeploy, uploads de usuários.

Para aplicações web com [WebAssembly](/blog/go-webassembly-wasm-browser/), embutir o `.wasm` compilado no servidor Go é uma combinação poderosa — o servidor serve tanto a API quanto o binário WASM.

## Combinando com Table-Driven Tests

Embutir fixtures de teste é outro uso prático. Combine com [table-driven tests](/blog/testes-tabela-go-guia-table-driven-tests/) para iterar sobre múltiplos arquivos de entrada:

```go
package parser

import (
	"embed"
	"io/fs"
	"strings"
	"testing"
)

//go:embed testdata/valid/*.json
var validFixtures embed.FS

//go:embed testdata/invalid/*.json
var invalidFixtures embed.FS

func TestParseValidFiles(t *testing.T) {
	entries, _ := fs.ReadDir(validFixtures, "testdata/valid")
	for _, e := range entries {
		t.Run(e.Name(), func(t *testing.T) {
			data, _ := validFixtures.ReadFile("testdata/valid/" + e.Name())
			_, err := Parse(data)
			if err != nil {
				t.Errorf("arquivo válido %s retornou erro: %v", e.Name(), err)
			}
		})
	}
}
```

Esse padrão é extensível — adicionar um novo caso de teste é simplesmente adicionar um arquivo JSON na pasta `testdata/`.

## Exemplo Completo: Web Server com Frontend Embutido

Um servidor Go completo que serve uma SPA com assets embutidos e uma [API REST](/aprenda/api-rest-go/):

```go
package main

import (
	"embed"
	"encoding/json"
	"io/fs"
	"log"
	"net/http"
)

//go:embed frontend/dist/*
var frontendFiles embed.FS

func main() {
	frontendFS, err := fs.Sub(frontendFiles, "frontend/dist")
	if err != nil {
		log.Fatal(err)
	}

	mux := http.NewServeMux()

	// API
	mux.HandleFunc("GET /api/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
	})

	// Frontend (SPA)
	mux.Handle("GET /", http.FileServer(http.FS(frontendFS)))

	log.Println("Servidor rodando em :8080")
	log.Fatal(http.ListenAndServe(":8080", mux))
}
```

Com [Docker multi-stage build](/blog/go-docker-imagens-minimas-multi-stage-builds/), compile o frontend no primeiro estágio, copie para o diretório do Go, e o binário final inclui tudo. O resultado é uma imagem Docker mínima com API e frontend em um único executável.

## Próximos Passos

`//go:embed` transforma seu binário Go em uma unidade autossuficiente de deploy. Combine com [Docker multi-stage builds](/blog/go-docker-imagens-minimas-multi-stage-builds/) para imagens mínimas, [OpenTelemetry](/blog/go-opentelemetry-observabilidade-tracing-metricas/) para observabilidade, e [Kubernetes](/blog/go-kubernetes-operadores-controller-runtime/) para orquestração.

Para gerenciar a configuração em runtime (não embutida), veja como usar [Cobra e Viper](/blog/go-cli-cobra-viper-linha-comando/). E para entender como [iteradores](/blog/iteradores-go-range-over-func/) podem ajudar a percorrer arquivos embutidos de forma mais elegante, confira nosso guia sobre range over func.

Quer ver como outras linguagens resolvem o empacotamento de assets? Veja como <a href="https://rustlang.com.br/blog/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a> usa crates como `include_str!` e `rust-embed` para embutir arquivos em tempo de compilação, ou como <a href="https://ziglang.com.br/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'ziglang.com.br' })">Zig</a> resolve isso com `@embedFile` — uma abordagem minimalista que é built-in na linguagem.

## FAQ

### O que é go:embed e desde quando está disponível?

A diretiva `//go:embed` foi introduzida no Go 1.16 (fevereiro de 2021). Ela permite embutir arquivos e diretórios no binário compilado usando os tipos `string`, `[]byte` ou `embed.FS`, sem dependências externas ou ferramentas de geração de código.

### Embutir arquivos aumenta muito o tamanho do binário?

Sim, o tamanho do binário aumenta proporcionalmente ao tamanho dos arquivos embutidos. Para assets de frontend (HTML, CSS, JS minificados), o impacto geralmente é de poucos MB. Para arquivos grandes como vídeos ou datasets, prefira leitura em disco.

### Posso embutir arquivos de fora do módulo Go?

Não. A diretiva `//go:embed` só aceita caminhos relativos ao package atual, e os arquivos devem estar dentro do módulo Go. Não é possível usar caminhos absolutos ou `../` para sair do módulo.

### Como embutir arquivos ocultos (dotfiles)?

Por padrão, arquivos começando com `.` ou `_` são ignorados pelo `//go:embed`. Para incluí-los, use o prefixo `all:` na diretiva: `//go:embed all:templates` — isso inclui `.gitkeep`, `_helpers.html` e similares.
