Build And Deploy
Upgrade Guide
Upgrade an existing GoLazy application to the latest GoLazy release.
Upgrade from v0.1.19
GoLazy v0.1.20 is source-compatible for applications already on v0.1.19.
It adds default in-memory auth, PostgreSQL auth, cache byte ceilings, automatic
OpenTelemetry startup, route/controller metric labels, richer route traces,
lazyjobs schedules and queue limits, response-writer and session helpers,
generated BeforeAction arguments, typed form validation helpers, and the
latest cheatsheet snippets. Existing application code can keep running, but
production deployments should review the operational defaults below before
rolling out.
Telemetry also remains source-compatible. lazyapp.New now creates a
lazytelemetry.Telemetry instance automatically and registers it in the app
dependency lifecycle. Deployments that already set
OTEL_TRACES_EXPORTER=otlp or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT will start
exporting request spans through the standard OpenTelemetry trace exporter after
the upgrade, so confirm the endpoint, protocol, and sampler settings before
rolling this into production. Prometheus request metrics now populate the
route label with the matched route pattern. The http_server_requests_total
and http_server_request_duration_seconds series also include controller and
action labels for matched controller routes, so dashboards or alerts that
aggregate those metrics may need to include or drop those labels explicitly.
Cache behavior remains source-compatible. Applications that rely on the
lazyapp.New default in-memory backend now get a 50 MiB cache byte ceiling.
Deployments that need another default-backend ceiling can set
LAZYAPP_CACHE_SIZE, using bare bytes or case-insensitive Kb, Mb, or Gb
units. Applications that need code-owned cache behavior can still provide their
own lazyapp.Config.Cache.Backend with inmemorycache.Options.MaxSizeBytes.
Production deployments with tight memory limits should also set GOMEMLIMIT
below the container memory limit.
The new MCP, auth, OAuth, JWT, and rate-limit packages are additive. Existing
applications do not need source changes unless they opt in to
lazyapp.Config.Auth, lazyapp.Config.OAuth, or lazyapp.Config.MCP.
lazyapp.New now includes a default in-memory lazyauth backend with zero
users. Set LAZYAUTH_DEFAULT_PASS to create the bootstrap admin user, and
set LAZYAUTH_DEFAULT_USER to use a different username. Apps with custom
password, file, PostgreSQL, SSO, or account-service auth should keep using
lazyapp.Config.Auth.
The new lazyjobs scheduling and queue-limit APIs are additive. Existing
Enqueue, EnqueueIn, EnqueueAt, job definitions, and backends continue to
work. Apps that register schedules must use a backend that implements
lazyjobs.SchedulerBackend; the bundled in-memory backend and
golazy.dev/pg/pgjobs do. PostgreSQL apps should include the latest
pgjobs.Migrations() source before enabling scheduled jobs so the
lazy_job_schedules table and schedule_key metadata exist.
lazycontroller.Base.ResponseWriter() is additive and does not require source
changes. Apps with custom base controllers that only copied the current request
or response writer in BindRequest can remove that app-level lifecycle method,
use Request() and ResponseWriter(), and keep request setup in
BeforeAction.
Generated BeforeAction arguments are additive and do not require source
changes. When you next touch a protected controller group, prefer a typed
generator such as GenUser() (*User, error) plus
BeforeAction(user *User) error over a CurrentUser view value or repeated
auth-only user parameters on every action. Keep the login redirect or status
mapping in HandleError.
The controller route redirect, session, flash, and Param helpers are
additive. Existing RedirectTo, Redirect, lazysession.Get(r), and
r.PathValue(...) code keeps working. New or touched controllers should prefer
RedirectToRoute with RedirectStatus(http.StatusSeeOther) after successful
create/update actions, PermanentRedirectTo or PermanentRedirectToRoute for
canonical moves, and SessionGet for read-only session access. Use
SessionSet, SessionDelete, FlashSet, and FlashGet for writes so the
framework can mark the session dirty and save the cookie only when needed.
Application auth wrapper types remain app-owned generated values; no
lazy upgrade source rewrite is required.
Generated form arguments are also additive. Existing direct c.Decode(&form)
calls can keep working, but new or touched form actions should usually move the
request input struct into a nearby file such as password_form.go, implement
GenPasswordForm by allocating the form and calling c.Decode(form), and have
the action receive *PasswordForm. Decode errors returned by the generator
skip the action and go through the same HandleError path as action errors.
Application validation failures after a create or update should come from
ordinary errors returned by lazyerrors.Validator(form) and optional form-owned
Validate() error methods. Render the form view with
http.StatusUnprocessableEntity, set the joined validation error for
field_errors_for / errors_for, and redirect rather than render after
successful creates.
Run the standard module update:
lazy upgrade --target v0.1.20
If an app adopted early migration examples with app-owned files in
migrations/postgres and uses lazymigrate.ForDatabase, move those files to
db/postgres/migrations before relying on the helper. Apps that intentionally
keep another layout can continue using an explicit
lazymigrate.FromFS(files, "migrations/postgres").
Upgrade from v0.1.18
GoLazy v0.1.19 adds opt-in PWA and browser-worker support plus bundled
application migrations through lazyapp.Config.Migrations. Existing
applications do not need source changes unless they want to become installable,
register worker scripts through GoLazy, prepare browser push subscriptions, or
let the app binary run migrations with LAZYAPP_MIGRATE=up or
LAZYAPP_MIGRATE=auto. The matching lazy development panel also adds
Terminal, Docs, and Media tabs. Terminal and Docs do not require application
source changes. The Media tab shows storage, file, and variant data when an app
opts in by passing named storages, *lazyfiles.Files, or *lazymedia.Media
through lazyapp.Config.
GoLazy also starts permanently redirecting GET and HEAD trailing-slash
requests for registered routes to the same route without trailing slashes. That
canonicalization is automatic and does not require application source changes.
Run the standard module update:
lazy upgrade --target v0.1.19
Then add lazyapp.Config.PWA and {{pwa}} in your layout only if the
application should opt in. Use lazyapp.Config.Workers for app-owned workers
that are not part of the PWA setup. Read
PWA, Workers And Notifications.
Add lazyapp.Config.Migrations only when the app should bundle migration
backends. Object-storage deployments can also opt in to
golazy.dev/lazyassets/assetmigrate to upload the current asset registry
during LAZYAPP_MIGRATE=up or LAZYAPP_MIGRATE=auto. This is additive;
lazy upgrade does not rewrite application source for migrations or asset
uploads.
Upgrade from v0.1.17
GoLazy v0.1.18 makes lazyapp.Config.Jobs run after Dependencies with the
dependency-initialized app context. Applications without Jobs do not need
source changes.
Run the automated migration:
lazy upgrade --target v0.1.18
The migration updates the golazy.dev module requirement and wraps static job
configs:
Jobs: lazyapp.Jobs(lazyjobs.Config{
Define: jobs.DefinedJobs,
})
Apps with durable job backends can instead return the config from a function
that reads shared dependencies from context, such as pg.FromContext(ctx) for
PostgreSQL-backed jobs. Read Background Jobs and
PostgreSQL.
Upgrade from v0.1.16
GoLazy v0.1.17 changes two controller APIs:
c.SetLayout("name")becomesc.Layout("name").c.CacheKey(...)andc.CacheKeyF(...)now returntruewhen an existing cached body was written. Returnnilimmediately on that hit, then continue setting controller data and rendering on a miss. Render cache keys now also include the app build version, and active render variants when present, so existing in-memory render cache entries naturally miss after a new build.
Run the automated migration:
lazy upgrade --target v0.1.17
The migration updates the golazy.dev module requirement, rewrites common
SetLayout, CacheKey, and CacheKeyF controller call shapes, and keeps the
normal upgrade checks in place.
Upgrade from v0.1.15
GoLazy v0.1.16 does not require source rewrites for applications that already
run on v0.1.15. The release adds framework cache support, deferred template
values, route-link helpers, richer telemetry, and the DevTools-style lazydev
panel. Use lazy upgrade to update the golazy.dev module requirement and run
the normal follow-up checks.
After updating the CLI, the default lazy development proxy may show a local
HTTPS setup page on the plain HTTP address. Install the GoLazy local
certificate authority once per machine or browser trust store to use the
development panel and proxied app over HTTPS on the same port. This does not
require application source changes.
Install or update the matching CLI:
curl -fsSL https://golazy.dev/install.sh | sh
lazy --version
Run the application migration:
lazy upgrade --target v0.1.16
lazy upgrade applies the framework module update with go get from the
release's go.mod manifest, then runs the standard follow-up checks. It does
not edit application source for the v0.1.15 -> v0.1.16 step.
Apps that adopted the early JSONL repositories for lazyfiles or lazymedia
should update their imports and constructors:
filesRepo, err := filesjsonl.New("storage/files.log.jsonl")
mediaRepo, err := mediajsonl.New("storage/variants.log.jsonl")
Use golazy.dev/lazyfiles/jsonl and golazy.dev/lazymedia/jsonl for those
aliases. The parent packages continue to expose the Repository interfaces
used by lazyfiles.Files and lazymedia.Media.
PostgreSQL-backed apps can now use golazy.dev/pg/pgfiles and
golazy.dev/pg/pgmedia for durable file and media metadata, and
golazy.dev/pg/pgstorage anywhere a lazystorage backend is useful, including
lazyassets.Upload.
Asset commands may change which Node package manager they use after the CLI
upgrade. lazy js and lazy tailwind now prefer active installed mise tools
in pnpm, yarn, bun, node order, with node mapped to npm. If mise
cannot provide one of those tools, the commands fall back to direct npm /
npx. No application source migration is required for this behavior change.
After updating to a CLI with the current lazy js behavior, app-owned files
under app/js regenerate as readable, unbundled, content-hashed assets.
Manifest library entrypoints remain bundled. App-owned importmap specifiers are
now relative to app/js: layouts import app.js, and generated Stimulus code
imports controllers such as controllers/hello_controller.js. Run
lazy upgrade to rewrite old /js/... imports; the v0.1.17 migration runs
lazy js automatically when js.toml is present. Commit the refreshed
importmap and lazyshaft files when the generated output changes.
lazy dump, lazy load, and the managed service preparation flow are
additive. No source migration is required. Apps can opt in by adding service
lifecycle tasks under .mise/tasks/<service>/ and, when explicit ordering is
needed, listing service names in lazy.toml.
If you are upgrading from v0.1.14 or earlier, first run the earlier
automated migrations or read the previous versioned upgrade guides. The
v0.1.14 -> v0.1.15 step still migrates dependency initializers and SEO
defaults before the v0.1.16 module update runs.
Review New Features
Caching
lazyapp.App now owns a default cache and exposes it through
golazy.dev/lazycache. Controller views, partials, and Turbo frame bodies can
use the cache helpers. Render cache keys include the app build version and
active render variants when present. Lazydev exposes cache state, keys, entry
content, and On/Off actions. Read Caching.
Deferred Template Values
Controllers can use SetLater and SetWhenNeeded when a template value is
expensive or only needed in some render paths. Templates resolve the value with
.Value. Read
Template Data And Helpers.
Route Link Helpers
Templates can use link_to, path_for, attr, data, and unless_current
to render escaped links to named routes without handwritten anchor tags. Read
Template Data And Helpers.
Telemetry
GoLazy can enable OpenTelemetry-compatible tracing, logging, request IDs, and
in-memory metrics from meaningful OTEL_* environment variables. Set
OTEL_SDK_DISABLED=true to disable telemetry explicitly. Prometheus metrics
are served from the control plane when OTEL_METRICS_EXPORTER=prometheus and
CONTROL_PLANE_ADDR are configured. Read
Telemetry.
Background Jobs
Applications can opt into golazy.dev/lazyjobs through
lazyapp.Config.Jobs. Existing apps do not need source changes unless they
want background jobs. Read Background Jobs.
lazydev Panel
The lazy development loop now embeds a DevTools-style panel at /_golazy/
with App, Requests, Services, Terminal, Routes, Jobs, BuildInfo, Assets, and
Cache tabs. It also forwards request IDs and trace context to the child app so panel
artifacts line up with framework telemetry. The Requests tab can list captured
request paths, filter by path or category, and open lazy-loaded Headers,
Tracing, and Logs details when detailed request monitoring is enabled. The
Routes tab reads the app's development control plane, so no application source
change is needed to see registered routes there; static GET routes link to the
running app. The BuildInfo tab shows the running app's Go version, command
path, main module, dependencies, replacements, recorded build settings, and the
last Go build trace summary; no application source migration is required. The
Dependencies tab shows the
running app's lazydeps service graph, progressively enhanced into SVG from
server-rendered table rows. It can simulate lazydev shutdown by sending
10 requests per second to the app root for a configured delay, marking
/readyz not ready, showing active requests and connections, and coloring
services as dependency cancellation progresses; no application source migration
is required. The
App tab owns app status,
changed-file groups, and rebuild, restart, and open-app controls. The Services
tab can restart, stop, or start
managed local services when service lifecycle tasks are present, groups output
by service tree and script name, parses JSON log lines into message and
attribute columns, caps log output at 100 rows, and shows run numbers for
repeated task attempts such as check; no application source migration is
required. The Assets tab lists the app asset manifest and public paths, and
the Terminal tab opens an xterm.js shell through a lazy-owned WebSocket in the
current app root. The Cache tab shows cache size, usage, searchable keys,
selected entry content, and cache controls. Detailed request monitoring also adds
per-region self time and allocation samples to the Requests tab without
requiring application source changes. Panel tab streams hydrate their lists
when they connect and then update only from relevant backend events. The status
bar keeps its status
stream inside the permanent Turbo frame, so tab switches keep one long-lived
status connection; no application source migration is required for that
behavior. The proxy also serves Chrome DevTools Automatic Workspace Folders
metadata for the app's app/js folder; no application route or source
migration is required. The injected host script also exposes
window.disableDevPanel() for the Chrome DevTools extension; no application
source migration is required. The proxy now injects hidden panel host markup
that is controlled by devpanel_controller.js; no application template
migration is required. Read lazydev and
Lazy.
The same public port accepts plain HTTP and HTTPS. Plain HTTP serves the local certificate authority setup page until the browser trusts the GoLazy CA; HTTPS serves the panel and proxied app traffic, with HTTP/2 available for the panel's long-lived streams.