Preparing Internal Ansible Roles for Public GitHub Publication

Bottom line

Publishing internal Ansible roles on GitHub is a well-trodden path with mature tooling, but success requires more than pushing code. A structured 4-phase approach - sanitize, restructure, quality-gate, and promote - separates roles that languish at zero stars from those that gain adoption. The technical path is straightforward (ansible-lint + molecule + Galaxy import), but the social path (documentation, community engagement, launch timing) determines whether anyone finds and uses your work. Standalone roles remain the lower-friction starting point despite the community's gradual shift toward collections. Plan for a 30-day launch effort with at least a week of pre-launch quality hardening.

Key findings

  • Security sanitization is the critical first gate: Every fetched source that covers open-sourcing internal code emphasizes removing hardcoded credentials, internal hostnames, and proprietary references before going public. Red Hat's guidance (Source 9) shows externalizing all secrets to environment variables - ANSIBLE_GALAXY_SERVER_<NAME>_TOKEN pattern - and never storing tokens in ansible.cfg. Real-world CVEs (SolarWinds CVE-2024-28987 CVSS 9.1, tj-actions supply chain attack affecting 23K repos) underscore the stakes. A grep for patterns like password:, internal FQDNs, and IP addresses should be run before any push.

  • Galaxy quality scoring directly impacts discoverability: Galaxy scores roles based on yamllint + ansible-lint output plus community surveys (Source 10). Penalties range from -1.0 (VERY_HIGH severity) to -0.075 (VERY_LOW). Incomplete meta/main.yml causes Galaxy to display "No platforms" - a signal that discourages users (Source 2). A role passing ansible-lint --profile production with explicit platform declarations and descriptive galaxy_tags (max 20) will score higher and rank better in search results.

  • Molecule + Docker + CI matrix is the testing standard: The current best practice (Sources 3, 11) uses geerlingguy's pre-built Docker images spanning 7 platforms (Ubuntu 20.04/22.04, Debian 11/12, Rocky 8/9, Amazon Linux 2023), run in parallel via GitHub Actions matrix builds with fail-fast: false. Linting runs as a separate fast-feedback CI job (ansible-lint + yamllint) that completes in seconds. Pre-commit hooks catch issues before they reach CI.

  • README quality and launch timing matter more than code quality for initial traction: The 30-day launch playbook (Source 5) emphasizes: launch Tuesday–Thursday 8–10 AM PT. A "Show HN" post with a founder's comment within 5 minutes; platform-tailored messaging; and responding to all comments within 2 hours. Repository polish - a demo GIF, one-line description, comparison table, and "good first issue" labels - determines whether visitors convert to stargazers. The first 30–60 minutes on Hacker News are the critical window for visibility.

  • Standalone roles vs. Collections: publish standalone now, plan migration later: The community is moving toward collections (Source 13 - Ansible docs officially document the migration path). Standalone roles remain fully supported and are lower friction for initial publishing. Adfinis (Source 15) notes "we generally recommend collections over single-role repositories" while acknowledging no official deprecation of standalone roles. The migration path (ansible-galaxy collection init + copy role to roles/) is well-documented, making it safe to start with standalone roles.

Background

Ansible Galaxy (galaxy.Ansible.Com) is the community hub for sharing Ansible roles and collections. Roles are standardized directory structures that encapsulate reusable automation tasks, handlers, variables, templates, and files. Publishing a role to Galaxy makes it installable with a single command (ansible-galaxy role install namespace.role_name). Galaxy imports roles directly from public GitHub repositories and uses Git tags matching SemVer as version numbers.

The ecosystem has been evolving since ~2020 from standalone roles toward collections - packages that bundle multiple roles, modules, and plugins under a namespace. But standalone roles remain the primary format for single-purpose automation sharing and are the recommended starting point for new publishers.

