Ansible Galaxy & Git Mirror Daemon

ORBITRON

Self-hosted mirror for Ansible roles & collections. A single static Go binary that speaks native Galaxy V1 + V3 APIs — so ansible-galaxy just works against it, with zero client changes, even when there is no internet.

CLIENT — no changes neededansible.cfgini
[galaxy]
server_list = orbitron

[galaxy_server.orbitron]
url = https://orbitron.internal/api/
  • 1static binary
  • 0runtime deps
  • V1+V3Galaxy APIs
  • 90ddefault prune
Capabilities

Built for isolated, pinned, repeatable automation

Orbitron caches whatever your pipelines need — roles, collections, git-backed sources — and serves them as an authentic Galaxy endpoint. Everything ships in one dependency-free binary.

Native Galaxy V1 + V3 API

Drop-in for ansible-galaxy role install and collection install. Point ansible.cfg at it — no client changes, no retraining. Roles stream as on-the-fly .tar.gz archives; collections serve real Galaxy V3 metadata.

Official Ansible collection

Manage the daemon purely with Ansible: chrisvanmeer.orbitron ships six HTTP modules (info, token, manifest, sync, purge, prune) and two roles — orbitron for install & lifecycle, orbitron_mirror for declarative mirroring. One playbook run, from empty host to serving cache.

One static binary

Compiled with CGO_ENABLED=0 for linux/amd64 and linux/arm64. Zero runtime dependencies — drop it on a bare-metal box, a container, or a Nomad allocation and it runs.

Parallel background sync

Ingest YAML requirements manifests once. Roles and collections are cloned and downloaded by concurrent workers, stored under content-addressed SHA-256 hashes, and replayed on every full re-sync.

Ansible-compatible version specifiers

latest, all, =1.4.5, >=1.0.0, ~=1.4.5, <2.0 and wildcards — a zero-dependency PEP 440-subset engine with proper semantic ordering. Never re-downloads what it already has on disk.

Access-based pruning

--prune --days N removes versions never served inside the retention window, backed by an atomic per-storage access index. Preview candidates with POST /api/v1/prune with {"dry_run": true}. Never-accessed versions are always kept.

Cyberpunk web dashboard

The /ui cache matrix: sortable storage inventory with block-level disk usage, tail-f log streaming, and system telemetry. Embedded HTMX, no CDN calls — air-gap ready. Type the mirror's name and see what happens.

Authentication & SSO that fits your stack

Bearer tokens and HTTP Basic by default, plus optional OpenID Connect SSO (Keycloak) for the dashboard — PKCE S256, group gating, HTTP-only session cookies. Protect client pulls with require_auth_pull or keep them open.

Observability out of the box

Prometheus metrics on /metrics — uptime, cache counts, version totals, namespaces, and real block-allocated byte usage at item and version granularity — paired with a ready-made Grafana dashboard (orbitron_v13_current.json) that tracks multiple daemons behind one Prometheus via an Orbitron instance template variable.
Architecture

Ingest once. Sync. Serve forever.

A tightly modelled pipeline — one daemon, three jobs: pull from the upstream world, cache locally, and hand artifacts back over authentic Galaxy endpoints.

Orbitron architecture diagram
  1. 01

    Ingest manifests

    POST YAML role & collection requirements to the management API. Each manifest is content-addressed by SHA-256 and deduplicated on identical re-submission.

  2. 02

    Sync in parallel

    Concurrent workers resolve versions against Galaxy V1/V3, download collection tarballs and shallow-clone git sources (--depth 1). Versions already on disk are never re-fetched.

  3. 03

    Store on disk

    Roles keep unpacked checkout trees; collections are stored as artifacts — both tracked through an atomic access index (.access.json) for pruning and telemetry.

  4. 04

    Serve like Galaxy

    Role downloads are streamed as on-the-fly .tar.gz; collections are served with full V3 metadata. ansible-galaxy sees a normal Galaxy server.

The /ui dashboard

Operations on the bridge of a spaceship

Full-screen, zero-dependency, terminal-themed monitoring built into the binary. Keep an eye on your mirror from a login page that looks like it belongs in a cyberpunk film.

orbitron // authentication
Orbitron cyberpunk web dashboard screenshot
  • Cache Matrix

    Every cached role and collection with sortable TYPE / NAME / VERSION / LAST ACCESS / DISK USAGE columns, a live search filter, and collapsed multi-version rows that unfold on click.

  • Tail-f log stream

    A sliding ▲ LOG STREAM drawer behaves like tail -f — pinned to new lines, releasing when you scroll up. 500-line tail, 2000-line scrollback.

  • System telemetry

    The ◄ SYS METRICS sidebar reports uplink status, cache activity, cache disk usage, mount free space, OS, architecture and last boot. Auto-refreshing.

  • Air-gap friendly

    HTMX is embedded in memory — no external CDN calls. Cookie-based auth, or SSO via Keycloak. Type the mirror’s name for a surprise.

