Skip to content

Docker

Terminal window
docker run -d --name ghchronicle \
-v /etc/ghchronicle/config.yaml:/config.yaml:ro \
-e GITHUB_TOKEN -e INFLUX_TOKEN \
-p 9605:9605 \
ghcr.io/jmrplens/ghchronicle -config /config.yaml

Built FROM gcr.io/distroless/static-debian13:nonroot over a static, CGO_ENABLED=0 binary. There is no shell and no package manager in it, so code execution inside the container has nothing to pivot with, and the nonroot tag bakes in uid 65532, which keeps it off root even when the orchestrator sets no securityContext of its own.

Two consequences worth knowing before you debug it:

  • docker exec ... sh does not work. There is no sh. Read the logs instead.
  • Anything the container writes must be owned by, or writable by, uid 65532.

The config file is mounted read-only. Four things are not:

PathNeeded for
state_fileAlways. Without a persistent path the stargazer walk repeats on every restart
sinks.dedupe_fileAlways. The write ledger, which defaults to sitting beside the state file
sinks.file.pathOnly with the file sink
log.fileOnly with a log file configured

Both of the first two live in the same directory by default, so one mounted volume covers them. Mounting only the state file loses the ledger on every restart, and every restart then costs a whole sweep of rewriting, which is the one thing the ledger exists to prevent: only what changed is written.

Terminal window
docker volume create ghchronicle-state
docker run -d --name ghchronicle \
-v /etc/ghchronicle/config.yaml:/config.yaml:ro \
-v ghchronicle-state:/var/lib/ghchronicle \
-e GITHUB_TOKEN \
ghcr.io/jmrplens/ghchronicle -config /config.yaml

EXPOSE 9605 is the Prometheus exporter, and it is the only listener the process ever opens. Publish it only if you enabled the prometheus sink; every other sink is outbound.

compose.yaml
services:
ghchronicle:
image: ghcr.io/jmrplens/ghchronicle
command: ["-config", "/config.yaml"]
restart: unless-stopped
environment:
GITHUB_TOKEN: ${GITHUB_TOKEN}
INFLUX_TOKEN: ${INFLUX_TOKEN}
volumes:
- ./config.yaml:/config.yaml:ro
- state:/var/lib/ghchronicle
volumes:
state:

The container takes the same flags as the binary, so a scheduler can run it without a long-lived service.

Terminal window
docker run --rm \
-v /etc/ghchronicle/config.yaml:/config.yaml:ro \
-v ghchronicle-state:/var/lib/ghchronicle \
-e GITHUB_TOKEN \
ghcr.io/jmrplens/ghchronicle -config /config.yaml -once

Mount the state volume in this mode too. It is what makes the second run cheap.