Ansible collection: foundata.acmesh

Quelltext

Veröffentlichungen (Releases)

Dokumentation

Probleme (Issues)

Lizenzierung

Sonstiges

Die Ansible-Collection foundata.acmesh stellt Ressourcen zur Verwaltung und Nutzung von acme.sh, einem Shell-basierten Automatic Certificate Management Environment (ACME)-Client. Hiermit lassen sich X.509-Zertifikate ausstellen und verwalten, wie sie typischerweise zur Absicherung von HTTPS-/TLS-/SSL-Diensten auf Webservern eingesetzt werden.

Die folgenden Abschnitte listen die README.md-Dateien der wichtigsten enthaltenen Inhalte (z. B. Rollen) auf. FĂĽr einen umfassenderen Ăśberblick empfiehlt sich ein Blick in die Quellcode-Repositories.

Dieses Projekt nutzt eine andere Arbeitssprache als Deutsch. Der folgende Abschnitt wurde automatisch aus der README-Datei generiert und liegt daher nur in der Originalsprache vor.

Ansible role: foundata.acmesh.run

The foundata.acmesh.run Ansible role (part of the foundata.acmesh Ansible collection). It provides automated management of ACME certificates using acme.sh.

Features

Main features:

  • Dedicated, configurable storage locations for installation, configuration, and certificate files.
  • Dedicated, configurable service user and group (non-root, rootless) for improved security, controlled via the run_acmesh_user and run_acmesh_group variables:
    • All acme.sh operations (account registration, issuance, installation, renewal) run as this unprivileged account, so issuance and renewal behave identically and permission problems surface during the Ansible run instead of weeks later at the first renewal. Privileged ports for standalone/alpn challenges are covered by an automatically granted CAP_NET_BIND_SERVICE capability.
    • By default, other users cannot read certificates managed by acme.sh. See the usage examples for ways to grant access if needed.
    • The reloadcmd of each certificate runs as root via a systemd path unit watching the installed certificate files, so services get reloaded reliably after issuance and renewal without granting the service user any privileges. See the usage examples for details.
  • Staged certificate installation (default since version 4.0.0): acme.sh installs into a root-protected staging area and a root-owned, sandboxed systemd service validates and publishes the files to their final destinations. This supports destinations below protected directories such as /etc/ssl/private and full ownership and mode control per certificate file via install.permissions, while acme.sh itself keeps running unprivileged. See Installation strategies for details and the direct compatibility strategy.
  • Automatic certificate renewal via systemd timer.
  • Support for multiple ACME challenge types: alpn, dns (including alias mode), standalone, and webroot
  • Global acme.sh shell alias for easy execution.
  • Uploading of pre-seeded certificate files before issuing new ones: Helps to prevent hitting CA rate limits, especially when frequently reinstalling target systems during development.

Currently not supported:

  • The acme.sh notify functionality.
  • Native handling of the Apache and NGINX modes (but you can use extra_flags to pass --nginx or --apache if really necessary).

Example playbooks, using this role

Webroot challenge (single domain)

Using only one domain per certificate and the webroot challenge. acme.sh runs as the unprivileged service user defined by run_acmesh_user (defaults to acmesh), which therefore needs write access to the webroot (it creates .well-known/acme-challenge below it), e.g. via group membership or a pre-created, service-user owned .well-known/acme-challenge directory:

---

- name: "Demo of the foundata.acmesh.run role"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.acmesh.run role"
      ansible.builtin.include_role:
        name: "foundata.acmesh.run"
      vars:
        run_acmesh_autoupgrade: true
        run_acmesh_cfg_accountemail: "hostmaster@example.org"
        run_acmesh_certs:
          - domains:
              - name: "example.org"
                challenge:  # parameters depend on type
                  type: "webroot"
                  webroot: "/var/www/example.org"
            install:
              ca_file: "/etc/pki/tls/certs/example.org/ca.cer"
              cert_file: "/etc/pki/tls/certs/example.org/cert.cer"
              fullchain_file: "/etc/pki/tls/certs/example.org/fullchain.cer"
              key_file: "/etc/pki/tls/certs/example.org/cert.key"
              reloadcmd: "systemctl reload apache2.service" # executed as root by a systemd unit, see the "Service reloads" section below
            server: "letsencrypt_test" # optional, CA alias or URL, defaults to "letsencrypt" see https://github.com/acmesh-official/acme.sh/wiki/Server for details.

The role clones acme.sh from GitHub by default. Use the run_acmesh_git_url parameter to point it at an internal Git mirror instead (e.g. for air-gapped environments).

DNS challenge (multiple domains and certificates)

Multiple domains per certificate with DNS challenge and challenge alias:

---

- name: "Demo of the foundata.acmesh.run role (multiple domains, DNS challenge)"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.acmesh.run role"
      ansible.builtin.include_role:
        name: "foundata.acmesh.run"
      vars:
        run_acmesh_autoupgrade: true
        run_acmesh_cfg_accountemail: "hostmaster@example.org"
        run_acmesh_certs:
          # first certificate: "example.org"
          - domains:
              - name: "example.org"
                challenge:
                  type: "webroot"
                  webroot: "/var/www/example.org"
            install:
              ca_file: "/etc/pki/tls/certs/example.org/ca.cer"
              cert_file: "/etc/pki/tls/certs/example.org/cert.cer"
              fullchain_file: "/etc/pki/tls/certs/example.org/fullchain.cer"
              key_file: "/etc/pki/tls/certs/example.org/cert.key"
              reloadcmd: "systemctl reload apache2.service" # executed as root by a systemd unit, see the "Service reloads" section
            # optional, CA alias or URL, defaults to "letsencrypt" see
            # https://github.com/acmesh-official/acme.sh/wiki/Server
            server: "zerossl"

          # second certificate: "foo.example.com" with an additional "bar.example.com" SAN
          - domains:
              - name: "foo.example.com"
                challenge:  # parameters depend on type
                  type: "dns"
                  dns_provider: "dns_hetzner"
                  # CNAME _acme-challenge.foo.example.com => _acme-challenge.foo.example.com.example.net
                  challenge_alias: "foo.example.com.example.net"
              - name: "bar.example.com"
                challenge:
                  type: "dns"
                  dns_provider: "dns_inwx"
                  # CNAME _acme-challenge.bar.example.com => _acme-challenge.example.net
                  challenge_alias: "example.net"
            install:
              ca_file: "/etc/pki/tls/certs/foo.example.com/ca.cer"
              cert_file: "/etc/pki/tls/certs/foo.example.com/cert.cer"
              fullchain_file: "/etc/pki/tls/certs/foo.example.com/fullchain.cer"
              key_file: "/etc/pki/tls/certs/foo.example.com/cert.key"
              reloadcmd: "systemctl reload nginx.service; systemctl restart postfix.service"
            # "{letsencrypt,buypass,google}_test" for staging, see
            # https://github.com/acmesh-official/acme.sh/wiki/Server
            server: "letsencrypt"
            force: false  # optional
            debug: false # optional
            post_hook: ""  # optional
            renew_hook: "" # optional
            extra_flags: "" # optional (workaround for edge cases, put --whatever here)

        # Environment variables needed for the DNS API authentication for
        # type: "dns" and dns_provider: "dns_hetzner" /  dns_provider: "dns_inwx"
        run_acmesh_environment:
          HETZNER_Token: "{{ lookup('ansible.builtin.unvault', '...') | ansible.builtin.string | ansible.builtin.trim }}"
          INWX_User: "exampleuser"
          INWX_Password: "{{ lookup('ansible.builtin.unvault', '...') | ansible.builtin.string | ansible.builtin.trim }}"

