DevStats docker images: minimal (hourly cron job sync), full (provisioning/bootstraping), Grafana (UI endpoint), Patroni (database), tests.
Create and remove docker images:
- To create DevStats docker container images and publish them, use:
DOCKER_USER=... ./images/build_images.sh. - To drop local DevStats docker container images use:
DOCKER_USER=... ./images/remove_images.sh. - You can add various flags to skip specific images like
SKIP_FULL=1,SKIP_MIN=1,SKIP_TEST=1,SKIP_PROD=1seeimages/build_images.sh. - You can skip publishing to docker hub via
SKIP_PUSH=1. IMAGE_TAG=xyzbuilds and pushes every image asname:xyzinstead of the default tag (latest) - e.g.IMAGE_TAG=try SKIP_PROD=1 SKIP_GRAFANA=1 SKIP_PATRONI=1 SKIP_STATIC_NOBINS=1 SKIP_TESTS=1 DOCKER_USER=... ./images/build_images.shto try new images in the test namespace (kubectl -n devstats-test set image cronjob/devstats-riff devstats-riff=DOCKER_USER/devstats-minimal-test:go127) without touching the tags the production CronJobs pull.
Image contents (Go images, images/Dockerfile.*):
- Binaries are built with
golang:1.27(statically linked, stripped) and installed under/usr/bin; the full images also have/etc/gha2db(metrics, SQLs, data files) and/go/src/devstats(thedevstatsdata repository +devstats-docker-imagesscripts,patch.sh-ed for the test/prod hostname, with symlinks to the binaries). - No source code is shipped: after the build the full images remove everything that came with the
devstatscodeclone (Go/Rust sources,.git,go.mod/go.sum, developer scripts) unless thedevstats/devstats-docker-imagestarballs provide a file at the same path (cron/,devel/,git/scripts,projects.yaml, ...), so/go/src/devstatsis exactly the same file set in the Go and-rustimages. - The full images use the official
postgres:18-alpineimage (psql/pg_dump/pg_restore matching the server version,docker-entrypoint.sh) minus server-only leftovers nothing in DevStats uses (no PostgreSQL server ever runs from these images): the LLVM JIT (libLLVM~170 MB,llvmjit.so, bitcode), C headers and static libraries. Because layers are additive the trimmed root filesystem is flattened into a fresh image (FROM scratch+COPY --from=pgbase / /) and the base image's runtime configuration (ENV,VOLUME,ENTRYPOINT,STOPSIGNAL,EXPOSE,CMD) is re-declared in the Dockerfile - keep it in sync withdocker image inspect postgres:18-alpinewhen bumping the base image. Result:devstats-test/devstats-prod~600 MB instead of ~900 MB with identical behaviour.
Shortcuts:
- Build only API images:
./example/build_api.sh.
Rust images (DevStats binaries from the Rust port in devstatscode/rust instead of the Go ones):
RUST=1 DOCKER_USER=... SKIP_PATRONI=1 ./images/build_images.shbuilds every image that contains at least one DevStats binary under an alternate name with the-rustsuffix:devstats-test-rust,devstats-prod-rust,devstats-minimal-test-rust,devstats-minimal-prod-rust,devstats-tests-rust,devstats-static-test-rust,devstats-static-prod-rust,devstats-reports-rust,devstats-api-prod-rust,devstats-api-test-rust. The regular (Go) images are not touched and images without DevStats binaries (grafana, patroni, static-cdf/graphql/default, backups-page) are skipped in this mode. AllSKIP_*flags work as usual.- The binaries are compiled inside Docker (
images/Dockerfile.rust-bins,rust:alpinebuilder, static Linux musl executables with the same names/CLI/env as the Go ones) (a local intermediate imageDOCKER_USER/devstats-rust-bins, never pushed, exported to./rust-bins/) and shipped viagrafana-bins.tar/api-bins.tar(same Dockerfiles as the Go images) anddevstats-bins.tar(images/Dockerfile.{full,minimal}.{prod,test}.rust, generated from the Go Dockerfiles byimages/rust_dockerfile.shso project lists stay single-sourced;images/Makefile.{full,minimal}.rustlay them out). The-rustimages have the same runtime content as the Go ones (same base images and packages,/etc/gha2db, identical/go/src/devstatsfile set, binaries under the same names, no source code in either) but are smaller: static Rust binaries are less than half the size of the Go ones. Docker with BuildKit (Docker 23+) is required; the cargo registry and build directory are BuildKit cache mounts, so rebuilds are incremental. devstats-tests-rustrunsrust/test.sh(rustfmt, clippy, unit tests and the Go⇄Rust compatibility tests against a local PostgreSQL 18):docker run -ti DOCKER_USER/devstats-tests-rust.RUST=1 DOCKER_USER=... ./images/remove_images.shremoves the-rustimages.DOCKER_USER=... ./images/build_rust_bins.sh [dir]only builds the static Rust binaries (into./rust-bins/by default) -build_images.shuses it inRUST=1mode anddevstats/devel/create_grafana_shared_data.shtakes thereplacer,sqlitedbandrunqshipped in the shared Grafana data from it, so grafana pods run the same binaries as the images.- Since 2026-09-12 the
-rustimages are the ones deployed (all CronJobs and the API indevstats-testanddevstats-prod,devstats-helmdefaults); the Go images are still built and pushed under their original names as the rollback path. - The
full/minimalDockerfiles (Go and Rust) setgit config --system maintenance.autoDetach falseandgc.autoDetach false: git 2.46+ otherwise forks a detachedgit maintenance run --autoafter everyfetch/pull/clone, and because thedevstatsbinary is PID 1 in the sync pods and does not reap orphaned processes, every such run stayed behind as a[git]zombie until the pod ended (1-2 per repository per run, harmless but noisy:pids.maxis in the hundreds of thousands). With the setting the auto maintenance still runs, but in the foreground.
Using kubernetes:
- To test sync DevStats image (
devstats-minimal-test,devstats-minimal-prodcontainers):DOCKER_USER=... ./images/test_image_kubernetes.sh devstats-minimal-test. - To test provisioning DevStats image (
devstats-test,devstats-prodcontainers):DOCKER_USER=... ./images/test_image_kubernetes.sh devstats-prod. - To test Grafana DevStats image (
devstats-grafanacontainer):DOCKER_USER=... ./images/test_image_kubernetes.sh devstats-grafana. - To execute test coverage:
./tests/test_from_docker.sh. - Making pushes to GitHb triggers automatic Travis CI builds.
Using docker:
- Replace
./images/test_image_kubernetes.shwith./images/test_image_docker.sh. - To execute test coverage:
./tests/test_from_k8s.sh.
- To run
devstats-reportsimage using docker:DOCKER_USER=... ./images/devstats_reports_docker.sh.
devstats-helm/backups.sh backs up every selected project sequentially (artificial-event archive, then full dump), followed by the shared affiliations database if present. Dump age and randomness no longer select projects; NOAGE is unnecessary. Existing schedules, ONLY selection and replica endpoint remain unchanged.
By default it waits indefinitely for provisioning, project syncs (even those older than nine hours), and shared affiliations imports. BACKUP_WAIT_STEP defaults to 60 seconds; a positive BACKUP_WAIT_MAX opts into a wait limit. A failed activity check refuses that backup, and failed or skipped backups make the run exit nonzero after processing the remaining databases. Stale flags therefore require investigation rather than automatic expiry. These checks do not lock out new syncs for the duration of a dump.
Full dumps use fast compression (-Z1) and replace the previous .dump only after successful completion; allow disk space for one additional in-progress dump. Backup connections disable parallel query workers and use 16MB work_mem; the existing FASTXZ=1 chart setting keeps artificial-archive compression light. Backup-only Helm defaults in cncf/devstats-helm limit the client pod to 1 CPU/2Gi (request 100m/256Mi); these are not server-side CPU/I/O limits.
Isolated orchestration tests (no real database or /root writes): python3 -m unittest discover -s tests -p test_backups.py.
See cncf/devstats-helm:ADDING_NEW_PROJECTS.md or NEW_PROJECT.md (this is only for updating docker images and adding to bare kubernetes deployment).