Digging Deeper

Embedding and Deployment

Package templates, public files, and content into one executable and configure it at runtime.

By Guillermo Alvarez - Published - Updated

Embed application resources

GoLazy applications use embed.FS:

//go:embed views public
var Files embed.FS

Sub-filesystems keep consumers scoped:

var Views = lazyapp.MustSub(Files, "views")
var Public = lazyapp.MustSub(Files, "public")

Services can use the same pattern for embedded Markdown or other application content.

Initialize embedded files

Pass embedded resources to lazyapp.New during application startup:

func App() *lazyapp.App {
    return lazyapp.New(lazyapp.Config{
        Name:    "sample_app",
        Drawer:  Draw,
        Public:  app.Public,
        Views:   app.Views,
        Context: Context,
    })
}

lazyapp.New opens and validates views before serving requests. Public files are registered with lazyassets, then served by dispatch after route lookup.

Fingerprinted assets

Every registered asset keeps its logical path and receives a content-hashed permanent path:

/styles.css
/styles-<hash>.css

The logical path uses a revalidation-friendly cache policy and an ETag. The permanent path uses an immutable cache policy because the URL changes whenever the final asset bytes change.

Templates should link stylesheets through stylesheet:

{{stylesheet "/styles.css"}}

Stylesheets also rewrite local url(...) references to permanent asset paths when the referenced file is registered.

Generated assets

Packages can add generated content during startup with asset sources:

lazyassets.SourceFunc(func(registry *lazyassets.Registry) error {
    return registry.Add(
        "/assets/app.js",
        []byte("console.log('ready')"),
        lazyassets.ContentType("text/javascript"),
    )
})

Pass sources through lazyapp.Config.Assets:

return lazyapp.New(lazyapp.Config{
    Name:   "sample_app",
    Drawer: Draw,
    Public: app.Public,
    Views:  app.Views,
    Assets: []lazyassets.Source{javascriptBundle},
})

Generated assets receive the same hash, integrity, ETag, cache policy, helper lookup, and unpack behavior as embedded public files.

The sample JavaScript pipeline writes generated files before the Go build:

lazy js

lazy js bundles manifest libraries into app/public/assets/lazyshaft and writes app/public/assets/importmap.json. Because both paths live under app/public, they are embedded into production binaries together with ordinary public files. App-owned scripts such as app/public/javascript/application.js remain plain browser modules.

Build one binary

Build the executable with ordinary Go:

go build ./cmd/app

That command uses Go's default output name. In the sample application the default name is app, which would collide with the existing app/ directory, so choose an explicit artifact path for production:

go build -o /tmp/sample-app ./cmd/app

Run it:

/tmp/sample-app

No template or public directory is required beside the binary. Views, layouts, public files, and other embedded resources are compiled into the executable.

Unpack assets for static serving

The built application can expose its registry for a production command that unpacks final asset bytes:

if err := appinit.App().Assets.Unpack(
    "public",
    lazyassets.WithUnpackMode(lazyassets.UnpackPermanent),
); err != nil {
    log.Fatal(err)
}

UnpackBoth writes logical and permanent paths, UnpackLogical writes only logical paths, and UnpackPermanent writes only content-hashed permanent paths. A manifest.json is written beside the unpacked files.

Configure the address

lazyapp.App.ListenAndServe reads ADDR, then PORT, then falls back to :3000:

PORT=4000 /tmp/sample-app
ADDR=127.0.0.1:4000 /tmp/sample-app

A numeric value is treated as a port. A full value is passed directly to the underlying server.

Runtime configuration beyond ADDR and PORT belongs to the application until the framework defines a stable configuration API.

Rebuild on file changes

Embedded files are compiled into the executable. After changing:

  • Views.
  • Layouts.
  • Public files.
  • Files referenced from CSS url(...).
  • Embedded Markdown.

rebuild or restart go run.

Restart go run ./cmd/app after changing embedded resources.

Deployment checklist

Before deployment:

  1. Run lazy js if JavaScript manifest or package files changed.
  2. Run tests, race tests, and go vet.
  3. Build the exact commit being deployed.
  4. Start the binary with the production listen address.
  5. Request the home page and at least one public asset.
  6. Confirm logs and process supervision are configured by the deployment environment.

GoLazy intentionally leaves TLS termination, process supervision, and platform configuration to the deployment environment and the Go standard library.