Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Ansible By James Joyner IV · · 9 min read

Ansible Error Guide: 'target uses selinux but python bindings aren't installed' — Fix

Quick answer

Fix Ansible's 'Aborting, target uses selinux but python bindings (libselinux-python) aren't installed' error: install libselinux for the right interpreter and match SELinux state.

Part of the Ansible Playbook & Module Errors hub
  • #ansible
  • #automation
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

This error means Ansible detected that SELinux is enabled on the target host, but the Python libselinux bindings that let a module read or set SELinux file contexts are missing for the Python interpreter Ansible is using. It typically fires from file-touching modules (copy, template, file, lineinfile) that need to preserve or set an SELinux context, and from the selinux/seboolean modules directly.

The literal message:

fatal: [db-01]: FAILED! => {"changed": false, "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed"}

A closely related variant names the module or the missing library explicitly:

fatal: [db-01]: FAILED! => {"changed": false, "msg": "Failed to import the required Python library (libselinux) on db-01's Python /usr/bin/python3.9. Please read the module documentation and install it in the appropriate location."}

The fix is almost always to install the libselinux Python bindings for the exact interpreter Ansible runs on the target — not just any Python on the box.

Symptoms

  • A copy, template, file, or lineinfile task fails on a Red Hat / CentOS / Rocky / Alma / Fedora host with the libselinux-python message.
  • The ansible.posix.selinux or seboolean module refuses to run.
  • The task works on Ubuntu/Debian targets but fails on RHEL-family hosts.
  • The host has SELinux in enforcing or permissive mode (not disabled).
  • libselinux is installed for the system Python but Ansible is using a different interpreter (a venv, /usr/bin/python3.11, or a custom path).
ansible-playbook -i inventory.ini site.yml --limit db-01
fatal: [db-01]: FAILED! => {"msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed"}

Common Root Causes

1. The bindings are simply not installed

A minimal RHEL-family image ships without the SELinux Python bindings.

ansible db-01 -i inventory.ini -m command -a "rpm -q libselinux python3-libselinux"
package python3-libselinux is not installed

2. Bindings installed for the wrong interpreter

Ansible is configured to use a specific interpreter (venv or newer Python) that lacks the bindings, while the system Python has them.

ansible db-01 -i inventory.ini -m setup -a "filter=ansible_python*"
"ansible_python_interpreter": "/opt/venv/bin/python3.11"

The python3-libselinux RPM installs into /usr/lib64/python3.9/..., not the venv — so the venv interpreter cannot import it.

3. Wrong package name for the Python version

On Python 2 hosts the package is libselinux-python; on Python 3 it is python3-libselinux. Installing the wrong one leaves the active interpreter without bindings.

4. SELinux enabled but you assumed it was off

The host was rebuilt or a base image changed SELinux from disabled to enforcing, so a playbook that never needed the bindings suddenly does.

ansible db-01 -i inventory.ini -m command -a "getenforce"
Enforcing

Diagnostic Workflow

Step 1: Confirm SELinux is actually active on the target

If SELinux is disabled, this error should not appear; if it does, the state changed recently.

ansible db-01 -i inventory.ini -m command -a "getenforce"
Enforcing

Step 2: Identify the exact interpreter Ansible uses

This is the crux — the bindings must exist for this interpreter, not the system default.

ansible db-01 -i inventory.ini -m setup -a "filter=ansible_python_interpreter"
db-01 | SUCCESS => {
    "ansible_facts": { "ansible_python_interpreter": "/usr/bin/python3" }
}

Step 3: Check whether that interpreter can import selinux

Probe the real interpreter directly rather than trusting rpm -q:

ansible db-01 -i inventory.ini -m command -a "/usr/bin/python3 -c 'import selinux; print(selinux.is_selinux_enabled())'"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'selinux'

A traceback here proves the binding is missing for that interpreter.

Step 4: Install the correct bindings with the right package name

