Build from source
One command, and the only path on an architecture we do not publish.
View as MarkdownThe worker is open source, which is the point: it is the one component that runs on your machine holding your credentials, so you can read the code that handles them.
https://github.com/savedhq/local-workerBuilding it yourself is also the answer to two practical problems: an architecture we publish no binary for, and a container image whose tag does not list yours.
Build the binary
Go 1.26 or newer, and nothing else. There is no C toolchain to find and no generation step to run first.
git clone https://github.com/savedhq/local-worker
cd local-worker
go build -o local-worker ./cmdTo run it straight out of the tree while you are reading it:
go run ./cmdEither way it reads config.yaml from the working directory, so put one in the directory you
run from. See Configuration.
Match the released build
The releases are built with these flags. Use them and you get the same binary we do, which is what makes a checksum comparison meaningful.
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -trimpath -ldflags="-s -w" -o local-worker-linux-amd64 ./cmdCGO_ENABLED=0 is what makes the result statically linked, so it does not care which libc the
target host has. -trimpath keeps your local paths out of the binary.
Swap GOOS and GOARCH for anything Go supports. The six targets we publish are
linux, darwin and windows on amd64 and arm64, but go tool dist list is a much
longer list and none of the rest need anything special from us.
Build the container image
The Dockerfile does not compile anything. It copies a prebuilt binary from dist/, so
that file has to exist before you build. This is deliberate: compiling Go inside a
multi-architecture build runs the toolchain under emulation, which turns seconds into tens of
minutes.
ARCH=amd64
mkdir -p dist
CGO_ENABLED=0 GOOS=linux GOARCH="$ARCH" \
go build -trimpath -ldflags="-s -w" -o "dist/app-${ARCH}" ./cmd
docker build --build-arg TARGETARCH="$ARCH" -t local-worker:dev .The image adds curl, ca-certificates, postgresql-client, mysql-client and redis on
top of Alpine, and runs as uid 65532 with no working directory set. Everything on the
container page applies to your build exactly as it does to
ours, including the config landing at /config.yaml.
For both architectures at once, into your own registry:
for a in amd64 arm64; do
CGO_ENABLED=0 GOOS=linux GOARCH="$a" \
go build -trimpath -ldflags="-s -w" -o "dist/app-${a}" ./cmd
done
docker buildx build --platform linux/amd64,linux/arm64 \
-t registry.example.com/local-worker:v0.1.0 --push .Buildx sets TARGETARCH per platform, so the right binary is copied into each. The apk add
layer still runs under emulation for the non-native architecture, which costs a minute or two
and is the only slow part left.
Run the tests
go test ./...What you are trusting either way
Building from source changes what you are trusting, and it is worth being precise about how
much. It removes our release pipeline from the chain. It does not remove the dependencies in
go.sum, the Go toolchain, or the base image if you build the container.
The thing worth reading, if you read one thing, is internal/activity: the dump, the
encryption and the upload. That is where your data is handled, and it is under a thousand
lines.