setup-python action for self-hosted runners running Amazon Linux

setup-python action for self-hosted runners running Amazon Linux

August 28, 2025
12 views
Get tips and best practices from Develeap’s experts in your inbox

Intro

Back in 2022, setup-python Github’s officials have stated they won’t be supporting Amazon Linux 2 or Amazon Linux 2023 systems, at least no for a while. In this article we will create a snippet together to extend the os support of the official action, while having a unified solution to fit most cases (rule of thumb, there will always be an edge case!) without breaking backwards compatibility, using simple logic.

But first, what’s actions/setup-python?

setup-python is one of Github’s official actions, used widely in order to set up a GitHub Actions workflow with a specific version of python, it also includes cacheing capabilities for the popolar tooling.

What’s wrong with installing python on the self-hosted runner?

Even thought it is possible to install python on the host, and it is also ChatGPT best answer, it is an bad pattern. The reason for it is by having python as a pre-requisite for running your workflows.

It can also get significantly difficult when your need different versions for different workflows / applications, and wish to use the same runner.

Shoutout: 🌟 kishaningithub/setup-python-amazon-linux 🌟

Out of that issue a new actions have emerged, meant to complete the puzzle and provide a action the replicated the same principals as the original, just meant for amazon’s most common AMI’s, Amazon Linux 2 & Amazon Linux 2023.

# https://github.com/kishaningithub/setup-python-amazon-linux/blob/main/action.yml
name: 'Setup python amazon linux'
description: 'setup-python action for amazon linux self hosted runners'
inputs:
  python-version:
    description: 'Version of python to be installed. Reads from .python-version if unset.'
  python-version-file:
    description: 'Version of python to be installed'
    default: '.python-version'
  cache:
    description: >
      [Deprecated] Used to specify whether caching is needed. Set to true, if you'd like to enable caching.
      Current implementation uses uv which pulls in pre-compiled binaries of python thereby eliminating the
      need to cache compiled python artifacts.
    default: 'true'

runs:
  using: "composite"
  steps:
    - name: Ensure system dependencies are installed
      shell: bash
      run: |
        sudo yum install -y tar gzip which

    - name: Install uv
      shell: bash
      run: |
        installation_directory="${{ github.action_path }}/.setup-python-amazon-linux/uv"
        echo "Installing uv.. installation_directory=${installation_directory}"
        uv_version="0.7.10"
        # HOME is set to foobar till this is resolved https://github.com/astral-sh/uv/issues/6965#issuecomment-2915796022
        curl -LsSf "https://github.com/astral-sh/uv/releases/download/${uv_version}/uv-installer.sh" | HOME="foobar" UV_UNMANAGED_INSTALL="${installation_directory}" bash --login
        echo "${installation_directory}" >> "${GITHUB_PATH}"

    - name: Find desired python version
      id: find-desired-python-version
      shell: bash
      run: |
        desired_python_version=$(${GITHUB_ACTION_PATH}/find-desired-python-version.sh "${{ inputs.python-version }}" "${{ inputs.python-version-file }}")
        echo "desired_python_version=${desired_python_version}" | tee -a "${GITHUB_OUTPUT}"

    - name: Set installation directory
      id: set-installation-directory
      shell: bash
      run: |
        desired_python_version="${{ steps.find-desired-python-version.outputs.desired_python_version }}"
        echo "installation_directory=${HOME}/.setup-python-amazon-linux/.python-versions/${desired_python_version}" | tee -a "${GITHUB_OUTPUT}"

    - id: setup-python
      shell: bash
      run: |
        installation_directory="${{ steps.set-installation-directory.outputs.installation_directory }}"
        desired_python_version="${{ steps.find-desired-python-version.outputs.desired_python_version }}"

        uv venv --python "${desired_python_version}" "${installation_directory}"

    - name: Add python to PATH
      shell: bash
      run: |
        installation_directory="${{ steps.set-installation-directory.outputs.installation_directory }}"
        echo "${installation_directory}/bin" >> "${GITHUB_PATH}"

        echo "The following python binaries are now available in the PATH"
        ls "${installation_directory}/bin"

    - name: Install pip
      shell: bash
      run: |
        installation_directory="${{ steps.set-installation-directory.outputs.installation_directory }}"

        python -m ensurepip --upgrade
        ln -sf "${installation_directory}/bin/pip3" "${installation_directory}/bin/pip"
        pip install --upgrade pip

branding:
  icon: 'code'
  color: 'yellow'

What’s behind the scenes?