Installation strategies, ownership and permissions

Since version 4.0.0, every certificate’s install supports a strategy (default: staged) and partial permissions overrides:

  • staged (the default, also used when strategy is omitted): acme.sh installs into an internal, root-protected staging area (one service-user writable directory per certificate below run_acmesh_cfg_cert_deploy_base_dir). A root-owned, heavily sandboxed systemd service (acmesh-deploy-<primary domain>.service, triggered by a path unit watching the staged files and started synchronously during the Ansible run) validates the staged files and publishes them to the final destinations with the configured ownership and modes. Because root performs the final copy, destinations below protected parent directories such as /etc/ssl/private (commonly root:ssl-cert with mode 0710) work without granting the service user any additional group memberships.
  • direct: the pre-4.0.0 behavior; acme.sh writes the final paths directly as the unprivileged service user. This is a compatibility option, not the recommended default: every destination must be writable by run_acmesh_user — also during unattended renewal — and ownership overrides are rejected (the service account could not maintain them). Mode overrides are allowed (renewals preserve the mode because acme.sh truncates files in place).

The deployment chain of the staged strategy is an extension of the reload mechanism:

renewal (unprivileged acme.sh) → staging files
  → acmesh-deploy-<id>.path → acmesh-deploy-<id>.service (root helper, validates + publishes)
  → final files (completion artifact last)
  → acmesh-reload-<id>.path → reloadcmd (root)

The helper (/usr/libexec/acmesh-deploy) validates all staged input before publishing anything: PEM parsing, private key/certificate match, certificate domain coverage, size limits, rejection of symlinks and non-regular files. Ownership and modes are applied before atomic renames, the artifact acme.sh writes last is published last (so the reload only fires on complete sets), unchanged files are not replaced, and metadata-only reconciliation does not trigger reloads. On a publish failure the already published artifacts of that run are restored best-effort. Remaining limitation: a hard system crash while publishing files that live on different filesystems can leave a mixed set — a fully atomic transaction across filesystems is not possible.

install.permissions is a set of partial overrides. Effective values are calculated per key with later mappings overriding earlier ones (unspecified keys inherit):

  1. Role defaults: directory run_acmesh_user:run_acmesh_group mode "2750"; files run_acmesh_user:run_acmesh_group mode "0640" (unchanged from 3.x).
  2. permissions.default (all certificate files).
  3. permissions.<artifact> (ca_file, cert_file, fullchain_file, key_file).

Example (key below /etc/ssl/private, readable by ssl-cert, the fullchain world-readable):

run_acmesh_certs:
  - domains:
      - name: "example.org"
        challenge:
          type: "webroot"
          webroot: "/var/www/example.org"
    install:
      strategy: "staged" # the default, shown for clarity
      key_file: "/etc/ssl/private/example.org/cert.key"
      fullchain_file: "/etc/ssl/certs/example.org/fullchain.cer"
      reloadcmd: "systemctl reload nginx.service"
      permissions:
        directory:
          owner: "root"
          group: "ssl-cert"
          mode: "0750"
        default:
          owner: "root"
          group: "ssl-cert"
          mode: "0640"
        fullchain_file:
          mode: "0644"

Notes:

  • Modes are quoted octal strings. Certificate files must not be executable or carry setuid/setgid/sticky bits; private keys must not be world-readable or group/world-writable ("0600", "0640", "0400", "0440" are typical values); directories may keep the setgid bit (default "2750").
  • Only the immediate destination directories are managed; existing ancestors such as /etc/ssl or /etc/ssl/private are never changed.
  • The role reconciles the final files and their metadata on every run. Playbooks which change certificate ownership or modes after applying the role must move those requirements into install.permissions — the role may overwrite such external changes.
  • Troubleshooting: a failed deployment leaves acmesh-deploy-<primary domain>.service in a failed state (systemctl --failed); the helper logs concisely to the journal (journalctl -t acmesh-deploy, never private key material). systemctl cat acmesh-deploy-<primary domain>.service shows the unit, systemd-analyze verify checks it. The helper reuses the Python interpreter Ansible discovered on the host; if that interpreter is removed, deployments fail visibly until the next role run rewrites the units.

Certificate file access for other services

The preferred way to grant services access to certificate files is the staged installation strategy with explicit install.permissions (e.g. group: "ssl-cert" plus a fitting mode), as shown above.

Alternatively — and as the only option for the direct strategy — add specific service users (e.g., www-data or nginx) to the group defined by run_acmesh_group (defaults to acmesh). ansible.builtin.user can help you with that:

- name: "Grant the webserver's service user read access to certs by adding it to the acmesh group"
  ansible.builtin.user:
    name: "www-data"
    groups:
      - "acmesh" # the groupname get set via run_acmesh_group role variable, defaults to "acmesh"
    append: true # do not remove existing group memberships

The reverse direction also works: run_acmesh_user_supplementary_groups adds the service user to existing groups (e.g. ssl-cert for direct installations below /etc/ssl/private, or a webroot group). ⚠️ Note that privileged groups such as ssl-cert may grant the service user read access to unrelated private keys; with the staged strategy this should not be needed merely to deploy a certificate.

Service reloads after certificate changes (reloadcmd)

The reloadcmd of a certificate is not executed by acme.sh. Instead, the role creates a systemd path unit (acmesh-reload-<primary domain>.path) which watches the installed certificate files and starts a companion service unit that runs the reloadcmd as root whenever the files change:

  • No sudo or polkit setup is needed; plain commands like reloadcmd: "systemctl reload nginx.service" just work. The unprivileged service user defined by run_acmesh_user does not get any privileges: it only writes the certificate files, root reacts to the change.
  • The behavior is identical no matter how the certificate files change: initial issuance through this role, automated renewal (acmesh-renewal.service, which runs unprivileged), manual acme.sh runs, or restoring pre-seeded files.
  • A failing reloadcmd leaves acmesh-reload-<primary domain>.service in a failed state, visible via systemctl --failed and the journal (journalctl -t acmesh-reload), instead of being silently swallowed during renewal.
  • The command is inlined into the service unit’s ExecStart= (systemd-escaped so the shell receives it exactly as configured, single line only); systemctl cat acmesh-reload-<primary domain>.service shows it.
  • The reload happens asynchronously (typically within milliseconds) after the certificate files were written, not synchronously as part of the Ansible task.

Reload units of certificates that are no longer listed in run_acmesh_certs are kept by default, as acme.sh continues to renew and reinstall already issued certificates until they are actively removed. Remove a certificate cleanly with a state: "absent" entry, or enable run_acmesh_certs_delete_unmanaged to reap such leftovers (see Removing certificates). All acmesh-reload-* resources get removed on run_acmesh_state: "absent".

