Let’s Encrypt on IIS with win-acme: Certificate Renewal That Runs Itself

Let’s Encrypt certificates last 90 days, and the failure mode for a Windows IIS box is always the same: nobody set up renewal in month one, and in month four the site throws a certificate error while the admin is on a plane. win-acme is the most widely deployed ACME client for IIS on Windows, and it can renew unattended through the Task Scheduler without any Linux tooling. This is the setup, the verification, and the specific failure modes. If you are choosing a host before you start, compare Windows VPS plans with full admin rights and port 80 access, because both are prerequisites.

Prerequisites that people skip

Before installing anything, confirm four things. Any one of them missing will make issuance fail with an error that does not mention the real cause.

  • Port 80 reachable from the internet. HTTP-01 validation requires an inbound connection on port 80. If your provider blocks it, use DNS-01 with a provider API instead.
  • Public DNS already pointing at the VPS. Create the A record first; Let’s Encrypt resolves it from multiple locations and a slow propagation will fail validation.
  • IIS site binding exists for the hostname on port 80, even if you intend HTTPS only. win-acme needs an HTTP site to answer the challenge.
  • Admin rights and outbound HTTPS. The client talks to acme-v02.api.letsencrypt.org on 443.

Install and first issuance

Download the latest win-acme release (the win-acme.v2.x.x.xxxx.x64.pluggable.zip asset) and extract it to C:\win-acme. Do not run it from a user profile folder — the renewal task runs as SYSTEM and must be able to find the binary.

Run the interactive wizard once to get a working renewal configuration, then convert to unattended updates:

  1. wacs.exe → M (create certificate, full options).
  2. Select the IIS site from the list rather than typing the domain. This automatically binds the certificate to the right site after issuance.
  3. Choose the validation method. http-01 for a normal IIS site; dns-01 if port 80 is closed or the hostname is not publicly resolvable.
  4. Set the store to Windows Certificate Store (WebHosting) so IIS can bind without exporting a PFX.
  5. Enable Installation: create or update bindings in IIS.
  6. Accept the renewal task creation, which installs a scheduled task named something like win-acme renew (acme-v02.api.letsencrypt.org), running daily with a randomized time.

When the wizard finishes, the certificate is issued, bound, and a renewal schedule exists. The whole process is usually under five minutes.

What the renewal task actually does

win-acme’s task runs daily and checks each managed certificate’s due date. Because Let’s Encrypt certificates are 90 days, the client renews at roughly 55–60 days remaining. That gives about 30 days of retry budget before expiry, which is enough to survive a multi-day outage and still recover.

Event Day Remaining Action
Issued 0 90 d Bound automatically
Task starts polling for renewal ~30 60 d No action, just a due-date check
Renewal window opens ~30–35 55–60 d New cert issued, binding updated
Retry window 35–85 — Daily retries on failure
Expiry 90 0 Site fails for clients

Verifying it actually works

Do not trust the wizard’s success message. Verify at three levels:

  1. The schedule exists: Get-ScheduledTask | Where TaskName -like "*win-acme*" | Select TaskName,State — it must read Ready, not Disabled.
  2. The binding is live: netsh http show sslcert and check the hostname:port entry has the thumbprint of the new certificate.
  3. The served chain is valid: from an external machine, openssl s_client -connect yourdomain.com:443 -servername yourdomain.com and confirm the leaf expiry matches what you expect and the chain includes the intermediate.

Then force a dry run of the renewal to be sure the validation path still works: wacs.exe --renew --force --test. The --test flag stages the request against the Let’s Encrypt staging endpoint, so you can exercise the whole flow without burning rate limits.

Common failure modes on a VPS

  • Validation fails with “connection refused”. Usually the Windows Firewall or the provider’s edge firewall blocking 80. Check both, and remember that adding an IIS binding does not open the port.
  • “Rate limit exceeded”. Five duplicate certificates per week per exact hostname. Use --test while debugging, and do not delete and reissue in a loop.
  • Renewal succeeds but the site still shows the old cert. The binding did not update — usually because the site was added under a different hostname, or SNI is off and IIS has one cert for the IP. Re-run with the IIS binding installation step enabled.
  • Task runs but exits immediately. Running under a service account without read access to C:\win-acme, or the extraction folder was moved. The task’s working directory is baked in.
  • Certificate store permission errors. The private key must be readable by IIS_IUSRS. Using the WebHosting store handles this automatically; a manual PFX import often does not.

Hardening after you are live

Once renewal is stable, close the loop:

  1. Add an HTTP-to-HTTPS redirect rule at the site level so port 80 is only used for ACME challenges and redirects.
  2. Enable HSTS only after you are confident the certificate renews — a cert outage plus HSTS means users cannot click through.
  3. Monitor expiry independently of win-acme. A tiny scheduled task that checks the local cert’s NotAfter and emails you at 20 days remaining catches the case where the renewal task itself silently stopped.
  4. Snapshot before the first renewal cycle, then restore the snapshot to a test VM and confirm the task runs there too — that is your disaster path.

Alternatives worth knowing

win-acme is not the only option. IIS Crypto’s cert tools and Posh-ACME are both credible; Posh-ACME is the better fit if you already script everything in PowerShell and want certificates as objects in a pipeline. Certify The Web is the friendlier GUI choice for people who do not want a console at all. All three speak the same ACME protocol, so switching later is a re-issue, not a redesign.

Whatever client you pick, the operational rule is the same: prove renewal works in week one, not in month three. Review Windows VPS plans with snapshot support so you can test a renewal cycle on a clone rather than on production.

Set it up once, verify with a staging renewal, monitor expiry independently, and this stops being a recurring problem entirely.

Leave a Comment