Renew through a paid / third-party CA (with key reuse)¶
What you'll have at the end: an endpoint renewing through a commercial ACME CA (DigiCert ONE, or any RFC 8555 CA that gave you a directory URL and EAB credentials), with the endpoint's existing private key kept across the renewal and the web server following the new certificate without a config change.
This is the full third-party flow, end to end. If your endpoint does not need to keep its key, the flow is the same minus Steps 6–7 — the renewal just generates a fresh key.
Prerequisites:
- The endpoint is visible on Manage with served proof — else do Track a new endpoint first.
- From your CA account team or portal: the ACME directory URL, the EAB key ID, and the EAB HMAC key. These identify your CA account to the ACME endpoint.
- A check that your domain's DNS doesn't forbid the CA: run
dig CAA shop.example.com +short(and the parent zones). No CAA records means any CA may issue; if records exist, one must authorize this CA. The Hub re-checks this before every order, fail-closed.
Time: 30–45 minutes for the first endpoint (subsequent ones reuse Steps 1–3 and take minutes).
Mock values used throughout — substitute your real ones:
| Endpoint | shop.example.com, Scout web-01, serving from /etc/nginx/ssl/ |
| Hub | https://hub.example.internal |
| CA directory | https://acme.example-ca.com/directory |
| EAB key ID | EXAMPLEKID_x9y8z7A6b5C4d3E2f1G0hIjKlMnOpQrS |
| EAB HMAC key | RXhhbXBsZUhtYWNLZXlNb2NrVmFsdWVGb3JEb2NzT25seU5vdFJlYWxLZXlNYXRlcmlhbEF0QWxs |
Step 1 — Register the CA as a provider¶
Settings → Certificate Providers → Add custom provider. Fill in:
| Field | Value | Why |
|---|---|---|
| Provider ID | example-ca |
Lowercase slug, letters/digits/hyphens only. Permanent — pick a name you'll recognize in profile dropdowns. |
| Display name | Example CA |
What the dashboard shows. |
| Directory URL | https://acme.example-ca.com/directory |
Exactly as the CA gave it. Must be https://. |
| Staging directory URL (optional) | (leave blank) | Most commercial ACME endpoints publish no staging directory. Blank means profiles on this provider are production-only — which is correct, not a compromise. |
| CAA issuer domain | example-ca.com |
The domain this CA tells its customers to put in CAA records. Mandatory: it's what the Hub's fail-closed CAA pre-check compares against. |
| Requires EAB credentials | ✓ | Refuses account registration with nothing stored, instead of failing later with a CA error. |
Click Add provider.
You should now see the provider in the panel's provider list, with External Account Binding: Required — not set.
Step 2 — Enter the EAB credentials¶
Still on the provider's detail: the EAB key ID / EAB HMAC key form. Paste both values exactly as issued — no trimming, no re-encoding, no added padding. The HMAC is a base64url string and is stored as that string; the Hub decodes it at signing time. Click Save EAB credentials.
You should now see the EAB state change to Configured (kid EXAMPLEKID_x9y8z7A6b5C4d3E2f1G0hIjKlMnOpQrS). The HMAC is write-only — it is encrypted at rest and never displayed again. A typo'd HMAC is not detected here; it surfaces in Step 8 at account registration, which is free — before any order exists (see the note there).
Step 3 — Create the renewal profile¶
Settings → Renewal Profiles → New Profile.
| Field | Value | Why |
|---|---|---|
| Profile Name | example-ca-rsa-reuse |
Named for what it does. |
| Contact Email | ops@example.com |
Registers the ACME account (keyed by email + provider + environment). |
| Issuer Type | Public ACME |
|
| Provider | Example CA |
The Step-1 row. |
| Environment | Production |
The only choice on a provider without a staging directory. |
| Validation Method | DNS-01 (Manual TXT) |
The lane for third-party CAs: EAB and custom directories are Hub-side, which HTTP-01 (Scout-side, Let's Encrypt-only this phase) cannot carry. Manual works even with zero DNS automation — and on a prevalidated CA account there is often no DNS step at all (Step 8). |
| Key Type | RSA 2048 (legacy compatibility) |
Match the endpoint's existing key — check with openssl rsa -in shop.key -noout -text | head -1. On this lane an RSA type nominates the reused key; it will not generate a fresh RSA key. |
| Preferred Chain | Default (CA decides) |
The only accepted value off Let's Encrypt. |
| Delivery Mode | Scout (installs and reloads locally) |
|
| Key Handling | ✓ Reuse private key across renewals (for pins/DANE) |
The point of this guide. If reuse is impossible the renewal is refused, never silently rotated. |
| Max Retries | 0 |
Every order on this CA costs money. Zero retries means one attempt per explicit click — a failure stops and tells you, instead of retrying a paid operation on its own. |
Click Create Profile. You should now see it in the Renewal Profiles
table with issuance DNS-01 (Manual TXT) and env production.
Step 4 — Make the certificate directory visible to the Scout¶
On the endpoint host, the directory the web server serves from must be in the Scout's cert-dirs file — this is what lets the Scout find the served key (Step 6), adopt it (Step 7), and later re-point the served files at the renewal (Step 9):
You should now see cert-dir file reloaded — starting a scan cycle in
journalctl -u cyphers-scout, and within a couple of minutes the endpoint's
drawer shows the discovered on-disk certificate.
Step 5 — Adopt the endpoint with the profile¶
On Manage, the shop.example.com row (Untracked) offers Adopt. Pick
example-ca-rsa-reuse in the Renewal profile select and click Apply.
You should now see the row managed. Nothing has been ordered yet. (Details: Adopt an endpoint.)
Step 6 — First Renew Now: the expected refusal¶
Click Renew Now. You should see:
The Activity feed spells it out: the key this endpoint serves is at
/etc/nginx/ssl/shop.key, which the Hub does not manage — reuse can only
load a key the Scout itself holds. This refusal is free (no order was
created) and it is the design working: the alternative would be silently
rotating a key you configured the profile to keep.
Step 7 — Adopt the key¶
Open the drawer and click Adopt key from /etc/nginx/ssl/shop.key. The Scout verifies the key matches what the endpoint currently serves, then copies it (on the host — it never crosses the network) into its managed storage. Verify yourself:
$ sudo openssl rsa -noout -modulus -in /etc/nginx/ssl/shop.key | openssl sha256
SHA2-256(stdin)= ab12cd34ef56...
$ sudo openssl rsa -noout -modulus -in /opt/cyphers/data/certs/shop.example.com/privkey.pem | openssl sha256
SHA2-256(stdin)= ab12cd34ef56...
Same digest = same key. (Full detail: Adopt a private key.)
Step 8 — Renew Now again — ⚠ this may be the paid click¶
Where money is spent
On a CA account whose domains are prevalidated (typical for OV/organization-validated setups), the order arrives with nothing left to prove, and the Hub's DNS watchdog finalizes it automatically within ~30 seconds — there is no second confirmation between this click and a billed certificate. Treat Renew Now as the purchase.
Before the order, the first-ever renewal on this provider registers the ACME
account — which is free and is where bad EAB credentials fail. The Hub
log line Persisted new ACME account credentials marks success; a
registration error at this point has cost nothing — re-check Step 2 and
click again.
Click Renew Now. The endpoint's hostname is added to the ACME domain allowlist automatically, the CAA pre-check runs, and the order is created. Two shapes from here:
Shape A — prevalidated (no DNS work). The drawer briefly shows Complete renewal — "No DNS records are required — the CA has already validated every domain on this order." — and the watchdog completes it on its own within about 30 seconds. You'll simply see the renewal close.
Shape B — TXT records required. The drawer shows Add DNS TXT record with the exact record(s):
Publish them in your DNS console. There is no deadline — the order parks until you act, and the Hub re-checks it every 30 seconds, completing the renewal on its own once every authoritative nameserver of your zone serves the records. Verify & Complete just runs that check immediately.
The all-nameservers bar is deliberate: the CA validates against whichever
authoritative host it likes, so the Hub refuses to submit for validation —
which is one-shot, and burns the order on a wrong answer — until the whole
fleet agrees. Right after you edit a record, "still waiting on
ns2.example-dns.com" is normal; it clears as your provider's servers
converge.
Step 9 — Verify the result¶
Wait for the renewal to close (Scout install + reload + served re-probe; usually under a minute after finalization), then check each claim:
The renewal is closed — drawer → Renewals tab: the newest entry
reads Closed, with Key: reused.
The served files followed — on the host, the external path is now a symlink into the Scout's managed directory, so nginx's unchanged config follows every future renewal:
$ ls -l /etc/nginx/ssl/shop.pem
lrwxrwxrwx ... /etc/nginx/ssl/shop.pem -> /opt/cyphers/data/certs/shop.example.com/fullchain.pem
The original files were quarantined by the deploy transaction, not deleted.
The key never changed:
$ sudo openssl rsa -noout -modulus -in /opt/cyphers/data/certs/shop.example.com/privkey.pem | openssl sha256
SHA2-256(stdin)= ab12cd34ef56... # identical to Steps 1/7
The world sees the new certificate — from any other machine:
$ echo | openssl s_client -connect shop.example.com:443 -servername shop.example.com 2>/dev/null | openssl x509 -noout -serial -issuer -enddate
serial=04ABCD1234EF...
issuer=CN=Example CA TLS RSA CA G1...
notAfter=Mar 8 12:00:00 2027 GMT
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.
Troubleshooting¶
| What you see | Meaning | Remedy |
|---|---|---|
pre-flight refusal: key_reuse_adoptable |
Served key exists but isn't under management. | Expected once — Step 7. |
pre-flight refusal: key_reuse_key_missing |
The Scout holds no key and can't see the served one — usually the cert directory isn't in cert-dirs.conf. |
Do Step 4, wait one cycle, retry. |
pre-flight refusal: key_reuse_algo_mismatch |
Profile key type ≠ held key (e.g. rsa_4096 profile, 2048-bit key). |
Fix the profile's Key Type to match the real key. |
pre-flight refusal: key_reuse_diverged |
Something else rotated the served key after adoption — another renewer (certbot?) is still active. | Stop the other renewer, re-adopt, retry. See Take over certbot. |
Provider option flagged — EAB required / issuance blocked on EAB |
Provider requires EAB, none stored. | Step 2. |
| Renewal fails during account setup, no order created | Bad EAB credentials or directory URL — caught at free registration. | Re-enter Step 2 values exactly as issued; retry costs nothing. |
| CAA refusal in pre-flight | The domain's CAA records don't authorize this CA (or the provider row's CAA issuer domain is wrong). | Fix the CAA record — or the Step-1 field — and retry. |
| Parked on TXT, then failed after 5 minutes | The manual-DNS window lapsed. Order unfinalized — unpaid. | Renew again with the records ready. |
| Renewal parks in action needed after install | Certificate issued + installed but the reload didn't take (hook failed or reload ineffective). No re-issue happens. | Fix the reload/hook on the host; the next routine scan closes the renewal on its own. |
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. |