Renew through Let's Encrypt (HTTP-01)¶
What you'll have at the end: an endpoint renewing through Let's Encrypt with zero DNS work — the Scout on the endpoint answers the CA's challenge on port 80 itself, installs the certificate, reloads the web server, and re-probes. This is the simplest public lane, validated first against Let's Encrypt's staging environment so the first production order runs a path you have already seen succeed.
Prerequisites:
- The endpoint is visible on Manage with served proof — else do Track a new endpoint first.
- The endpoint's hostname resolves in public DNS to the host the Scout runs on — Let's Encrypt validates by connecting to it from the internet.
- Port 80 reachable from the internet on that host (firewall and any cloud security group). The Scout listens on port 80 during the challenge.
- Port 80 free at renewal time: either nothing serves plain HTTP on the
host, or an operator-local
prehook stops the web server (and thedeployhook restarts it) — see Scout → Security. A server that only listens on 443 needs no hooks for this.
Time: 15–20 minutes, including the staging dry run.
Mock values used throughout — substitute your real ones:
| Endpoint | shop.example.com, served by Scout web-01 |
| Hub | https://hub.example.internal |
| Scout install root | /opt/cyphers (data dir /opt/cyphers/data) |
Step 1 — Create a staging profile¶
Settings → Certificate issuance → Renewal Profiles → New Profile.
| Field | Value | Why |
|---|---|---|
| Profile Name | le-http01-staging |
Named for what it does. |
| Contact Email | ops@example.com |
Registers the ACME account. Accounts are keyed by (email, provider, environment) — the staging and production accounts are separate. |
| Issuer Type | Public ACME |
|
| Provider | Let's Encrypt |
HTTP-01 is offered on Let's Encrypt only in this phase — the Validation Method select drops the option on any other provider (see Certificate providers → Phase-1 boundary). |
| Environment | Staging |
Let's Encrypt publishes a real staging directory, so the dry run exercises the identical flow — challenge, install, reload, re-probe — without touching production rate limits. |
| Validation Method | HTTP-01 |
The point of this guide. |
| Key Type | ECDSA P-384 (recommended) |
HTTP-01 generates whatever key type the profile names, RSA included — pick what your clients need. |
| Delivery Mode | Scout (installs and reloads locally) |
Leave the rest at defaults and click Create Profile.
You should now see the profile in the Renewal Profiles table with
issuance HTTP-01 and environment staging. (Field-by-field detail:
Hub → Renewal profiles.)
Step 2 — Adopt the endpoint with the staging profile¶
On Manage, the shop.example.com row (Untracked) offers Adopt. Pick
le-http01-staging (staging) in the Renewal profile select — the picker
suffixes staging profiles so you can't grab one by accident later — and
click Apply.
You should now see the row managed. Nothing has been ordered yet. (Details: Adopt an endpoint.)
Step 3 — Staging dry run: Renew Now¶
The staging certificate is really installed
A staging renewal is not a simulation: the Scout installs the staging certificate and reloads the web server, and browsers will warn on it until Step 5 replaces it with the production one — Let's Encrypt's staging issuers are not publicly trusted. Run Steps 3–5 in one sitting, or do the dry run on a non-critical endpoint.
Click Renew Now on the row. The Scout now runs the entire ACME flow locally (see Scout → Commands):
- Loads or creates a persistent ACME account for the staging environment
(
/opt/cyphers/data/acme/staging.json; production usesproduction.json). - Probes the currently-served certificate before running your
prehook, so it can find and later re-point the on-disk paths the web server actually serves from. - Runs the
prehook (if present), listens on port 80, and lets the CA fetch the challenge token. The whole obtain is bounded by a 3-minute timeout — a wedged order fails rather than hanging the Scout. - Installs the result into the managed directory
/opt/cyphers/data/certs/shop.example.com/(fullchain.pem,cert.pem,chain.pem,privkey.pem— the private key is generated locally and never leaves the host), re-pointing any external served paths at it as symlinks. - Runs the
deployhook (your reload), validates the install, and re-probes the served endpoint. Any validation failure rolls the previous certificate back.
You should now see the renewal close within a minute or two: the
endpoint's drawer → Renewals tab shows the newest entry as Closed,
and the served certificate's issuer name is unmistakably staging (Let's
Encrypt's staging intermediates carry (STAGING) in their names). The
plumbing — DNS, port 80, hooks, install, reload — is proven.
Step 4 — Switch the profile to Production¶
Settings → Certificate issuance → Renewal Profiles, open
le-http01-staging, change Environment to Production (rename it if
you like, e.g. production-le), and click Save Changes.
You should now see the profile listed with environment production.
The endpoint keeps the same profile assignment — no re-adoption needed.
Step 5 — Renew Now again: the production certificate¶
Click Renew Now on the row again. Same flow as Step 3, now against the production directory; the hostname is added to the Hub's ACME domain allowlist automatically on the click.
You should now see the renewal close, this time with a publicly trusted certificate.
Step 6 — Verify the result¶
The renewal is closed — drawer → Renewals tab: newest entry Closed. A renewal only closes when a probe has observed the endpoint serving the new certificate, not merely when it was issued (see Renewal lanes → Verification closes every lane).
The world sees it — from any other machine:
$ echo | openssl s_client -connect shop.example.com:443 -servername shop.example.com 2>/dev/null | openssl x509 -noout -issuer -enddate
issuer=C=US, O=Let's Encrypt, CN=...
notAfter=...
The row is healthy — back on Manage, after the next observations the row shows the new expiry and no findings. A brief "Waiting on re-check" right after renewal is normal; it clears as soon as both vantages have re-observed the endpoint.
From here, every future renewal is one click — or none: Enable autopilot.
Troubleshooting¶
| What you see | Meaning | Remedy |
|---|---|---|
| Renewal fails quickly, error mentions the HTTP-01 provider / port 80 | The Scout could not bind port 80 — something else is listening. | Free the port, or add a pre hook that stops the web server (and a deploy hook that starts it). Hooks are operator-local; the Hub cannot ship them. |
| Renewal fails after up to ~3 minutes with a challenge/validation error | Let's Encrypt could not reach port 80 from the internet (firewall, NAT, DNS pointing elsewhere). The 3-minute obtain timeout also catches a wedged order. | Fix inbound port 80 / public DNS for the hostname, then Renew Now again. |
409 pre-flight refusal: … on the Renew click |
The pre-flight found a reason this renewal can't succeed or can't take effect — no order was created. | Read the named reason and remedy in the refusal; see Scout → Commands, renewal pre-flight. |
duplicate_cert_limit_risk finding |
Let's Encrypt allows 5 identical-name certificates per 7 days. The pre-flight surfaces the shrinking headroom as you approach the limit and refuses a renewal that would go past it — before any order exists. | Wait for the 7-day window to roll before renewing again. |
| Renewal parks in action needed after install | The certificate was issued and installed but the reload didn't take (hook failed, or the server still serves the old cert). No re-issue happens — re-issuing can't fix a flaky reload. | Fix the reload/hook on the host; the next routine scan observes the new certificate being served and closes the renewal on its own. |
| Renew refused, Scout reported dark | The bound Scout hasn't been seen recently — nothing could run the flow. | Check systemctl status cyphers-scout on the host and the Scout's row under Scouts. |
You need a wildcard (*.example.com) |
HTTP-01 cannot issue wildcards, on any CA. | Use a DNS-01 validation method — see Renewal lanes. |
a renewal is already in progress for this endpoint |
In-flight dedup — the previous attempt hasn't reached a terminal state. | Wait for it to close or fail; check the drawer. |