Deploy in Docker
templ output is Go code compiled into your binary, so deployment is the same as any Go web app: build a static binary, put it in a small image, copy your assets.
Multi-stage Dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.24 AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go install github.com/a-h/templ/cmd/templ@latest \
&& templ generate \
&& CGO_ENABLED=0 go build -o /server .
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /server /server
COPY assets /assets
EXPOSE 8080
ENTRYPOINT ["/server"]
CGO_ENABLED=0produces a static binary that runs on distroless- The distroless static image with the nonroot user keeps the runtime surface minimal
- Copy the static assets (CSS, JS, images) the server serves; templ code itself needs no copying
Tip: You do not need
go:embedfor templ output.*_templ.gofiles are Go source compiled into the binary - there is nothing to embed.
If you commit generated files, the templ generate step is a no-op safety net; if you do not, it is required before go build.
Serving static assets
Serve CSS and JS with the standard library:
fs := http.FileServer(http.Dir("/assets"))
mux.Handle("/assets/", http.StripPrefix("/assets/", fs))
mux.Handle("/", templ.Handler(page()))
Single-binary variant
A single-binary deployment embeds assets with go:embed, so the container image needs only the one file:
//go:embed assets
var assets embed.FS
That variant is not in the official docs, but it is plain Go stdlib behavior - go:embed of templ’s own output is unnecessary and does nothing useful.
Where else it runs
The hosting docs also cover AWS Lambda via Docker images, so the same build output works for serverless targets.