Current state (2026)

  • Ansible Galaxy is actively maintained on the galaxy NG platform
  • ansible-galaxy role import and GitHub Actions auto-import are the standard publishing workflow
  • Community infrastructure includes Ansible Forum (forum.Ansible.Com), Ansible Bullhorn newsletter (200+ editions), Meetup Pro for global events, and an active subreddit ecosystem
  • Galaxy content scoring uses yamllint + ansible-lint + import-time checks; community surveys augment scores after 3+ responses
  • Molecule 5+ removed built-in lint; linting is now run directly (yamllint + ansible-lint) before molecule test

Technical and implementation details

Phase 1: Security sanitization

  1. Remove hardcoded credentials: Replace any inline passwords, tokens, or API keys with variables that default to empty/placeholder values
  2. Remove internal references: Search for internal hostnames, IPs, URLs, LDAP paths, and company-specific paths; replace with variables or remove
  3. Audit defaults/main.yml: Ensure default values are safe for public exposure - no real credentials, no internal server names
  4. Check files/ and templates/: Binary files and templates may contain embedded internal configuration; review all
  5. Git history audit: Consider whether any previous commits contain sensitive data; use git filter-branch or start fresh if needed
  6. Run secrets scanners: gitleaks or truffleHog against the full Git history

Phase 2: Structural cleanup

Target the community-standard structure (Source 14 - Adfinis):

ansible-role-<name>/
├── defaults/main.yml        # Safe public defaults
├── handlers/main.yml         # Service restarts via notify
├── meta/main.yml             # Galaxy metadata (critical)
├── tasks/
│   ├── main.yml              # Imports only + tags
│   ├── install.yml           # Package installation
│   └── config.yml            # Configuration rendering
├── templates/                # Mirror target filesystem paths
├── vars/
│   ├── Debian.yml            # OS-specific static vars
│   └── RedHat.yml
├── molecule/default/         # Docker-based tests
├── .ansible-lint             # Lint configuration
├── .yamllint                 # YAML style rules
├── .github/workflows/        # CI pipelines
├── README.md
├── LICENSE
├── CONTRIBUTING.md
└── CHANGELOG.md

Key conventions:

  • Role tagging: role::<rolename>:<subtask> (enables --tags role::nginx:install)
  • OS-specific vars loaded via with_first_found: {{ ansible_distribution }}_{{ ansible_distribution_major_version }}.yml{{ ansible_distribution }}.yml{{ ansible_os_family }}.yml
  • Templates include {{ ansible_managed | comment }} header
  • Service restarts via handlers, not conditional task logic
  • Use ansible.builtin. FQCN for all modules

Phase 3: Quality gates

Linting (runs first, takes seconds):

# .ansible-lint
profile: moderate  # Start here, graduate to production
exclude_paths: [.cache/, .github/, molecule/, .venv/]
# .yamllint
extends: default
rules:
  line-length: {max: 160, level: warning}
  truthy: {allowed-values: ['true', 'false', 'yes', 'no']}
  indentation: {spaces: 2, indent-sequences: true}

Pre-commit hooks (catches before commit):

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/ansible/ansible-lint
    rev: v24.10.0
    hooks:
      - id: ansible-lint
  - repo: https://github.com/adrienverge/yamllint
    rev: v1.38.0
    hooks:
      - id: yamllint

CI workflow (GitHub Actions):

