Container image based on the GitHub Actions Runner with Python tooling, DevOps CLIs, and a full container build pipeline baked in. Designed to be used as a self-hosted runner that can build itself.
ghcr.io/actions/actions-runner (GitHub Actions Runner). Trivy is run with --pkg-types library and --ignore-unfixed, so OS packages from the base image (Ubuntu, containerd, docker-buildx, etc.) are not reported. Any remaining base-origin library findings can be listed in .trivyignore with expiration dates. Base image version is controlled by RUNNER_VERSION in manifest.yaml and is kept up to date by Renovate.
| Tool | Version |
|---|---|
| Python | 3.12, 3.13 (via deadsnakes PPA) |
| Poetry | latest |
| UV | latest |
| Tool | Description |
|---|---|
| Argo Workflows CLI | Workflow orchestration on Kubernetes |
| Kargo CLI | Application lifecycle orchestration |
| pack | Cloud Native Buildpacks CLI |
| skopeo | Container image registry operations |
These tools allow the image to run its own build pipeline as a self-hosted runner.
| Tool | Description |
|---|---|
| buildah | OCI container image builder |
| dive | Container filesystem analysis |
| trivy | Vulnerability scanner |
| hadolint | Dockerfile/Containerfile linter |
| yq | YAML processor |
| pre-commit | Git hooks framework |
| Workflow | Trigger | Description |
|---|---|---|
| CI | Pull request to main |
Commitlint, hadolint lint, test build |
| Release | Push to main |
Semantic release, build, scan, push to GHCR |
| Renovate | Automated | Keeps tool versions and dependencies up to date via PRs |
Releases are fully automated via semantic-release. Pushing to main triggers version analysis based on Conventional Commits. Tool versions are kept up to date automatically by Renovate.
| Commit prefix | Version bump |
|---|---|
fix: |
Patch (1.0.0 -> 1.0.1) |
feat: |
Minor (1.0.0 -> 1.1.0) |
feat!: / BREAKING CHANGE: |
Major (1.0.0 -> 2.0.0) |
When a new version is determined, the release workflow:
- Creates a GitHub release with auto-generated notes
- Updates
CHANGELOG.md - Validates the Containerfile with hadolint
- Builds the image with
buildah(OCI format, squashed layers) - Runs
divefilesystem efficiency scan - Runs
trivyvulnerability scan (library packages only, HIGH/CRITICAL, unfixed ignored) - Pushes to GHCR with semver tags:
1.2.3,1.2,1,latest
ghcr.io/deerhide/python-github-runner:latest
ghcr.io/deerhide/python-github-runner:1
ghcr.io/deerhide/python-github-runner:1.2
ghcr.io/deerhide/python-github-runner:1.2.3
Install Docker, then install the build tools:
./scripts/install_tools.shLocal install may use different (e.g. latest) versions for some tools than the pinned versions in the image and CI.
Build configuration is defined in manifest.yaml:
name: python-github-runner
tags:
- latest
registry: ghcr.io/deerhide/python-github-runner
build:
format: oci
args:
- RUNNER_VERSION=2.321.0
- ARGO_VERSION=3.6.4
- KARGO_VERSION=1.9.2
- PACK_VERSION=0.36.4
- DIVE_VERSION=0.12.0
- HADOLINT_VERSION=2.12.0
- YQ_VERSION=4.45.4
labels:
- org.opencontainers.image.source=https://github.com/deerhide/python-github-runner
- org.opencontainers.image.description="Python GitHub Runner"
- org.opencontainers.image.licenses="MIT"
- org.opencontainers.image.authors="Deerhide"
- org.opencontainers.image.vendor="Deerhide"Authenticate to the container registry:
skopeo login ghcr.ioRun the full build pipeline (lint, build, scan, push):
./scripts/builder.shInstall the git hooks locally:
pre-commit install --hook-type pre-commit --hook-type commit-msgHooks run automatically on every commit:
| Hook | Stage | Description |
|---|---|---|
| trailing-whitespace | pre-commit | Remove trailing whitespace |
| end-of-file-fixer | pre-commit | Ensure files end with a newline |
| check-yaml | pre-commit | Validate YAML syntax |
| check-added-large-files | pre-commit | Prevent large files from being committed |
| check-merge-conflict | pre-commit | Detect merge conflict markers |
| detect-private-key | pre-commit | Prevent private keys from being committed |
| hadolint | pre-commit | Lint Containerfile |
| shellcheck | pre-commit | Lint shell scripts |
| commitlint | commit-msg | Validate conventional commit messages |
Run all hooks manually against all files:
pre-commit run --all-filesThis project uses Conventional Commits. Commit messages are validated by commitlint on pull requests and locally via pre-commit hooks.
# Good
git commit -m "feat: add kubectl to image"
git commit -m "fix: correct trivy scan exit code"
git commit -m "chore: update argo to v3.7.0"
# Bad
git commit -m "added stuff"
git commit -m "WIP".
├── Containerfile # Multi-stage container definition
├── manifest.yaml # Build configuration and metadata
├── CHANGELOG.md # Generated by semantic-release
├── install-man-page.sh # Optional: install yq man page locally
├── .releaserc.yaml # Semantic release configuration
├── .hadolint.yaml # Hadolint configuration
├── .commitlintrc.yaml # Commitlint configuration
├── .pre-commit-config.yaml # Pre-commit hooks configuration
├── .containerignore # Build context exclusions
├── .dive-ci # Dive efficiency thresholds
├── .github/
│ └── workflows/
│ ├── ci.yaml # PR validation
│ └── release.yaml # Semantic release + build + push
├── renovate.json # Renovate dependency update config
└── scripts/
├── builder.sh # Local build orchestration
├── install_tools.sh # Build tool installer
├── lib_utils.sh # Logging utilities
└── login_skopeo.sh # Registry authentication helper
This image is based on actions/actions-runner. Trivy is configured to scan only library packages and to ignore unfixed vulnerabilities, so base-image OS packages are not reported. Any remaining base-origin findings can be listed in .trivyignore with expiration dates. Keep RUNNER_VERSION in manifest.yaml up to date (Renovate opens PRs) and review or remove .trivyignore entries when upgrading.