The pre_hook, post_hook and renew_hook commands are different: they are executed by acme.sh itself and therefore always run as the unprivileged service user — consistently during issuance and automated renewal. Keep them free of privileged operations (or grant the needed permissions yourself, e.g. via community.general.sudoers, foundata.linux.sudo or a polkit rule).

dns-persist-01 challenge (long-lived TXT record)

A dns-persist-01 challenge, for a domain whose DNS you cannot (or do not want to) automate with an API plugin. Instead of a per-issue _acme-challenge TXT record, it uses a single, long-lived _validation-persist.<domain> TXT record bound to your ACME account key, so you publish the record once and renewals need no further DNS changes:

---

- name: "Demo of the foundata.acmesh.run role (dns-persist-01 challenge)"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.acmesh.run role"
      ansible.builtin.include_role:
        name: "foundata.acmesh.run"
      vars:
        run_acmesh_autoupgrade: true
        run_acmesh_cfg_accountemail: "hostmaster@example.org"

        # The dns-persist-01 TXT record value is derived from the ACME account
        # key. Pre-seed that key so the same long-lived record keeps working
        # across reinstalls and on every server that shares this certificate.
        # The server must match run_acmesh_certs[].server below.
        run_acmesh_cfg_account_keys:
          - server: "letsencrypt"
            account_key: "{{ lookup('ansible.builtin.unvault', 'files/acmesh_account_key_letsencrypt.pem') }}"

        # Seconds to wait after the role prints the TXT record instructions,
        # giving you time to publish the record. Set to 0 in CI or when the
        # record is already in place.
        run_acmesh_dns_persist_pause: 600

        run_acmesh_certs:
          - domains:
              - name: "example.org"
                challenge:  # parameters depend on type
                  type: "dns_persist"
                  dns_persist_wildcard: true  # optional, also authorize *.example.org
                  # dns_persist_ca_name: "letsencrypt.org"  # optional, override the CA identity in the record value
                  # dns_persist_days: 365  # optional, adds a "persistUntil" hint to the record
            install:
              ca_file: "/etc/pki/tls/certs/example.org/ca.cer"
              cert_file: "/etc/pki/tls/certs/example.org/cert.cer"
              fullchain_file: "/etc/pki/tls/certs/example.org/fullchain.cer"
              key_file: "/etc/pki/tls/certs/example.org/cert.key"
              reloadcmd: "systemctl reload apache2.service" # executed as root by a systemd unit, see the "Service reloads" section
            # must match the server of the pre-seeded account key above
            server: "letsencrypt"

On the first run the role prints the exact _validation-persist.<domain> TXT record(s) to create and then pauses for run_acmesh_dns_persist_pause seconds. Publish the record at your DNS provider, then let the play continue (or re-run it later). Because the record value is derived from the ACME account key, pre-seeding that key with run_acmesh_cfg_account_keys keeps the same TXT record valid across reinstalls and across multiple servers sharing the certificate. See the acme.sh DNS-persist-mode wiki for background.

Removing certificates

Set state: "absent" on a certificate entry to remove all of its data: the acme.sh data below run_acmesh_cfg_cert_home (this also stops the automated renewal for it), its acmesh-reload-* and acmesh-deploy-* systemd units, its staging data and deployment manifest, and the installed files listed under install (their directories are kept, as they may be shared with other software). The certificate is not revoked at the ACME CA:

run_acmesh_certs:
  - domains:
      - name: "example.org"
        challenge:
          type: "webroot"
          webroot: "/var/www/example.org"
    install:
      ca_file: "/etc/pki/tls/certs/example.org/ca.cer"
      cert_file: "/etc/pki/tls/certs/example.org/cert.cer"
      fullchain_file: "/etc/pki/tls/certs/example.org/fullchain.cer"
      key_file: "/etc/pki/tls/certs/example.org/cert.key"
    state: "absent"

Certificates that are simply deleted from run_acmesh_certs keep their data and continue to be renewed (and, with the staged strategy, deployed) by the existing units. Set run_acmesh_certs_delete_unmanaged: true to reap such unmanaged leftovers declaratively (acme.sh data, reload units, deploy units, staging directories and manifests; installed files of unmanaged certificates are never touched because their paths are unknown to the role).

A full uninstall via run_acmesh_state: "absent" intentionally preserves all certificate data (see below).

Uninstall

Uninstall (certificate files, if present, will be preserved):

---

- name: "Demo of the foundata.acmesh.run role (removal, uninstall)"
  hosts: localhost
  gather_facts: false
  tasks:

    - name: "Trigger invocation of the foundata.acmesh.run role"
      ansible.builtin.include_role:
        name: "foundata.acmesh.run"
      vars:
        run_acmesh_state: "absent"

Pre-seeding certificate files

This role supports uploading backed-up acme.sh certificate folders from the Ansible control node to target systems before issuing new certificates. Files are only transferred if they do not already exist on the target, preventing the accidental overwrite of up-to-date certificates. This feature helps avoid CA rate limits, especially when frequently reinstalling target systems during development.

Usage:

  1. Back up a certificate directory such as www.example.com_ecc or example.org_rsa from acme.sh’s certificate home (run_acmesh_cfg_cert_home, default: /var/opt/acme.sh).
  2. Place the backup under files/acme.sh/certhome in your playbook directory on the control node.

Migration from 3.x and downgrades

Version 4.0.0 changes the default installation strategy to staged:

  • The first run may report changed for an existing installation. It migrates the acme.sh installation paths (Le_Real*Path) to the staging area, creates the staged deployment resources (staging directories, helper, manifests, acmesh-deploy-* units) and reconciles the final files and their metadata. Subsequent runs are idempotent when certificate content, configuration, strategy, destinations and permissions are unchanged.
  • Move post-role permission fixes into install.permissions. The role now reconciles final certificate ownership and modes on every run and may overwrite external changes (chmod/chown tasks after the role, relocation workarounds). Supplementary ssl-cert group membership that was only needed for direct installation below /etc/ssl/private can usually be removed as well.
  • Keeping the old behavior: set strategy: "direct" per certificate. Switching a certificate between the strategies is supported in both directions; the role re-points acme.sh and creates or removes the staged deployment resources accordingly.
  • Downgrading to 3.x after the migration requires an explicit direct reinstallation: 3.x does not understand the staging paths, so renewals would keep updating the staging area while the final destinations go stale. Re-run the 3.x role (its --install-cert re-points acme.sh at the final paths) and remove the acmesh-deploy-* units, /etc/acmesh-deploy.d, /usr/libexec/acmesh-deploy and the staging base directory (default /var/opt/acme.sh-deploy) manually.

Supported tags

It might be useful and faster to only call parts of the role by using tags:

  • run_acmesh_setup: Manage basic resources, such as packages or service users.
  • run_acmesh_config: Manage settings, such as adapting or creating configuration files.
  • run_acmesh_cert: Manage and issue certificates.

There are also tags that are generally not intended to be called directly but are included for completeness and to cover edge cases:

  • run_acmesh_always, always: Tasks needed by the role itself for internal role setup and the Ansible environment.

Role variables

Main entry point for the foundata.acmesh.run role

The following variables can be configured for this role:

