Deployment
This guide covers deploying a Tinka App. You will learn how to:
- Build the App for production with
act build - Ship static files inside the binary
- Build for another operating system or architecture
- Run the binary in production
- Package the App in a container image
Overview
A Tinka App builds into one binary that holds everything it needs: your code, Tinka, and any files you embed. There's nothing to install next to it, no runtime and no dependencies to copy, so deploying the App means copying one file and running it with its configuration in the environment.
act build runs go build with the settings a production binary needs, and prints the command it runs, so a machine without act, such as a CI runner or a container build, runs the same command with Go alone.
Build the App
Run act build from the App's directory:
act build$ GOWORK=off CGO_ENABLED=0 go build -trimpath -o bin/blog .
Built bin/blog (14.7 MB, v1.2.0, linux/amd64, revision 58d23ba966e3)The binary is named after the last part of the App's module path and written to bin/. It is built with:
CGO_ENABLED=0, so it's static: it runs on any machine of the same operating system and architecture, including minimal container images without a C library.-trimpath, so it holds no paths from the machine that built it, and the same commit builds the same binary anywhere.GOWORK=off, so ago.workfile on your machine can't change what goes in.
The version comes from the git tag of the commit, such as v1.2.0, and ends in +dirty when the App has uncommitted changes. Go records it in the binary, along with the commit, so act about and your own code read it without a build step.
act build warns when go.mod replaces a module with a local directory, because that build differs from one made from the same commit on another machine, and when the App has uncommitted changes.
Ship static files
Files the App serves, such as stylesheets and images, go inside the binary with embed, so they always match the code that serves them:
package main
import (
"embed"
"io/fs"
"net/http"
"github.com/mucolabs/tinka"
)
//go:embed public
var public embed.FS
// assets serves the files in public/ under /assets/.
func assets(app *tinka.App) error {
files, err := fs.Sub(public, "public")
if err != nil {
return err
}
app.Handle(http.MethodGet, "/assets/", http.StripPrefix("/assets/", http.FileServerFS(files)))
return nil
}act serve rebuilds the App when an embedded file changes, as it does for Go files.
Build for another system
Pass --target to build for another operating system and architecture, such as a Linux server from a Mac:
act build --target linux/arm64The first build for a new target takes longer, while Go builds its standard library for it; later builds are as fast as usual. Pass --cgo only when a dependency needs C, such as a SQLite driver written in C: the binary then needs the system's C library at run time.
Run the App in production
Copy the binary to the server, and run it with its configuration in the environment. Production reads no .env file, so set every variable in your platform's settings or your service definition:
APP_ENV=production APP_KEY=your_app_key ./blogRun with no arguments, the binary serves requests; with arguments, it runs one of the App's Commands, such as ./blog about. It shuts down gracefully on SIGTERM, which is what platforms send when they replace an instance. See Application lifecycle for the shutdown timeout and Health checks for readiness probes and draining.
On a server managed with systemd, socket activation keeps the port open while the App restarts. See Socket activation.
Package the App in a container
A container build has Go but not act, so it runs the go build command that act build prints. Build in a Go image, then copy the static binary into a minimal one:
FROM golang:1.27 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o /blog .
FROM gcr.io/distroless/static-debian12
COPY --from=build /blog /blog
ENV APP_ENV=production
USER nonroot
EXPOSE 3000
ENTRYPOINT ["/blog"]A .dockerignore that leaves out .git also leaves out the version: the binary then reports (devel). Copy .git into the build stage when you want the version recorded.
See also: Configuration & Environment, Commands reference, Health checks