<!-- https://pkizerotohero.com/guides/win-acme-windows/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Automate Let's Encrypt certificates on Windows and IIS with win-acme: test on staging, create the certificate and binding, check the renewal settings.

Source: https://pkizerotohero.com/guides/win-acme-windows/ · Last reviewed: 2026-10-20

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](https://pkizerotohero.com/guides/#task-automate)

Platforms

- Windows
- IIS

Who it is for

- [IT ops](https://pkizerotohero.com/start/#itops)

Level

Practitioner · 6 steps

## 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](https://pkizerotohero.com/guides/wildcard-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](https://www.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](https://www.win-acme.com/manual/getting-started) 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:

```powershell
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](https://www.win-acme.com/reference/plugins/installation/iis));
6. creates the scheduled renewal task, if it does not exist yet.

The same in one unattended command, built from the [command-line reference](https://www.win-acme.com/reference/cli) and the [IIS source plugin](https://www.win-acme.com/reference/plugins/source/iis):

```powershell
.\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

```powershell
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:

```json
{
  "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](https://www.win-acme.com/reference/settings)). `RenewalDisableServerSchedule: false` means win-acme follows the CA’s suggested renewal time through [ARI](https://pkizerotohero.com/handbook/glossary/#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](https://github.com/win-acme/win-acme/releases/tag/v2.2.9.1701)), 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

```powershell
.\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:

```powershell
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:

```bash
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:

*Interactive tool: Lifetime calendar (runs in the browser at [/tools/lifetime/](https://pkizerotohero.com/tools/lifetime/))*

## 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`](https://pkizerotohero.com/errors/chrome-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`](https://pkizerotohero.com/errors/openssl-verify-error-21/) or [`The remote certificate is invalid`](https://pkizerotohero.com/errors/dotnet-remote-certificate-invalid/).** Windows did not send the intermediate. See [install a certificate in IIS](https://pkizerotohero.com/guides/install-certificate-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.

Note

Not only Let's Encrypt

win-acme works with any ACME server, including commercial CAs and a private ACME CA: pass its directory with `--baseuri`. See [A CA of your own](https://pkizerotohero.com/handbook/private-ca/).

## Why this works

win-acme is an [ACME](https://pkizerotohero.com/handbook/glossary/#acme) client. It proves control of each name by answering an [ACME challenge](https://pkizerotohero.com/handbook/glossary/#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](https://pkizerotohero.com/handbook/acme/), [Proving you own the name](https://pkizerotohero.com/handbook/domain-validation/) and [Inventory, expiry and renewal](https://pkizerotohero.com/handbook/certificate-lifecycle/).

## Go deeper

- [win-acme: getting started](https://www.win-acme.com/manual/getting-started): installation and the first certificate.
- [win-acme: automatic renewal](https://www.win-acme.com/manual/automatic-renewal): the scheduled task and when it renews.
- [win-acme: settings](https://www.win-acme.com/reference/settings): every option in `settings.json`.
- [Let’s Encrypt: rate limits](https://letsencrypt.org/docs/rate-limits/): what each limit means.
- [RFC 9773](https://www.rfc-editor.org/rfc/rfc9773): ACME Renewal Information.

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/guides/win-acme-windows/).

Last reviewed 20 October 2026Edition 0.9

## Keep going

### Learn why it works

- [Chapter 20: ACME and the 47-day schedule](https://pkizerotohero.com/handbook/acme/)
- [Chapter 19: Inventory, expiry and renewal](https://pkizerotohero.com/handbook/certificate-lifecycle/)
- [Chapter 11: Proving you own the name: DV, OV, EV and CAA](https://pkizerotohero.com/handbook/domain-validation/)

### Error pages

- [NET::ERR\_CERT\_DATE\_INVALID](https://pkizerotohero.com/errors/chrome-err-cert-date-invalid/)
- [verify error:num=21:unable to verify the first certificate](https://pkizerotohero.com/errors/openssl-verify-error-21/)
- [The remote certificate is invalid according to the validation procedure](https://pkizerotohero.com/errors/dotnet-remote-certificate-invalid/)

### Tools

- [Chain checker](https://pkizerotohero.com/tools/chain/): Paste a chain and see which link is missing, expired or out of order.
- [Lifetime calendar](https://pkizerotohero.com/tools/lifetime/): See how long a certificate may live on any date, and when to renew.

### Topics

- [Lifecycle and automation](https://pkizerotohero.com/topics/lifecycle-and-automation/)

## More guides: Automate renewal

- [How to get and renew Let's Encrypt certificates with certbot](https://pkizerotohero.com/guides/certbot-lets-encrypt/)
- [How to automate certificates in Kubernetes with cert-manager](https://pkizerotohero.com/guides/kubernetes-cert-manager/)
- [How to get a wildcard certificate with DNS-01](https://pkizerotohero.com/guides/wildcard-dns-01/)