Quickstart

Up and mirroring in minutes

Three ways to run a mirror — bare metal, container, or fully managed by Ansible. Same superpower underneath: a Galaxy endpoint your playbooks already know how to talk to.

Install as a systemd service

Fetch a static binary from the releases page, then --install bootstraps the orbitron user, directories, systemd unit and logrotate in one shot. systemctl reload = hot SIGHUP re-config.

DEPLOYterminalbash
$ sudo ./bin/orbitron --install
$ sudo systemctl status orbitron

# create an admin token (script-friendly: -q)
$ sudo orbitron --generate-token -q

# hot-reload config.yml, no downtime
$ sudo systemctl reload orbitron

Run the GHCR image

Multi-arch image (amd64 + arm64), non-root, git and openssh-client bundled. Config is rendered from ORBITRON_* env vars — no file to mount. Persist your cache on a volume.

DEPLOYorbitron docker composebash
$ docker compose up -d
[+] Running 1/1
 ✔ Container orbitron  Started

$ docker compose logs orbitron | grep "ADMIN TOKEN"
orbitron  | [2026-09-23 08:12:04] ADMIN TOKEN: obt_xxxx...
# also available via ghcr.io/chrisvanmeer/orbitron:latest

Declarative, with the official collection

chrisvanmeer.orbitron covers the full lifecycle from Ansible: install, configure, bootstrap tokens, mirror wait-for-sync and day-two pruning — six HTTP modules plus two roles.

DEPLOYorbitron_daemon.ymlbash
$ ansible-galaxy collection install chrisvanmeer.orbitron
$ ansible-playbook -i hosts orbitron_daemon.yml

# roles:
#   chrisvanmeer.orbitron.orbitron        # install + operate daemon
#   chrisvanmeer.orbitron.orbitron_mirror # declare roles/collections

1 · Feed it

Post a YAML requirements manifest — roles, collections or git-backed sources.

INGESTroles_requirements.ymlyaml
roles:
  - name: geerlingguy.nginx
    version: ">=2.0.0"
  - name: RHEL9-CIS
    src: https://github.com/ansible-lockdown/RHEL9-CIS.git
    version: 2.0.2
APIcurlbash
curl -X POST https://orbitron.internal/api/v1/requirements/roles \
  -H "Authorization: Bearer $ORBITRON_TOKEN" \
  -H "Content-Type: text/yaml" \
  --data-binary @roles_requirements.yml

2 · Consume it

Point ansible-galaxy at the mirror. That is the whole setup.

CLIENTansible.cfgini
[galaxy]
server_list = orbitron

[galaxy_server.orbitron]
url = https://orbitron.internal/api/
# token = YOUR_ORBITRON_TOKEN  # if require_auth_pull
FETCHterminalbash
$ ansible-galaxy role install -r roles_requirements.yml
$ ansible-galaxy collection install -r collections_requirements.yml

Put it behind a reverse proxy. Terminate TLS with Nginx,Traefik or HAProxy in front of the daemon — Orbitron honorsX-Forwarded-Proto / X-Forwarded-For, and ansible.cfgabove talks native HTTPS. Point load balancer health checks at the unauthenticatedGET /healthz (200 when storage is writable). This keeps API calls, tokens and any git credentials in mirrored src URLs encrypted in transit.

HTTP API

Management endpoints that play nice

A small, pragmatic API surface — ingest content, watch syncs, inspect storage and run day-two operations. The Galaxy V1 & V3 serving endpoints live on the same tree.

  • GET/healthz

    Unauthenticated liveness for load balancers & orchestrators — 200 when the storage path is writable, 503 when degraded.

  • POST/api/v1/requirements/roles · /collections

    Ingest role & collection YAML manifests. Content-addressed by SHA-256 and deduplicated; parallel workers mirror on the spot.

  • POSTGET/api/v1/sync · /sync/status

    Re-run every stored manifest, or poll progress and a bounded history of finished syncs.

  • GETDELETE/api/v1/storage

    Full cached-version inventory. DELETE /storage/{type}/{name}/{version} purges a single cached version.

  • POST/api/v1/prune

    Access-based cleanup with retention window — preview exactly what would be deleted with {"dry_run": true}.

  • GETPOSTDELETE/api/v1/tokens

    Token lifecycle: create, list, revoke and rotate admin tokens, with optional per-token TTLs.

  • GET/metrics

    Prometheus exposition — uptime, cache counts, version totals and block-level storage bytes.

Every endpoint above except /healthz requires a Bearer token or HTTP Basic auth.

self-host · cache · serve

Keep automation moving, even offline

Grab the binary, feed it a requirements file, and let your fleet install roles and collections at LAN speed — pinned, cached and yours forever.