Architecture
Generation Pipeline
manifest.yml is the single source of truth for all image definitions.
manifest.yml
└─▶ lib/image_generator.rb (ERB rendering)
├─▶ core/noble/Dockerfile
├─▶ core/noble/docker-bake.hcl
├─▶ ruby/4.0/Dockerfile
└─▶ node/24/Dockerfile
Image Hierarchy
ubuntu:noble / ubuntu:jammy / ubuntu:bionic
└─▶ core (base packages, users, locale)
├─▶ core-dev (build-essential, dev headers)
├─▶ ruby (Ruby + Bundler + RubyGems)
│ └─▶ ruby-dev
└─▶ node (Node.js + npm + Yarn)
└─▶ node-dev
Manifest Structure
globals:
defaults: # Apply to all image types
registry: ghcr.io/djbender
platforms: [linux/amd64, linux/arm64]
core:
versions:
noble:
latest: true # Gets the :latest tag
ruby:
defaults: # Override globals for ruby
base_image: "%{registry}/core"
versions:
'4.0':
ruby_version: 4.0.1
latest: true
Tag Generation
lib/tag_generator.rb generates tags consistently across templates and CI:
- Primary:
:version,:version-distro,:major,:latest - Dev:
:version-dev,:version-dev-distro,:major-dev
CI Flow
push to main
└─▶ GitHub Actions (build-images.yml)
├─▶ RSpec + RuboCop
├─▶ Build matrix (per image × per platform)
│ ├─▶ linux/amd64 (ubuntu-latest)
│ └─▶ linux/arm64 (ubuntu-24.04-arm)
└─▶ Merge jobs (multi-arch manifests)
└─▶ Push to ghcr.io/djbender/*
Feature branches build single-arch only. Multi-arch builds run on push to main.