# .github/workflows/ci.yml
name: Molecule Test
on: [push, pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ansible/ansible-lint@main
  molecule:
    needs: lint
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        distro: [ubuntu2204, debian12, rocky9]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: {python-version: "3.11"}
      - run: pip install ansible-core molecule molecule-plugins[docker]
      - run: molecule test -- --limit ${{ matrix.distro }}

Galaxy auto-import on tag:

# .github/workflows/galaxy-import.yml
name: Import to Galaxy
on:
  push:
    tags: ['v*']
jobs:
  import:
    runs-on: ubuntu-latest
    steps:
      - run: pip install ansible
      - run: ansible-galaxy role import --token "${{ secrets.GALAXY_API_KEY }}" <github-user> <repo-name>

Phase 4: Galaxy metadata

The meta/main.yml file is what Galaxy reads. Complete every field:

Field Requirement
role_name Name users install (lowercase, underscores)
author Your Galaxy username
description Shows in search results
license MIT or Apache 2.0 recommended
min_ansible_version e.g., "2.14"
platforms List OS + versions you've tested
galaxy_tags Max 20, used for search/categorization
dependencies List or []

Evidence, comparisons, and related context

Standalone roles vs. Collections: Collections are the community direction and required for Red Hat Certified Content. But standalone roles remain easier to publish, discover, and install. Recommendation: publish standalone roles now, migrate to a collection when you have 3+ related roles or custom modules (Sources 2, 13, 15).

Ansible vs. Other IaC tools: Ansible occupies the configuration management + application deployment niche. Terraform/Pulumi focus on infrastructure provisioning. In practice, Ansible roles complement rather than compete with Terraform modules - many teams use both. Position your roles accordingly.

Quality comparison: Roles with CI badges (passing tests) and Galaxy quality scores ≥4.0 signal reliability. Sources 2 and 10 confirm users check these indicators before installing. A role without CI badges will be compared unfavorably to roles that have them.

Limitations and critiques

  • Galaxy scoring documentation gap: The only documented scoring system (Source 10) is from the old Galaxy platform. Current Galaxy NG scoring appears similar but lacks official public documentation. This is a source gap.
  • No internal-to-public case studies found: Despite extensive search, no team has publicly documented their experience open-sourcing internal Ansible roles. The Supabase Ansible stack (Source 13 search result) is the closest example but not a detailed case study.
  • Promotion advice is generic: The 30-day launch plan and growth strategies (Sources 5, 6) are general open-source advice. Ansible-specific promotion metrics are absent.
  • Molecule Docker images from geerlingguy: While these are community standard, they depend on one maintainer. Consider the bus factor.
  • The Galaxy ecosystem is consolidating toward collections: Publishing standalone roles in 2026 is valid but may require eventual migration. This should be factored into long-term maintenance planning.

Open questions

  • What is the current Galaxy NG content scoring algorithm? (Old docs may be outdated)
  • Do Ansible Bullhorn newsletter features drive measurable role adoption?
  • Are there any teams that have documented their experience open-sourcing internal Ansible roles, including metrics before/after?
  • What is the typical conversion rate from Galaxy views to ansible-galaxy install for a new role?
  • How much does the ansible-role- prefix in repo names help or hurt GitHub search discoverability vs. Galaxy normalization?

Practical takeaways

  1. Run the security grep first. Before any refactoring, scan for credentials, internal IPs, and company hostnames. This is the #1 risk when open-sourcing internal code. Use gitleaks on the full Git history.

  2. Start with ansible-galaxy role init and ansible-lint --fix. Generate a fresh skeleton, then port your logic into it rather than trying to clean the existing structure incrementally. Run ansible-lint --fix . to auto-correct formatting issues.

  3. Use the cookiecutter-ansible-role template (idealista) or ansible-galaxy role init to generate a standards-compliant skeleton with molecule tests, CI config, and pre-commit hooks already wired up.

  4. The meta/main.yml is your storefront - fill every field. Galaxy displays "No platforms" and "Empty description" for incomplete metadata, which directly reduces installs. Spend 15 minutes getting this right.

  5. CI badges build trust. A green CI badge and a Galaxy quality score ≥4.0 are the signals users look for before installing an unfamiliar role. Set up GitHub Actions with lint + molecule matrix before your first public push.

  6. Launch on a Tuesday or Wednesday morning (PT). Post a "Show HN" on Hacker News, follow within 5 minutes with a detailed founder's comment. Respond to all feedback within hours. The first 60 minutes determine algorithmic visibility.

  7. Plan for 30 days of sustained effort. Week -1: polish repo and build pre-launch buzz. Launch day: coordinated multi-platform push. Weeks 1-4: respond to feedback, publish weekly tutorials, engage community. Target: 500+ stars, 20+ contributors by day 30.

  8. Consider the collection migration path early. Even if you start with standalone roles, structure them so migration to a collection is straightforward - use FQCN for modules, avoid hyphens in role names, and keep plugins separate from role logic.

Sources used