VariableTypeRequiredDefaultDescription (abstract)
run_acmesh_statestrNo"present"Determines whether the managed resources should be present or absent.

present ensures that required components, such as software packages, are installed and configured.

absent reverts changes as much as possible, such as …
run_acmesh_autoupgradeboolNofalseIf set to true, all managed packages will be upgraded during each Ansible run (e.g., when the package provider detects a newer version than the currently installed one).
run_acmesh_autorenewalboolNotrueEnables daily automatic certificate renewal via systemd timer (this role is not using acme.sh’s cronjob function).
run_acmesh_environment đź”’dictNo{}Defines environment variables required for ACME DNS challenges.

This is typically needed for DNS challenge plugins, such as those requiring DNS API credentials (e.g., HETZNER_Token, INWX_User, INWX_Password). Multiple variables can be …
run_acmesh_git_urlstrNo"https://github.com/acmesh-official/acme.sh.git"The Git repository URL for acme.sh. The role uses this URL to clone the source code during installation or updates and to query available version tags via git ls-remote.

Can be set to an internal Git mirror for air-gapped environments or to …
run_acmesh_git_fallback_version_branchstrNo"master"The Git branch to clone when no version tag could be determined from the remote repository (e.g. because git ls-remote failed or returned no matching tags).

See https://github.com/acmesh-official/acme.sh/issues/1162 for why acme.sh uses …
run_acmesh_git_versionstrNo""Overrides the automatically detected acme.sh version with a specific Git ref (tag, branch, or commit hash). When set (non-empty string), the role skips the upstream version tag detection via git ls-remote and uses this value directly for the …
run_acmesh_certslistNo[]Defines certificates to be requested, their associated domains, challenge methods, and installation details. Each item in the list is a dictionary with suboptions / keys.

Example:

