How-to guide · Automate renewal

How to automate certificates on Windows with win-acme

In one sentence

win-acme gets Let's Encrypt certificates for your IIS sites, binds them, and renews them every day from a scheduled task that follows the CA's renewal advice.

Task
Automate renewal
Platforms
  • Windows
  • IIS
Who it is for
Level
Practitioner · 6 steps
On this page
  1. Before you start
  2. Steps
  3. 1. Download and unpack it to a permanent folder
  4. 2. Rehearse on the staging CA
  5. 3. Create the certificate and binding
  6. 4. Check the renewal task and its settings
  7. 5. Prove that renewal works
  8. 6. Other services on Windows
  9. Check it worked
  10. If something goes wrong
  11. Why this works
  12. Go deeper

Before you start

  • Windows Server with IIS, and an administrator account.
  • Public DNS records for every name, pointing to this server, and port 80 reachable from the internet. win-acme answers the HTTP-01 challenge itself, next to IIS.
  • IIS sites with host-name bindings for those names, so win-acme can find them.
  • For wildcard names, a DNS validation plugin instead: see wildcard certificates with DNS-01.

Why it matters: Let’s Encrypt certificates last 90 days by default, and that drops to 64 days from 10 February 2027. Manual renewals on IIS do not keep up.

Steps

1. Download and unpack it to a permanent folder

Download the current release from win-acme.com; on 20 October 2026 that was version 2.2.9.1, and the “trimmed” build suits most servers. Unzip it to %ProgramFiles%\win-acme, as the getting-started guide recommends, not to Downloads or a temporary folder: the renewal task runs from there.

The scheduled task runs as SYSTEM and executes wacs.exe and any scripts in that folder, so only administrators may be able to change it. %ProgramFiles% has that protection by default.

2. Rehearse on the staging CA

Open an administrator PowerShell in that folder and start win-acme in test mode, which uses Let’s Encrypt’s staging server and its higher rate limits:

cd "$env:ProgramFiles\win-acme"
.\wacs.exe --test

Go through step 3 with staging first. Staging certificates are not trusted by browsers, which is expected. Remove the test renewal afterwards from the main menu’s renewal manager.

3. Create the certificate and binding

Run .\wacs.exe without --test and choose N (“create a new certificate with default settings”). win-acme then:

  1. lists the IIS sites and bindings, and asks which names to include;
  2. registers an ACME account (you accept the terms and give an email address);
  3. proves control of each name over HTTP with its own self-hosting plugin;
  4. stores the certificate in the computer’s WebHosting store (or My, where WebHosting does not exist), with a non-exportable key;
  5. creates or updates the HTTPS bindings: port 443 on all IP addresses, per host name (IIS installation plugin);
  6. creates the scheduled renewal task, if it does not exist yet.

The same in one unattended command, built from the command-line reference and the IIS source plugin:

.\wacs.exe --source iis --siteid 1 --host www.example.com,example.com --installation iis --accepttos --emailaddress ops@example.com

Find the site ID with Get-IISSite.

4. Check the renewal task and its settings

Get-ScheduledTask | Where-Object TaskName -like 'win-acme*' | Format-List TaskName, State, Principal

The task runs once a day and renews what is due. Its timing comes from settings.json next to wacs.exe, which win-acme creates from settings_default.json on the first run. In the 2.2.9.1 download the defaults are:

{
  "RenewalDays": 55,
  "RenewalDaysRange": 0,
  "RenewalMinimumValidDays": null,
  "RenewalDisableServerSchedule": false,
  "RandomDelay": "04:00:00",
  "StartBoundary": "09:00:00",
  "ExecutionTimeLimit": "02:00:00"
}

RenewalDays counts from issue: 55 days fits a 90-day certificate. RenewalMinimumValidDays (empty means 7 days) forces renewal when little time is left (settings reference). RenewalDisableServerSchedule: false means win-acme follows the CA’s suggested renewal time through ARI, RFC 9773, on the IETF standards track, when that is earlier than its own schedule; keep it that way. Version 2.2.9 moved to the ARI version Let’s Encrypt uses, and the release notes say 2.2.3 to 2.2.8 “are currently reporting non-fatal errors” for it (release notes), so upgrade older installations.

Plan for shorter lifetimes. With the default dropping to 64 days and then 45 days from 16 February 2028, a fixed 55 days leaves little or no margin whenever ARI gives no answer. Our advice, not win-acme’s documented default: lower RenewalDays to about two-thirds of the lifetime, and keep ARI on for earlier renewals.

5. Prove that renewal works

.\wacs.exe --renew --force --verbose

--force renews “disregarding the validity of the current certificates and the prefered schedule”. Each forced run against production issues a real certificate and counts towards the limit of up to 5 certificates for the exact same set of names every 7 days, so do this once, or rehearse it with --test.

6. Other services on Windows

The download’s Scripts folder contains installation scripts for Exchange, Remote Desktop Gateway, SQL Server, ADFS and Java keystores, among others. win-acme can also write PEM or PFX files for software that reads files, such as Apache or nginx on Windows.

Check it worked

On the server:

Get-ChildItem Cert:\LocalMachine\WebHosting, Cert:\LocalMachine\My -ErrorAction SilentlyContinue |
    Where-Object Issuer -like '*Let''s Encrypt*' | Format-Table Subject, NotAfter, Thumbprint
Get-IISSiteBinding -Name "Default Web Site"

From another machine, check the chain the site sends:

openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null | grep -E '^ [0-9] s:|^   i:|Verify return code'

You want the server certificate, the Let’s Encrypt intermediate, and Verify return code: 0 (ok). The lifetime calendar shows when renewals fall:

Try it: Lifetime calendarOpen the full tool

Runs in your browser. Nothing you type or paste is sent anywhere.

Start from

The schedule for public TLS certificates

Issued fromLongest lifetimeDomain check reuseRenewals a year, at least
1 September 2020398 days398 days (from 1 October 2021)1
15 March 2026200 days200 days2
15 March 2027100 days100 days4
15 March 202947 days10 days8

From the CA/Browser Forum Baseline Requirements (sections 6.3.2 and 4.2.1), checked 20 October 2026. Renewals a year: 365 divided by the lifetime, rounded up; renewing earlier means more.

If something goes wrong

  • Validation fails. Check each name’s A and AAAA records, port 80 in the firewall and any load balancer in front, then rerun with --verbose. The logs are in %ProgramData%\win-acme\, in a Log folder under the ACME server’s name (for example acme-v02.api.letsencrypt.org). Let’s Encrypt allows only up to 5 failed validations per name, per account, per hour, so debug with --test.
  • The site still shows the old or an expired certificate, NET::ERR_CERT_DATE_INVALID. The task did not run (check its last result in Task Scheduler), or the binding belongs to a site win-acme does not manage. Bindings that used the previous certificate are updated on renewal.
  • Some clients report unable to verify the first certificate or The remote certificate is invalid. Windows did not send the intermediate. See install a certificate in IIS, step 3.
  • The task fails although manual runs work. It runs as SYSTEM: the folder must not be in a user profile, and scripts must not depend on a user’s drive mappings or credentials.

Why this works

win-acme is an ACME client. It proves control of each name by answering an ACME challenge on port 80, finalizes the order with a CSR, then puts the new certificate and key into the Windows store and points the IIS bindings at it. The daily task repeats this when a certificate is due or the CA asks for an earlier renewal. See ACME and the 47-day schedule, Proving you own the name and Inventory, expiry and renewal.

Go deeper

Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.

Last reviewed Edition 0.9