Use the package that matches the interpreter’s Python major version. Note the chicken-and-egg: install with dnf/yum (which pre-date the SELinux binding requirement), not with a copy-based task:

- name: Ensure SELinux Python bindings are present
  hosts: rhel_hosts
  become: true
  tasks:
    - name: Install python3 selinux bindings
      ansible.builtin.dnf:
        name:
          - python3-libselinux
          - python3-policycoreutils
        state: present

Step 5: Re-run and verify the import now succeeds

ansible db-01 -i inventory.ini -m command -a "/usr/bin/python3 -c 'import selinux; print(\"ok\")'"
db-01 | CHANGED | rc=0 >>
ok

The original copy/template task now runs and can set file contexts.

Example Root Cause Analysis

A template task that deployed a config file worked on the old fleet but failed on newly provisioned RHEL 9 hosts:

fatal: [app-07]: FAILED! => {"msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed"}

Step 1 confirms SELinux is on:

ansible app-07 -i inventory.ini -m command -a "getenforce"
Enforcing

Step 2 shows Ansible is pointed at a project venv:

"ansible_python_interpreter": "/opt/deploy-venv/bin/python3.11"

Step 3 confirms the venv cannot import selinux, even though the system Python can. The team had installed python3-libselinux via the base image — but into the system /usr/bin/python3.9, while playbooks used the /opt/deploy-venv interpreter that the RPM never touched.

Root cause: the SELinux bindings existed for the wrong interpreter. Because libselinux is a C-extension binding to a system library and is not reliably pip-installable, the durable fix was to point Ansible at the system interpreter for tasks that manage files:

# group_vars/rhel_hosts.yml
ansible_python_interpreter: /usr/bin/python3

with python3-libselinux ensured present. The template task then set the file’s SELinux context correctly and succeeded.

Prevention Best Practices

  • Bake python3-libselinux (and python3-policycoreutils for seboolean) into base images for any RHEL-family host that runs Ansible file modules.
  • Pin ansible_python_interpreter deliberately and make sure the SELinux bindings are installed for that interpreter — venvs do not inherit system C-extension bindings.
  • Add a bootstrap play that installs the bindings with dnf/yum before any copy/template task, so a fresh host self-heals on first run.
  • Match the package to the Python version: python3-libselinux for Python 3, libselinux-python for legacy Python 2 hosts.
  • Do not “fix” the error by disabling SELinux — that trades a one-package install for a real security regression; install the bindings instead.
  • For a fleet where interpreter and SELinux state vary by host, the free incident assistant can help correlate which hosts need the bindings. See more in the Ansible guides.

Quick Command Reference

# Is SELinux actually active?
ansible <host> -i inventory.ini -m command -a "getenforce"

# Which interpreter does Ansible use on the target?
ansible <host> -i inventory.ini -m setup -a "filter=ansible_python_interpreter"

# Can THAT interpreter import selinux?
ansible <host> -i inventory.ini -m command -a "/usr/bin/python3 -c 'import selinux'"

# Install the bindings (Python 3 / RHEL family)
ansible <host> -i inventory.ini -b -m dnf \
  -a "name=python3-libselinux,python3-policycoreutils state=present"

# Pin the interpreter that has the bindings (group_vars):
#   ansible_python_interpreter: /usr/bin/python3

Conclusion

Aborting, target uses selinux but python bindings (libselinux-python) aren't installed means SELinux is enabled on the target but the Python interpreter Ansible uses cannot import the selinux bindings needed to manage file contexts. The usual root causes:

  1. The python3-libselinux package is not installed at all.
  2. It is installed for a different interpreter than the one Ansible uses (common with venvs).
  3. The wrong package name for the target’s Python major version.
  4. SELinux was recently switched to enforcing/permissive on a host you assumed was disabled.

Confirm SELinux is on, find the exact interpreter with setup, prove the import fails for that interpreter, and install the matching libselinux bindings — never by disabling SELinux — to turn the abort into a clean run.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.