Linux Exporter Role Usage Guide
The linux_exporter role deploys Prometheus node_exporter to Linux hosts, running it as a dedicated node-exp system user instead of root.
This role does not issue or manage TLS certificates itself. Certificate issuance is a separate, deliberate step using playbooks/certificate-authority-issue.yml — the same way every other role in this repo handles certs — rather than something bundled into the install. See the three usage options below.
Requirements
| Requirement | Details |
|---|---|
prometheus.prometheus collection |
Must be installed on the control node (ansible-galaxy collection install prometheus.prometheus). |
certificate_authority role |
Only needed if issuing a certificate (Options B and C below) — not a dependency of linux_exporter itself. |
Tags
A tag must be specified when running this role. Running without --tags or with --tags all is not supported and will result in an error.
| Tag | Includes | Description |
|---|---|---|
install |
create_user, prepare_filesystem, install_node_exporter, verify_endpoint |
Runs all tasks required to install node_exporter. |
create_user |
Creates the node-exp system group/user. |
|
prepare_filesystem |
Creates linux_exporter_config_dir (default /etc/node_exporter). Must be created before a certificate is issued into it or a path under it. |
|
install_node_exporter |
Installs and starts node_exporter via the prometheus.prometheus.node_exporter collection role. |
|
verify_endpoint |
Polls the metrics endpoint after install and asserts node_exporter_build_info is present in the response. Included in install — not a separate manual step. |
Usage
Certificate issuance, when needed, always happens between create_user/prepare_filesystem and install: node_exporter's own preflight checks require the cert/key to already exist on disk before the upstream role will start the service, and the certificate should be owned by node-exp (created in step 1) so node_exporter can actually read it.
The examples below issue the cert/key into linux_exporter_config_dir (/etc/node_exporter) rather than /etc/ssl/{certs,private}. Confirmed against real RHEL 9 and Ubuntu 24.04 hosts: RHEL doesn't ship /etc/ssl/private at all (/etc/ssl there is a symlink tree into /etc/pki/tls with no private entry), so that path only works on Debian/Ubuntu without an extra manual mkdir first. /etc/node_exporter is created by prepare_filesystem regardless of OS, so it always has somewhere to land.
Option A — HTTP only (no TLS)
1. Create host_vars
2. Create the user and config directory
ansible-playbook --limit=<host> playbooks/deploy-node_exporter.yml --tags create_user,prepare_filesystem --become
3. Install
verify_endpoint polls http://localhost:9100/metrics automatically, since linux_exporter_tls_server_config is empty.
Option B — Server-side TLS only (default)
An encrypted listener with no client certificate requirement. This is linux_exporter_tls_server_config's default — no host_vars override is needed as long as the certificate is issued to the default paths (/etc/node_exporter/tls.crt, /etc/node_exporter/tls.priv).
1. Create the user and config directory
ansible-playbook --limit=<host> playbooks/deploy-node_exporter.yml --tags create_user,prepare_filesystem --become
2. Issue the certificate
ansible-playbook playbooks/certificate-authority-issue.yml \
-l <host> \
-e @extra_vars/certificate_authority.yml \
-e certificate_authority_linux_key_path=/etc/node_exporter/tls.priv \
-e certificate_authority_linux_cert_path=/etc/node_exporter/tls.crt \
-e certificate_authority_linux_key_owner=node-exp \
-e certificate_authority_linux_key_group=node-exp \
-e certificate_authority_linux_cert_owner=node-exp \
-e certificate_authority_linux_cert_group=node-exp \
-e 'certificate_authority_cert_sans=[]' \
-e certificate_authority_cert_include_default_names=true \
-e certificate_authority_cert_validity_days=3650 \
--tags generate_key_linux,generate_csr_linux,sign_certificate,write_certificate_linux \
--become
3. Install
Option C — Full mTLS (client certificate required)
1. Create host_vars
# host_vars/<host>.yml
linux_exporter_tls_server_config:
cert_file: /etc/node_exporter/tls.crt
key_file: /etc/node_exporter/tls.priv
client_auth_type: RequireAndVerifyClientCert
client_ca_file: /etc/node_exporter/ca.crt
2. Create the user and config directory
ansible-playbook --limit=<host> playbooks/deploy-node_exporter.yml --tags create_user,prepare_filesystem --become
3. Issue the server certificate
ansible-playbook playbooks/certificate-authority-issue.yml \
-l <host> \
-e @extra_vars/certificate_authority.yml \
-e certificate_authority_linux_key_path=/etc/node_exporter/tls.priv \
-e certificate_authority_linux_cert_path=/etc/node_exporter/tls.crt \
-e certificate_authority_linux_key_owner=node-exp \
-e certificate_authority_linux_key_group=node-exp \
-e certificate_authority_linux_cert_owner=node-exp \
-e certificate_authority_linux_cert_group=node-exp \
-e 'certificate_authority_cert_sans=[]' \
-e certificate_authority_cert_include_default_names=true \
-e certificate_authority_cert_validity_days=3650 \
--tags generate_key_linux,generate_csr_linux,sign_certificate,write_certificate_linux \
--become
certificate_authority_linux_key_owner/_key_group/_cert_owner/_cert_group require certificate_authority at or after commit 91de146 ("Add optional owner/group for Linux cert and key files") — older checkouts of that role won't apply ownership, leaving the key root-owned and unreadable by node_exporter.
4. Write the trusted CA bundle
Nothing in certificate_authority's Linux tasks writes the CA bundle to the target — there's no Linux equivalent of trust_root_ca_windows.yml. Copy it directly with an ad hoc command instead:
ansible <host> -m ansible.builtin.copy \
-a "dest=/etc/node_exporter/ca.crt mode=0644 owner=node-exp group=node-exp content='{{ ca_certificate }}'" \
-e @extra_vars/certificate_authority.yml \
--become
5. Install
Note:
verify_endpointdoesn't supply a client certificate by default, and aRequireAndVerifyClientCertlistener will reject an unauthenticated request — so by defaultverify_endpointjust skips the check under full mTLS (with an explanatory message) rather than failing the install. Setlinux_exporter_test_client_cert/linux_exporter_test_client_keyto a certificate trusted byclient_ca_fileif you wantverify_endpointto actually run and pass under full mTLS — see Variables.
Issuing a client certificate (for Prometheus, or for testing)
Under full mTLS, anything that connects to node_exporter — the actual Prometheus server scraping it, or you testing by hand — needs its own client certificate trusted by client_ca_file. Issue one the same way as the server cert, just with certificate_authority_cert_extended_key_usage: [clientAuth] instead of the default [serverAuth]:
ansible-playbook playbooks/certificate-authority-issue.yml \
-l <prometheus-host> \
-e @extra_vars/certificate_authority.yml \
-e certificate_authority_linux_key_path=/etc/prometheus/certs/node-exporter-client.key \
-e certificate_authority_linux_cert_path=/etc/prometheus/certs/node-exporter-client.crt \
-e 'certificate_authority_cert_extended_key_usage=["clientAuth"]' \
-e 'certificate_authority_cert_sans=["DNS:prometheus"]' \
-e certificate_authority_cert_include_default_names=false \
-e certificate_authority_cert_validity_days=3650 \
--tags generate_key_linux,generate_csr_linux,sign_certificate,write_certificate_linux \
--become
Point Prometheus's scrape_configs tls_config at the resulting cert_file/key_file, plus ca_file set to the same CA bundle written in step 4 above.
Issuing one for yourself, to test by hand: target the control node directly instead of a real inventory host — swap -l <prometheus-host> for -i localhost, -c local:
ansible-playbook playbooks/certificate-authority-issue.yml \
-i localhost, -c local \
-e @extra_vars/certificate_authority.yml \
-e certificate_authority_linux_key_path=/tmp/node-exporter-client.key \
-e certificate_authority_linux_cert_path=/tmp/node-exporter-client.crt \
-e 'certificate_authority_cert_extended_key_usage=["clientAuth"]' \
-e 'certificate_authority_cert_sans=["DNS:ansible-client"]' \
-e certificate_authority_cert_include_default_names=false \
-e certificate_authority_cert_validity_days=3650 \
--tags generate_key_linux,generate_csr_linux,sign_certificate,write_certificate_linux \
--become
--become is still needed even for a /tmp destination — generate_key_linux installs python3-cryptography via the OS package manager first, which needs root regardless of where the key ends up. That leaves the files root-owned, so reclaim them before using them yourself:
Then test directly against a host (swap in its real IP if this box can't resolve its DNS name):
curl -k --cert /tmp/node-exporter-client.crt --key /tmp/node-exporter-client.key https://<host>:9100/metrics
If this host also runs perimeter_auth, the same recipe (with the same paths) is documented in that role's guide — see docs/perimeter-auth/usage-guide.md, step 5.