Digging Deeper
Embedding and Deployment
Package templates, public files, and content into one executable and configure it at runtime.
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.
Tailwind CSS uses the same public-asset rule. The stylesheet source lives outside public files:
app/styles/application.css
Compile it before building the Go binary:
lazy tailwind
The command writes app/public/styles.css. Because that output is under
app/public, production builds embed the compiled CSS and lazyassets
fingerprints it like any other public file.
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.
- Tailwind input stylesheets.
- Public files.
- Files referenced from CSS
url(...). - Embedded Markdown.
rerun the needed asset command, then rebuild or restart the running process.
During development, lazy watches these files and restarts the temporary app
binary after successful rebuilds. Direct go run ./cmd/app processes and
production binaries still need a manual restart after embedded resources
change.
Deployment checklist
Before deployment:
- Run
lazy tailwindif Tailwind input, package files, or stylesheet output changed. - Run
lazy jsif JavaScript manifest or package files changed. - Run tests, race tests, and
go vet. - Build the exact commit being deployed.
- Start the binary with the production listen address.
- Request the home page and at least one public asset.
- 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.