# syntax=docker/dockerfile:1 # KeepOrg synchronization server. # # The server is a static binary with no third-party dependencies and no cgo, # so the runtime image carries the binary, a certificate bundle for talking # to an OIDC provider, and nothing else. There is no shell in it: the binary # probes its own health. # # podman build --format docker -t keeporgd: --build-arg VERSION= . # docker build -t keeporgd: --build-arg VERSION= . # # podman needs --format docker, because the OCI image format has no # HEALTHCHECK field and drops it silently. # # or `make docker`, which takes the version from versions.mk. FROM golang:1.24-alpine AS build ARG VERSION=dev WORKDIR /src # Only what the server is built from, so a change to the clients, the # Android app or the docs does not invalidate this layer. COPY go.mod ./ COPY cmd/keeporgd ./cmd/keeporgd COPY internal ./internal RUN CGO_ENABLED=0 GOOS=linux go build \ -trimpath \ -ldflags "-s -w -X main.Version=${VERSION}" \ -o /out/keeporgd ./cmd/keeporgd # Empty directories to copy into the scratch image with the right owner. # There is no shell there to create them later. RUN mkdir -p /out/var/lib/keeporg /out/etc/keeporg FROM scratch ARG VERSION=dev LABEL org.opencontainers.image.title="KeepOrg server" \ org.opencontainers.image.description="Synchronization server for collaborative KeePass databases" \ org.opencontainers.image.version="${VERSION}" \ org.opencontainers.image.source="https://garage.rcmd.space/keeporg" \ org.opencontainers.image.licenses="AGPL-3.0-or-later" \ cc.tiredsysadmin.application="cc.tiredsysadmin.linux.keeporgd" # Needed to reach an OIDC provider over TLS. Nothing else from the builder # comes along. COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt # The data directory holds state.json and the KDBX blobs. The configuration # directory holds server.json and, unless the key is supplied through # KEEPORG_MASTER_KEY, the master key that unwraps the vault keys. They are # separate on purpose: whoever holds both can read every merge vault. # # Give the master key somewhere durable, one of these three ways: # # --secret keeporg_master_key -e KEEPORG_MASTER_KEY_FILE=/run/secrets/keeporg_master_key # -e KEEPORG_MASTER_KEY="$(head -c 32 /dev/urandom | base64)" # -v keeporg-config:/etc/keeporg # # The first is preferred: the key is never in the process environment and # never on a disk this container can write. The file may hold base64 or 32 # raw bytes. Neither environment variable is ever created by the server, so # a secret that failed to mount is reported instead of being replaced. # # With none of the three, the server mints a key into the container's own # layer on first start and loses it on the next `run`. It says so loudly at # startup, and merging is all that stops working: clients still open their # vaults. COPY --from=build --chown=65532:65532 /out/var/lib/keeporg /var/lib/keeporg COPY --from=build --chown=65532:65532 /out/etc/keeporg /etc/keeporg VOLUME ["/var/lib/keeporg", "/etc/keeporg"] COPY --from=build /out/keeporgd /keeporgd # A fixed unprivileged id, matching the convention used by distroless. USER 65532:65532 EXPOSE 8765 # The image has no shell, so the binary checks itself. HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ CMD ["/keeporgd", "-healthcheck", "-addr", ":8765"] ENTRYPOINT ["/keeporgd"] CMD ["-addr", ":8765", "-data", "/var/lib/keeporg"]