This actions uses the composite approach to leverage yum, which is the package manager for these AMIs, as well as UV, the new kid on the block for managing python packages and projects, and has already taken the hearts of many due to it being “blazingly fast”, and written in rust.
Once uv is installed, the desired python version is quickly calculated using plain bash, and initialized as a virtual environment (venv) in the runner’s action path, aka the path to where the actions is located. It then added this venv path to the GITHUB_PATH which is the runners search path for executing binaries, to make our desired python versions (virtualized!) available for the rest of our job by simply invoking python .

# https://github.com/actions/setup-python/blob/main/action.yml
---
name: "Setup Python"
description: "Set up a specific version of Python and add the command-line tools to the PATH."
author: "GitHub"
inputs:
  python-version:
    description: "Version range or exact version of Python or PyPy to use, using SemVer's version range syntax. Reads from .python-version if unset."
  python-version-file:
    description: "File containing the Python version to use. Example: .python-version"
  cache:
    description: "Used to specify a package manager for caching in the default directory. Supported values: pip, pipenv, poetry."
    required: false
  architecture:
    description: "The target architecture (x86, x64, arm64) of the Python or PyPy interpreter."
  check-latest:
    description: "Set this option if you want the action to check for the latest available version that satisfies the version spec."
    default: false
  token:
    description: "The token used to authenticate when fetching Python distributions from https://github.com/actions/python-versions. When running this action on github.com, the default value is sufficient. When running on GHES, you can pass a personal access token for github.com if you are experiencing rate limiting."
    default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
  cache-dependency-path:
    description: "Used to specify the path to dependency files. Supports wildcards or a list of file names for caching multiple dependencies."
  update-environment:
    description: "Set this option if you want the action to update environment variables."
    default: true
  allow-prereleases:
    description: "When 'true', a version range passed to 'python-version' input will match prerelease versions if no GA versions are found. Only 'x.y' version range is supported for CPython."
    default: false
  freethreaded:
    description: "When 'true', use the freethreaded version of Python."
    default: false
  pip-version:
    description: "Used to specify the version of pip to install with the Python. Supported format: major[.minor][.patch]."
outputs:
  python-version:
    description: "The installed Python or PyPy version. Useful when given a version range as input."
  cache-hit:
    description: "A boolean value to indicate a cache entry was found"
  python-path:
    description: "The absolute path to the Python or PyPy executable."
runs:
  using: 'node20'
  main: 'dist/setup/index.js'
  post: 'dist/cache-save/index.js'
  post-if: success()
branding:
  icon: 'code'
  color: 'yellow'

Key differences from the official action

Other than the obvious os support, a few differences to notice when you consider using this action:

  • Not as well-known and trusted
  • Runs in composite mode, which does not support and prepost / post-if event driven executions
  • Short & simple
  • Easily readable, using plain bash
  • Lack’s caching, among other useful features.
  • Download python versions from a different source then the official.

Summary

I love it when the open-source community takes actions where the corp lacks, especially when the issues are relevant, and could potentielly benifit many people. I could already tell it would be a Large / XL size t-shirt to build Golden AMI’s with pyenv installed and shift workflows to new tooling so I was stocked to find this action, allowing me to focus on other tasks (clearing my backlog amongst them).

So even though it is not yet perfect (and you can help if be!), is get the job done, and most importantly, make our workflows independent, with no prerequisites or special tooling required.

BONUS

Here’s a short but useful snippet which allowed me to set-and-forget and standardize the way we setup python for our workflows:

steps:
  - name: Pre cleanup
    uses: zMynxx/github-actions/blob/main/.github/actions/shared/clean-workspace@main
    with:
      github-workspace: ${{ github.workspace }}

  - name: Checkout
    uses: actions/checkout@v4.2.2
    with:
      fetch-depth: 0

  - name: Check OS
    id: os_check
    shell: bash
    run: |
      set -exu
      cat /etc/os-release
      source /etc/os-release
      echo "os_id=$ID" >> $GITHUB_OUTPUT

  - name: Setup Python on Ubuntu
    if: steps.os_check.outputs.os_id == 'ubuntu'
    uses: actions/setup-python@v4
    with:
      python-version: "3.13"

  - name: Setup Python on Amazon Linux
    if: steps.os_check.outputs.os_id == 'amzn' || steps.os_check.outputs.os_id == 'alinux2023'
    uses: kishaningithub/setup-python-amazon-linux@v1
    with:
      python-version: "3.13"
We’re Hiring!
Develeap is looking for talented DevOps engineers who want to make a difference in the world.
Skip to content