``` run_acmesh_certs: # first certificate: “example.org” …
run_acmesh_certs_delete_unmanagedboolNofalseIf set to true, certificate data below run_acmesh_cfg_cert_home that does not belong to any certificate listed in run_acmesh_certs gets removed (which also stops the automated renewal for it), as do acmesh-reload-* and acmesh-deploy-* …
run_acmesh_userstrNo"acmesh"Specifies the service user account that runs acme.sh and owns relevant files and directories.

All acme.sh operations run as this account: account registration, certificate issuance and installation during the Ansible run as well as the …
run_acmesh_groupstrNo"acmesh"Specifies the group associated with the service user for managing acme.sh and the corresponding file permissions.
run_acmesh_user_supplementary_groupslistNo[]Exact list of supplementary groups for the service user account (run_acmesh_user) beyond run_acmesh_group. The role manages the account’s supplementary groups as the exact unique union of run_acmesh_group and this list; group memberships added …
run_acmesh_cfg_accountemailstrNo""Specifies the email address to be associated with the ACME account. This email is used for expiration notices and recovery purposes. Some ACME providers might refuse to issue certificates if not set.
run_acmesh_cfg_homestrNo"/opt/acme.sh"Specifies the installation directory for the acme.sh software (relates to acme.sh option –home). Will also be used as home directory of the service user defined (see run_acmesh_user).
run_acmesh_cfg_config_homestrNo"/etc/acme.sh"Defines where configuration files are stored (relates to acme.sh option –config-home).
run_acmesh_cfg_cert_homestrNo"/var/opt/acme.sh"Specifies the directory where certificates are maintained by acme.sh (relates to acme.sh option –certhome). ⚠️ Do not rely on the files in this directory directly. Instead, copy the certificates where needed using the “install” key of …
run_acmesh_cfg_cert_deploy_base_dirstrNo"/var/opt/acme.sh-deploy"Base directory for the internal staging areas of certificates using the staged installation strategy. The directory itself is root-owned and holds one subdirectory per certificate which is writable by the service user: acme.sh installs into that …
run_acmesh_cfg_logfilestrNo"/var/log/acme.sh.log"Path to the log file where acme.sh logs its operations (relates to acme.sh option –log).
run_acmesh_cfg_log_levelintNo1Specifies the log level (relates to acme.sh option –log-level). Possible values are 1 (less logging) and 2 (more logging).
run_acmesh_cfg_syslogintNo3Specifies what to log (relates to acme.sh option –syslog). Possible values are 0 (disable syslog), 3 (errors), 6 (info) and 7 (debug)
run_acmesh_cfg_ca_bundlestrNo""Path to a custom CA certificate bundle file on the remote host. Passed as --ca-bundle to all acme.sh commands that make HTTPS requests (issue, register-account). Needed when the ACME server uses a certificate signed by a private or non-standard …
run_acmesh_cfg_ca_pathstrNo""Path to a directory of CA certificates in PEM format on the remote host. Passed as --ca-path to all acme.sh commands that make HTTPS requests. Alternative to run_acmesh_cfg_ca_bundle when the HTTP client (curl/wget) requires a directory of …
run_acmesh_cfg_account_keyslistNo[]List of ACME account keys to pre-seed. This is useful for dns-persist-01 challenges where the TXT record is bound to a specific account key, and you want to reuse the same key across multiple servers or after re-installations.

Each item must …
run_acmesh_dns_persist_pauseintNo600Controls the pause duration (in seconds) after displaying dns-persist-01 TXT record instructions. Set to 0 to disable the pause (useful for CI/CD pipelines or environments where TXT records are guaranteed to already be published).

If the …

run_acmesh_state

⇑ Back to ToC ⇑

Determines whether the managed resources should be present or absent.

present ensures that required components, such as software packages, are installed and configured.

absent reverts changes as much as possible, such as removing packages, deleting created users, stopping services, restoring modified settings, …

  • Type: str
  • Required: No
  • Default: "present"
  • Choices: present, absent

run_acmesh_autoupgrade

⇑ Back to ToC ⇑

If set to true, all managed packages will be upgraded during each Ansible run (e.g., when the package provider detects a newer version than the currently installed one).

  • Type: bool
  • Required: No
  • Default: false

run_acmesh_autorenewal

⇑ Back to ToC ⇑

Enables daily automatic certificate renewal via systemd timer (this role is not using acme.sh’s cronjob function).

  • Type: bool
  • Required: No
  • Default: true

run_acmesh_environment

⇑ Back to ToC ⇑

Defines environment variables required for ACME DNS challenges.

This is typically needed for DNS challenge plugins, such as those requiring DNS API credentials (e.g., HETZNER_Token, INWX_User, INWX_Password). Multiple variables can be defined in parallel to support different providers for different domains on the same server. For more details on acme.sh’s DNS API support see https://github.com/acmesh-official/acme.sh/wiki/dnsapi

Example:

run_acmesh_environment:
  "HETZNER_Token": "\{\{ lookup('ansible.builtin.unvault', '...') | ansible.builtin.string | ansible.builtin.trim \}\}"
  • Type: dict
  • Required: No
  • Sensitive: Yes (no_log, values are masked in logs)
  • Default: {}

run_acmesh_git_url

⇑ Back to ToC ⇑

The Git repository URL for acme.sh. The role uses this URL to clone the source code during installation or updates and to query available version tags via git ls-remote.

Can be set to an internal Git mirror for air-gapped environments or to avoid GitHub API rate limits.

  • Type: str
  • Required: No
  • Default: "https://github.com/acmesh-official/acme.sh.git"

run_acmesh_git_fallback_version_branch

⇑ Back to ToC ⇑

The Git branch to clone when no version tag could be determined from the remote repository (e.g. because git ls-remote failed or returned no matching tags).

See https://github.com/acmesh-official/acme.sh/issues/1162 for why acme.sh uses master as the branch for the latest release.

  • Type: str
  • Required: No
  • Default: "master"

run_acmesh_git_version

⇑ Back to ToC ⇑

Overrides the automatically detected acme.sh version with a specific Git ref (tag, branch, or commit hash). When set (non-empty string), the role skips the upstream version tag detection via git ls-remote and uses this value directly for the git clone operation.

This is useful when the latest tagged release does not yet include a feature you need (setting to master is usually safe as acme.sh aims for a production-ready master branch by default), or when you want to pin a specific version for reproducibility.

When set, the upgrade task also respects this value and skips the normal semver comparison that gates upgrades.

  • Type: str
  • Required: No
  • Default: ""

run_acmesh_certs

⇑ Back to ToC ⇑

Defines certificates to be requested, their associated domains, challenge methods, and installation details. Each item in the list is a dictionary with suboptions / keys.

Example:

run_acmesh_certs:
  ## first certificate: "example.org"
  - domains:
      - name: "example.org"
        challenge: # parameters depend on type
          type: "webroot"
          webroot: "/var/www/example.org"
    install:
      ca_file: "/etc/pki/tls/certs/example.org/ca.cer"
      cert_file: "/etc/pki/tls/certs/example.org/cert.cer"
      fullchain_file: "/etc/pki/tls/certs/example.org/fullchain.cer"
      key_file: "/etc/pki/tls/certs/example.org/cert.key"
      # Executed as root by a systemd unit whenever the installed
      # certificate files change (see the README for details).
      reloadcmd: "systemctl reload apache2.service"
  ## second certificate: "foo.example.com" with an additional "bar.example.com" SAN
  - domains:
      - name: "foo.example.com"
        challenge: # parameters depend on type
          type: "dns"
          dns_provider: "dns_hetzner"
          challenge_alias: "foo.example.com.example.net"
      - name: "bar.example.com"
        challenge:
          type: "dns"
          dns_provider: "dns_inwx"
          challenge_alias: "bar.example.com.example.net"
    install:
      ca_file: "/etc/pki/tls/certs/foo.example.com/ca.cer"
      cert_file: "/etc/pki/tls/certs/foo.example.com/cert.cer"
      fullchain_file: "/etc/pki/tls/certs/foo.example.com/fullchain.cer"
      key_file: "/etc/pki/tls/certs/foo.example.com/cert.key"
      reloadcmd: "systemctl reload nginx.service; systemctl restart postfix.service"
    # optional, CA alias or URL, defaults to "letsencrypt". "{letsencrypt,buypass,google}_test"
    # for staging, see https://github.com/acmesh-official/acme.sh/wiki/Server for details.
    server: "zerossl"
    force: false  # optional
    debug: false # optional
    post_hook: ""  # optional
    renew_hook: "" # optional
    extra_flags: "" # optional (workaround for edge cases, put --whatever in here, used during issuing a cert")

Environment variables needed for the DNS API authentication for

type: "dns" and dns_provider: "dns_hetzner" / dns_provider: "dns_inwx"

run_acmesh_environment: HETZNER_Token: "{{ lookup('ansible.builtin.unvault', '…') | ansible.builtin.string | ansible.builtin.trim }}" INWX_User: "exampleuser" INWX_Password: "{{ lookup('ansible.builtin.unvault', '…') | ansible.builtin.string | ansible.builtin.trim }}"

  • Type: list
  • Required: No
  • Default: []
  • List Elements: dict
run_acmesh_certs['domains']

⇑ Back to ToC ⇑

List of dictionaries specifying the domains to be included in the certificate request, along with their challenge configurations. If multiple domains are defined, they will be included in the same certificate as Subject Alternative Names (SANs).

The first entry is the certificate’s primary domain and identity: it names the acme.sh storage directory and the acmesh-reload-* systemd units, so it must be unique across all run_acmesh_certs entries (the role fails fast on duplicates).

  • Type: list
  • Required: Yes
  • List Elements: dict
run_acmesh_certs['domains']['name']

⇑ Back to ToC ⇑

The domain name to request a certificate for.

  • Type: str
  • Required: Yes
run_acmesh_certs['domains']['challenge']

⇑ Back to ToC ⇑

Dictionary defining the ACME challenge parameters. The required parameters depend on the challenge type

  • Type: dict
  • Required: No
run_acmesh_certs['domains']['challenge']['type']

⇑ Back to ToC ⇑

Challenge method. See https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert for details.

  • Type: str
  • Required: No
  • Choices: alpn, dns, dns_persist, standalone, webroot
run_acmesh_certs['domains']['challenge']['dns_provider']

⇑ Back to ToC ⇑

Optional. Required for “dns” challenges. Specifies the DNS provider for API-based verification. See https://github.com/acmesh-official/acme.sh/wiki/dnsapi for supported providers and their name. You usually have to provide credentials for their APIs via the global run_acmesh_environment variable or the per-certificate environment key.

  • Type: str
  • Required: No
run_acmesh_certs['domains']['challenge']['challenge_alias']

⇑ Back to ToC ⇑

Optional. Used with “dns” challenges when using CNAME delegation. Requires a CNAME record such as: _acme-challenge.example.com -> _acme-challenge.example.com.example.net See https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode for details.

  • Type: str
  • Required: No
run_acmesh_certs['domains']['challenge']['domain_alias']

⇑ Back to ToC ⇑

Optional. Used with “dns” challenges as an alternative to challenge_alias. Instead of CNAME delegation, the alias domain is set directly as the domain to use for the DNS challenge validation. See https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode for details.

  • Type: str
  • Required: No
run_acmesh_certs['domains']['challenge']['webroot']

⇑ Back to ToC ⇑

Optional. Required for “webroot” challenges. Specifies the directory where the ACME challenge response should be placed (e.g. the document root of the web server serving the domain). The service user defined by run_acmesh_user needs write access there (acme.sh creates .well-known/acme-challenge below it during issuance and renewal), e.g. via group membership or a pre-created, service-user owned .well-known/acme-challenge directory. See https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert for details.

  • Type: str
  • Required: No
run_acmesh_certs['domains']['challenge']['httpport']

⇑ Back to ToC ⇑

Optional. Used with “httpport” challenges. Specifies a non-standard port for acme.sh’s internal HTTP webserver to listen, might be needed behind a reverse proxy or load balancer.

  • Type: int
  • Required: No
run_acmesh_certs['domains']['challenge']['tlsport']

⇑ Back to ToC ⇑

Optional. Used with “alpn” challenges. Specifies a non-standard port for acme.sh’s internal HTTPS webserver to listen, might be needed behind a reverse proxy or load balancer.

  • Type: int
  • Required: No
run_acmesh_certs['domains']['challenge']['dns_persist_wildcard']

⇑ Back to ToC ⇑

Optional. Used with “dns_persist” challenges. If true, the persistent TXT record will also authorize wildcard certificates and matching subdomains for this domain. Corresponds to acme.sh –dns-persist-wildcard. See https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode for details.

  • Type: bool
  • Required: No
run_acmesh_certs['domains']['challenge']['dns_persist_ca_name']

⇑ Back to ToC ⇑

Optional. Used with “dns_persist” challenges. Specifies a custom CA identity domain name for the TXT record value (instead of deriving it from the server URL). Corresponds to acme.sh –dns-persist-ca-name. See https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode for details.

  • Type: str
  • Required: No
run_acmesh_certs['domains']['challenge']['dns_persist_days']

⇑ Back to ToC ⇑

Optional. Used with “dns_persist” challenges. Specifies the number of days the TXT record should be considered valid (adds a persistUntil field to the record). Corresponds to acme.sh –dns-persist-days. See https://github.com/acmesh-official/acme.sh/wiki/DNS-persist-mode for details.

  • Type: int
  • Required: No
run_acmesh_certs['install']

⇑ Back to ToC ⇑

Dictionary defining where and how to install the issued certificates. The following keys specify file paths, the installation strategy, ownership and permission overrides and a reload command:

  • ca_file: String. Path to the certificate authority (CA) file.

  • cert_file: String. Path to the certificate file.

  • fullchain_file: String. Path to the full certificate chain file.

  • key_file: String. Path to the private key file.

  • strategy: String, staged (the default) or direct. See the suboption description for details.

  • permissions: Dictionary of partial ownership and mode overrides for the destination directory and the installed files.

  • reloadcmd: String. Command to reload or restart services after certificate installation and renewal (e.g., web server or mail server). It is executed as root by a systemd unit whenever the installed certificate files change, so no sudo or polkit setup is needed. Requires at least one of the install file paths to be set. See the README of this role for details.

  • Type: dict

  • Required: No

run_acmesh_certs['install']['ca_file']

⇑ Back to ToC ⇑

  • Type: str
  • Required: No
run_acmesh_certs['install']['cert_file']

⇑ Back to ToC ⇑

  • Type: str
  • Required: No
run_acmesh_certs['install']['fullchain_file']

⇑ Back to ToC ⇑

  • Type: str
  • Required: No
run_acmesh_certs['install']['fullcain_file']

⇑ Back to ToC ⇑

Deprecated alias of fullchain_file (typo, “cain” instead of “chain”). Do not use.

  • Type: str
  • Required: No
run_acmesh_certs['install']['key_file']

⇑ Back to ToC ⇑

  • Type: str
  • Required: No
run_acmesh_certs['install']['strategy']

⇑ Back to ToC ⇑

Installation strategy for the certificate files.

staged (the default, also used when this key is omitted): acme.sh installs the certificate into an internal, root-protected staging area below run_acmesh_cfg_cert_deploy_base_dir and a root-owned systemd service (acmesh-deploy-<primary domain>.service) validates and publishes the files to the final destinations. This supports destinations below protected parent directories (such as /etc/ssl/private) and full ownership and mode control via permissions, while acme.sh itself keeps running unprivileged. See the README of this role for details.

direct: acme.sh installs directly to the final paths as the unprivileged service user (the behavior of version 3.x and below of this role). This is a compatibility option, not the recommended default: every destination must be writable by run_acmesh_user - also during unattended renewal - and permissions ownership overrides are rejected because the service account could not maintain them.

  • Type: str
  • Required: No
  • Default: "staged"
  • Choices: staged, direct
run_acmesh_certs['install']['permissions']

⇑ Back to ToC ⇑

Partial overrides for the ownership and permissions of the destination directory and the installed certificate files.

Effective values are calculated per key; later mappings override earlier mappings, unspecified keys inherit from the earlier ones:

  1. role defaults (directory: run_acmesh_user:run_acmesh_group with mode "2750"; files: run_acmesh_user:run_acmesh_group with mode "0640")
  2. default (applies to all certificate files)
  3. the per-artifact mapping (ca_file, cert_file, fullchain_file, key_file)

mode values must be quoted octal strings (such as "0640"). Certificate files must not be executable and must not carry setuid/setgid/sticky bits; private keys must additionally not be world-readable or group/world-writable ("0600", "0640", "0400" and "0440" are typical valid values). Directories may keep the setgid bit (the default directory mode is "2750"). owner and group accept names (recommended) or numeric ID strings and must exist on the target.

Example:

permissions:
  directory:
    owner: "root"
    group: "ssl-cert"
    mode: "0750"
  default:
    owner: "root"
    group: "ssl-cert"
    mode: "0640"
  fullchain_file:
    mode: "0644"
  key_file:
    group: "dovecot"
  • Type: dict
  • Required: No
run_acmesh_certs['install']['permissions']['directory']

⇑ Back to ToC ⇑

Ownership and mode overrides for the immediate destination directories of the certificate files. Only these directories are managed; existing ancestors (such as /etc/ssl or /etc/ssl/private) are never changed.

  • Type: dict
  • Required: No

Note: This option has more options but this README shows nested options up to level 3. Please refer to the argument_specs file for complete details.

run_acmesh_certs['install']['permissions']['default']

⇑ Back to ToC ⇑

Ownership and mode overrides applied to all installed certificate files (overridable per artifact).

  • Type: dict
  • Required: No

Note: This option has more options but this README shows nested options up to level 3. Please refer to the argument_specs file for complete details.

run_acmesh_certs['install']['permissions']['ca_file']

⇑ Back to ToC ⇑

Ownership and mode overrides for the installed ca_file.

  • Type: dict
  • Required: No

Note: This option has more options but this README shows nested options up to level 3. Please refer to the argument_specs file for complete details.

run_acmesh_certs['install']['permissions']['cert_file']

⇑ Back to ToC ⇑

Ownership and mode overrides for the installed cert_file.

  • Type: dict
  • Required: No

Note: This option has more options but this README shows nested options up to level 3. Please refer to the argument_specs file for complete details.

run_acmesh_certs['install']['permissions']['fullchain_file']

⇑ Back to ToC ⇑

Ownership and mode overrides for the installed fullchain_file.

  • Type: dict
  • Required: No

Note: This option has more options but this README shows nested options up to level 3. Please refer to the argument_specs file for complete details.

run_acmesh_certs['install']['permissions']['key_file']

⇑ Back to ToC ⇑

Ownership and mode overrides for the installed key_file.

  • Type: dict
  • Required: No

Note: This option has more options but this README shows nested options up to level 3. Please refer to the argument_specs file for complete details.

run_acmesh_certs['install']['reloadcmd']

⇑ Back to ToC ⇑

Optional. Command to reload or restart services after the certificate was installed or renewed. It is not executed by acme.sh but by a root-side systemd path unit (acmesh-reload-<primary domain>.path) which watches the installed certificate files and runs the command (as root, via a companion service unit) whenever they change. This works identically for the initial issuance, automated renewal and manual acme.sh runs, without granting the service user defined by run_acmesh_user any privileges (no sudo or polkit setup needed). A failing reload command leaves the acmesh-reload-<primary domain>.service unit in a failed state, visible via systemctl --failed and the journal.

Requires at least one of fullchain_file, key_file, ca_file or cert_file to be set (the unit watches the last one of these files acme.sh writes). Reload units of certificates that are no longer listed in run_acmesh_certs are kept by default (acme.sh keeps renewing and reinstalling issued certificates until they are actively removed). Remove a certificate with its state: absent or enable run_acmesh_certs_delete_unmanaged to reap such leftovers; everything gets removed when run_acmesh_state is absent.

  • Type: str
  • Required: No
run_acmesh_certs['server']

⇑ Back to ToC ⇑

Optional. Specifies the ACME CA server to use. Defaults to letsencrypt. Other options include:

run_acmesh_certs['force']

⇑ Back to ToC ⇑

Optional. If true, forces certificate issuance even if the current certificate is still valid. Defaults to false.

  • Type: bool
  • Required: No
run_acmesh_certs['debug']

⇑ Back to ToC ⇑

  • Type: bool
  • Required: No
run_acmesh_certs['dnssleep']

⇑ Back to ToC ⇑

  • Type: int
  • Required: No
run_acmesh_certs['pre_hook']

⇑ Back to ToC ⇑

Optional. Command to execute before attempting certificate issuance or renewal. Executed by acme.sh as the unprivileged service user defined by run_acmesh_user (during issuance as well as automated renewal), so it must not require root privileges. Use reloadcmd for reloading or restarting services (it runs as root). See https://github.com/acmesh-official/acme.sh/wiki/Using-pre-hook-post-hook-renew-hook-reloadcmd for details.

  • Type: str
  • Required: No
run_acmesh_certs['post_hook']

⇑ Back to ToC ⇑

Optional. Command to execute after a successful certificate issuance or renewal. Executed by acme.sh as the unprivileged service user defined by run_acmesh_user (during issuance as well as automated renewal), so it must not require root privileges. Use reloadcmd for reloading or restarting services (it runs as root). See https://github.com/acmesh-official/acme.sh/wiki/Using-pre-hook-post-hook-renew-hook-reloadcmd for details.

  • Type: str
  • Required: No
run_acmesh_certs['renew_hook']

⇑ Back to ToC ⇑

Optional. Command to execute after renewing the certificate. Executed by acme.sh as the unprivileged service user defined by run_acmesh_user, so it must not require root privileges. Use reloadcmd for reloading or restarting services (it runs as root). See https://github.com/acmesh-official/acme.sh/wiki/Using-pre-hook-post-hook-renew-hook-reloadcmd for details.

  • Type: str
  • Required: No
run_acmesh_certs['extra_flags']

⇑ Back to ToC ⇑

Optional. Additional CLI flags for edge cases. Useful for passing custom parameters during certificate issuance wich are not natively supported by this role, just pass them as “–foo –bar ‘baz’”.

  • Type: str
  • Required: No
run_acmesh_certs['environment']

⇑ Back to ToC ⇑

Optional. Dictionary of environment variables specific to this certificate. Setting environment variables per certificate can improve readability, as it clearly shows which certificate or domain is using which credentials in setups that involve multiple DNS provider credentials.

These are merged with the globalrun_acmesh_environment, with per-certificate values taking precedence on key conflicts.

Please note that acme.sh DNS API plugins usually persist credentials per provider (not per certificate) in account.conf, so using different credentials for the same DNS provider across certificates will result in only the last-written set being saved for automatic renewals.

  • Type: dict
  • Required: No
  • Sensitive: Yes (no_log, values are masked in logs)
run_acmesh_certs['state']

⇑ Back to ToC ⇑

Optional. present (the default) issues and maintains the certificate.

absent removes all data of this certificate: the acme.sh data below run_acmesh_cfg_cert_home (which also stops the automated renewal for it), the acmesh-reload-* and acmesh-deploy-* systemd units, the staging data and deployment manifest of the staged installation strategy and the installed certificate files listed under install. Directories holding installed files are kept, as they may be shared with other software. The certificate is not revoked at the ACME CA. This works independently of run_acmesh_certs_delete_unmanaged.

  • Type: str
  • Required: No
  • Default: "present"
  • Choices: present, absent

run_acmesh_certs_delete_unmanaged

⇑ Back to ToC ⇑

If set to true, certificate data below run_acmesh_cfg_cert_home that does not belong to any certificate listed in run_acmesh_certs gets removed (which also stops the automated renewal for it), as do acmesh-reload-* and acmesh-deploy-* systemd units, staging directories and deployment manifests the current configuration does not define. Useful to declaratively clean up certificates that were simply deleted from run_acmesh_certs (instead of being listed with state: absent).

Two safety properties: installed certificate files of unmanaged certificates are never touched (their installation paths are unknown to the role), and nothing is removed while run_acmesh_certs is undefined. Note that an empty certificate list (run_acmesh_certs: [], the default) combined with true declares “no certificates”, so all certificate data below run_acmesh_cfg_cert_home gets removed.

Prefer a per-certificate state: absent entry for targeted removal including the installed files.

  • Type: bool
  • Required: No
  • Default: false

run_acmesh_user

⇑ Back to ToC ⇑

Specifies the service user account that runs acme.sh and owns relevant files and directories.

All acme.sh operations run as this account: account registration, certificate issuance and installation during the Ansible run as well as the automated renewal (acmesh-renewal.service). Issuance and renewal therefore behave identically, and permission problems surface during the Ansible run instead of weeks later at the first renewal. When a certificate uses the standalone or alpn challenge on a privileged port (below 1024), the needed CAP_NET_BIND_SERVICE capability is granted automatically for both issuance and renewal.

This must be a dedicated account fully owned by this role. The role rewrites the account’s password, shell, home directory and supplementary group membership on installation. When user management is enabled, the role fully manages and removes the account named by run_acmesh_user, regardless of who originally created it: run_acmesh_state: absent deletes the account together with its home directory.

Do not point this at a shared, pre-existing or privileged account. The role refuses to run when run_acmesh_user resolves to root, a UID 0 account, or the account Ansible connects with, to avoid damaging or deleting an account that is not meant to be managed by it.

  • Type: str
  • Required: No
  • Default: "acmesh"

run_acmesh_group

⇑ Back to ToC ⇑

Specifies the group associated with the service user for managing acme.sh and the corresponding file permissions.

  • Type: str
  • Required: No
  • Default: "acmesh"

run_acmesh_user_supplementary_groups

⇑ Back to ToC ⇑

Exact list of supplementary groups for the service user account (run_acmesh_user) beyond run_acmesh_group. The role manages the account’s supplementary groups as the exact unique union of run_acmesh_group and this list; group memberships added outside of this role are removed again on the next run. Every listed group must already exist on the target (the role fails fast otherwise).

Both the automated renewal service (acmesh-renewal.service) and all acme.sh invocations of this role (which run via setpriv --init-groups) pick these groups up.

⚠️ Privileged groups (such as ssl-cert on Debian-based systems) may grant read access to unrelated private keys. This escape hatch is useful for direct certificate installations below protected directories, webroot write access, hooks and similar local requirements, but should not be needed merely to deploy a certificate through the (default) staged installation strategy.

  • Type: list
  • Required: No
  • Default: []
  • List Elements: str

run_acmesh_cfg_accountemail

⇑ Back to ToC ⇑

Specifies the email address to be associated with the ACME account. This email is used for expiration notices and recovery purposes. Some ACME providers might refuse to issue certificates if not set.

  • Type: str
  • Required: No
  • Default: ""

run_acmesh_cfg_home

⇑ Back to ToC ⇑

Specifies the installation directory for the acme.sh software (relates to acme.sh option –home). Will also be used as home directory of the service user defined (see run_acmesh_user).

  • Type: str
  • Required: No
  • Default: "/opt/acme.sh"

run_acmesh_cfg_config_home

⇑ Back to ToC ⇑

Defines where configuration files are stored (relates to acme.sh option –config-home).

  • Type: str
  • Required: No
  • Default: "/etc/acme.sh"

run_acmesh_cfg_cert_home

⇑ Back to ToC ⇑

Specifies the directory where certificates are maintained by acme.sh (relates to acme.sh option –certhome). ⚠️ Do not rely on the files in this directory directly. Instead, copy the certificates where needed using the “install” key of run_acmesh_certs (relates to acme.sh option –install-cert). This ensures that the certificates are properly maintained and automatically reinstalled upon renewal. See the following for more information: https://github.com/acmesh-official/acme.sh/issues/2350#issuecomment-1449235599

  • Type: str
  • Required: No
  • Default: "/var/opt/acme.sh"

run_acmesh_cfg_cert_deploy_base_dir

⇑ Back to ToC ⇑

Base directory for the internal staging areas of certificates using the staged installation strategy. The directory itself is root-owned and holds one subdirectory per certificate which is writable by the service user: acme.sh installs into that staging area and the root-owned acmesh-deploy-* systemd units validate and publish the files to their final destinations.

Must not be located below (or be a parent of) run_acmesh_cfg_home, run_acmesh_cfg_config_home or run_acmesh_cfg_cert_home; the role fails fast otherwise.

  • Type: str
  • Required: No
  • Default: "/var/opt/acme.sh-deploy"

run_acmesh_cfg_logfile

⇑ Back to ToC ⇑

Path to the log file where acme.sh logs its operations (relates to acme.sh option –log).

  • Type: str
  • Required: No
  • Default: "/var/log/acme.sh.log"

run_acmesh_cfg_log_level

⇑ Back to ToC ⇑

Specifies the log level (relates to acme.sh option –log-level). Possible values are 1 (less logging) and 2 (more logging).

  • Type: int
  • Required: No
  • Default: 1
  • Choices: 1, 2

run_acmesh_cfg_syslog

⇑ Back to ToC ⇑

Specifies what to log (relates to acme.sh option –syslog). Possible values are 0 (disable syslog), 3 (errors), 6 (info) and 7 (debug)

  • Type: int
  • Required: No
  • Default: 3
  • Choices: 0, 3, 6, 7

run_acmesh_cfg_ca_bundle

⇑ Back to ToC ⇑

Path to a custom CA certificate bundle file on the remote host. Passed as --ca-bundle to all acme.sh commands that make HTTPS requests (issue, register-account). Needed when the ACME server uses a certificate signed by a private or non-standard CA.

This is a global acme.sh setting, acme.sh persists it to its account.conf and applies it to all operations including cron renewals.

  • Type: str
  • Required: No
  • Default: ""

run_acmesh_cfg_ca_path

⇑ Back to ToC ⇑

Path to a directory of CA certificates in PEM format on the remote host. Passed as --ca-path to all acme.sh commands that make HTTPS requests. Alternative to run_acmesh_cfg_ca_bundle when the HTTP client (curl/wget) requires a directory of individual CA certificate files.

This is a global acme.sh setting, acme.sh persists it to its account.conf and applies it to all operations including cron renewals.

  • Type: str
  • Required: No
  • Default: ""

run_acmesh_cfg_account_keys

⇑ Back to ToC ⇑

List of ACME account keys to pre-seed. This is useful for dns-persist-01 challenges where the TXT record is bound to a specific account key, and you want to reuse the same key across multiple servers or after re-installations.

Each item must specify a server (CA alias or URL, same value used in run_acmesh_certs[].server) and account_key (PEM-encoded private key content). The role seeds the key and then runs acme.sh --register-account which confirms the account with the CA and may generate the account.json metadata automatically if needed (per RFC 8555 section 7.3.1, the CA identifies accounts by their public key).

Example:

run_acmesh_cfg_account_keys:
  - server: "letsencrypt"
    account_key: "\{\{ lookup('ansible.builtin.unvault', 'files/acmesh_account_key_letsencrypt.pem') \}\}"
  • Type: list
  • Required: No
  • Default: []
  • List Elements: dict
run_acmesh_cfg_account_keys['server']

⇑ Back to ToC ⇑

The CA alias or full ACME directory URL. Must match the value used in run_acmesh_certs[].server (e.g., “letsencrypt”, “zerossl”, or a URL).

  • Type: str
  • Required: Yes
run_acmesh_cfg_account_keys['account_key']

⇑ Back to ToC ⇑

PEM-encoded ACME account private key content. Sensitive - use ansible.builtin.unvault or similar secret management to supply this value.

  • Type: str
  • Required: Yes
  • Sensitive: Yes (no_log, values are masked in logs)

run_acmesh_dns_persist_pause

⇑ Back to ToC ⇑

Controls the pause duration (in seconds) after displaying dns-persist-01 TXT record instructions. Set to 0 to disable the pause (useful for CI/CD pipelines or environments where TXT records are guaranteed to already be published).

If the pause expires without user confirmation, the play continues.

  • Type: int
  • Required: No
  • Default: 600

Dependencies

See dependencies in meta/main.yml.

Compatibility

See min_ansible_version in meta/main.yml and __run_acmesh_supported_platforms in vars/main.yml.

External requirements

  • Git repository access: The role uses git ls-remote to query available version tags from the configured git repository. This works with any git remote (GitHub, GitLab, local mirrors), making it suitable for air-gapped environments with internal mirrors (see run_acmesh_git_url parameter).
  • systemd: The role requires systemd as init system, there is no fallback for others. It automates renewals with its own acmesh-renewal.timer / acmesh-renewal.service pair (acme.sh’s built-in cron support is disabled with --nocron on purpose) and executes reloadcmd via root-side systemd path units watching the installed certificate files.
  • SELinux: This role does not handle SELinux configurations. Please add additional tasks before or after this role to accommodate these changes (e.g. cert_t might be needed as context). The following Ansible modules may help with SELinux configuration:
  • Permissions to restart services for the service user: Not needed for reloadcmd, which runs as root via a systemd path unit (see Service reloads after certificate changes). However, the pre_hook, post_hook and renew_hook commands are executed by acme.sh itself and therefore always run as the unprivileged service user defined by run_acmesh_user (defaults to acmesh); privileged operations in these hooks have to be allowed explicitly (e.g. via sudo or polkit).
  • Webroot write access for the service user: When using the webroot challenge, the service user needs write access to the configured webroot directory (acme.sh creates .well-known/acme-challenge below it during issuance and renewal).

Beside that, there are no special requirements not covered by the role or Ansible itself.