Updated Nov 5, 20266 min read

Go Backend Boilerplate in 2026: What a Starter Needs and How to Get One

The pieces every Go API starter ends up with, a minimal standard library skeleton you can copy, and a starter that comes with Postgres, tests and deployment to AWS or GCP already wired.

Every Go backend starter converges on the same parts: an entry point, configuration, logging, graceful shutdown, a database with migrations, tests and a way to ship it. The HTTP router is the smallest of these decisions, and the database, test and deployment wiring is where a new project spends its first week and where hand-copied boilerplate drifts between services.

This guide lists what a Go boilerplate should include, gives a standard library skeleton you can start from, and shows the Encore (a tool that works alongside your framework) starter that comes with Postgres, tests and deployment to your own AWS or GCP account.

At a glance

PartWhat to use in 2026
LayoutPackages at the module root, cmd/ and internal/ once there are several binaries
Routingnet/http with method and path patterns (Go 1.22 and later)
Logginglog/slog with a JSON handler in production
ConfigEnvironment variables, read once at startup
Shutdownsignal.NotifyContext and http.Server.Shutdown
DatabasePostgres with versioned SQL migrations
Testsgo test against a real Postgres, not mocks of it
CITests, go vet and govulncheck on every pull request

Layout and configuration

The Go team's guide to organizing a Go module recommends starting with packages at the module root and adding cmd/ for multiple commands and internal/ for packages that other modules must not import. Deep trees of pkg/, domain/ and adapters/ created on day one mostly hold one file each, and a newcomer, or a coding agent, has to open five folders to follow a single request.

Read configuration from the environment once at startup, as described in the twelve-factor app, and fail fast when a required value is missing. Secrets do not belong in a committed .env file, which is the first thing an AI coding agent with repository access reads.

A standard library skeleton

Starting with Go 1.22, net/http routes by method and path pattern, so a small API needs no router dependency. The skeleton below logs with log/slog, stops accepting requests on SIGTERM with signal.NotifyContext and drains in-flight requests before exiting, which is what container platforms expect during a rolling deploy:

package main import ( "context" "encoding/json" "errors" "log/slog" "net/http" "os" "os/signal" "syscall" "time" ) func main() { log := slog.New(slog.NewJSONHandler(os.Stdout, nil)) mux := http.NewServeMux() mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) }) mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) { log.Info("get user", "id", r.PathValue("id")) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{"id": r.PathValue("id")}) }) srv := &http.Server{ Addr: ":" + getenv("PORT", "8080"), Handler: mux, ReadHeaderTimeout: 5 * time.Second, } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() go func() { if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Error("server failed", "err", err) os.Exit(1) } }() <-ctx.Done() shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(shutdownCtx); err != nil { log.Error("shutdown", "err", err) } } func getenv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback }

The skeleton handles the process lifecycle but not the parts that take longer: a Postgres instance for local development, a migration runner, a test database that is reset between runs, a Dockerfile, a CI workflow, and the cloud resources, networking and permissions the service needs in production. Each of those is another file to keep in sync across every service built from the same template.

Tests, CI and security checks

Run tests against a real Postgres, since mocked database layers pass while the SQL is wrong and the query that fails in production is the one the mock accepted. In CI, run go test ./..., go vet ./... and govulncheck, which reports only vulnerabilities in code paths your module actually calls. Pin the Go version with the go and toolchain lines in go.mod, so local builds and CI compile with the same release.

Set up the Encore starter

Get started
brew install encoredev/tap/encore &&
encore app create
Copy

Run in your terminal to get started locally.

Create an app from the URL shortener template, which is a small Go API with a Postgres database:

~
$ encore app create my-api --example=url-shortener$ cd my-api && encore run

A Go starter with the database, tests and deploys wired, using Encore

Encore

Encore is a tool that works alongside your framework for TypeScript and Go, with a platform that provisions and deploys into your own AWS or GCP account. The starter is ordinary Go: the url service declares its endpoints with //encore:api comments and its database in code, and encore run starts Postgres locally, applies the migrations in url/migrations and serves the API with a local development dashboard for calling endpoints and reading traces.

The Infrastructure page in the Encore dashboard with a database's configuration panel open, showing fields for vCPUs, memory, storage, availability type, max connections and backups.
Each provisioned resource can be reviewed and its configuration edited per environment in the Encore dashboard. The screenshot shows a GCP Cloud SQL database.

The core of the starter's url/url.go:

package url import ( "context" "encore.dev/storage/sqldb" ) type URL struct { ID string // short-form URL id URL string // complete URL, in long form } //encore:api public method=GET path=/url/:id func Get(ctx context.Context, id string) (*URL, error) { u := &URL{ID: id} err := db.QueryRow(ctx, ` SELECT original_url FROM url WHERE id = $1 `, id).Scan(&u.URL) return u, err } var db = sqldb.NewDatabase("url", sqldb.DatabaseConfig{ Migrations: "./migrations", })

The template also includes url/url_test.go, which shortens a URL and reads it back. encore test compiles the app and runs go test with all the usual flags against a test database, as covered in the testing docs.

A second service is a new package that calls the first like a function, and Encore turns the call into an API call between services. This redirect service sends visitors to the long URL:

// redirect/redirect.go package redirect import ( "net/http" "encore.app/url" "encore.dev" ) //encore:api public raw method=GET path=/r/:id func Redirect(w http.ResponseWriter, req *http.Request) { id := encore.CurrentRequest().PathParams.Get("id") u, err := url.Get(req.Context(), id) if err != nil { http.NotFound(w, req) return } http.Redirect(w, req, u.URL, http.StatusFound) }

The parts the standard library skeleton left open come from the same code on deploy. Encore builds the services, provisions Postgres as RDS on AWS or Cloud SQL on GCP, runs the services on Fargate, EKS, Cloud Run or GKE, and provisions an IAM role for each service, with a deploy on every push once the GitHub integration is connected. Adding Pub/Sub, cron jobs or object storage later is a declaration in the service that uses it rather than a new section of the boilerplate. For a coding agent, the starter is small enough to read in one pass, and the infrastructure it needs is in the same files as the endpoints.

Best fit: Go teams starting a new API who want Postgres, tests, service-to-service calls and deployment to their own AWS or GCP account working from the first commit.

Frequently asked questions

What should a Go backend boilerplate include?

A module layout with a cmd entry point or service packages, configuration from the environment, structured logging with log/slog, graceful shutdown on SIGTERM, a database with versioned migrations, tests that run against a real database, a Dockerfile or build step, and a CI workflow that runs tests and govulncheck.

Is there an official Go project layout?

The Go team documents module layouts at go.dev/doc/modules/layout, which recommends starting simple with packages at the module root and adding cmd and internal directories when a module has several commands or private packages. The widely shared golang-standards/project-layout repository is a community convention, not an official standard.

Should I use a Go boilerplate generator or start from scratch?

Start from the smallest starter that already runs against the database you will use in production, since the time goes into wiring the database, tests and deployment, not into the HTTP router. Generators that produce many folders up front tend to leave empty layers you maintain without using.

How do I create a Go API starter with Postgres using Encore?

Running encore app create my-app --example=url-shortener gives you a url service with Shorten and Get endpoints declared with //encore:api, a Postgres database declared with sqldb.NewDatabase and its migrations, and a test that runs with encore test. encore run starts it locally with Postgres.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders